Coverage Report

Created: 2025-06-13 06:38

/src/icu/icu4c/source/i18n/number_mapper.cpp
Line
Count
Source (jump to first uncovered line)
1
// © 2018 and later: Unicode, Inc. and others.
2
// License & terms of use: http://www.unicode.org/copyright.html
3
4
#include "unicode/utypes.h"
5
6
#if !UCONFIG_NO_FORMATTING
7
8
// Allow implicit conversion from char16_t* to UnicodeString for this file:
9
// Helpful in toString methods and elsewhere.
10
#define UNISTR_FROM_STRING_EXPLICIT
11
12
#include "number_mapper.h"
13
#include "number_patternstring.h"
14
#include "unicode/errorcode.h"
15
#include "number_utils.h"
16
17
using namespace icu;
18
using namespace icu::number;
19
using namespace icu::number::impl;
20
21
22
UnlocalizedNumberFormatter NumberPropertyMapper::create(const DecimalFormatProperties& properties,
23
                                                        const DecimalFormatSymbols& symbols,
24
                                                        DecimalFormatWarehouse& warehouse,
25
0
                                                        UErrorCode& status) {
26
0
    return NumberFormatter::with().macros(oldToNew(properties, symbols, warehouse, nullptr, status));
27
0
}
28
29
UnlocalizedNumberFormatter NumberPropertyMapper::create(const DecimalFormatProperties& properties,
30
                                                        const DecimalFormatSymbols& symbols,
31
                                                        DecimalFormatWarehouse& warehouse,
32
                                                        DecimalFormatProperties& exportedProperties,
33
504k
                                                        UErrorCode& status) {
34
504k
    return NumberFormatter::with().macros(
35
504k
            oldToNew(
36
504k
                    properties, symbols, warehouse, &exportedProperties, status));
37
504k
}
38
39
MacroProps NumberPropertyMapper::oldToNew(const DecimalFormatProperties& properties,
40
                                          const DecimalFormatSymbols& symbols,
41
                                          DecimalFormatWarehouse& warehouse,
42
                                          DecimalFormatProperties* exportedProperties,
43
504k
                                          UErrorCode& status) {
44
504k
    MacroProps macros;
45
504k
    Locale locale = symbols.getLocale();
46
47
    /////////////
48
    // SYMBOLS //
49
    /////////////
50
51
504k
    macros.symbols.setTo(symbols);
52
53
    //////////////////
54
    // PLURAL RULES //
55
    //////////////////
56
57
504k
    if (!properties.currencyPluralInfo.fPtr.isNull()) {
58
0
        macros.rules = properties.currencyPluralInfo.fPtr->getPluralRules();
59
0
    }
60
61
    /////////////
62
    // AFFIXES //
63
    /////////////
64
65
504k
    warehouse.affixProvider.setTo(properties, status);
66
504k
    macros.affixProvider = &warehouse.affixProvider.get();
67
68
    ///////////
69
    // UNITS //
70
    ///////////
71
72
504k
    bool useCurrency = (
73
504k
            !properties.currency.isNull() ||
74
504k
            !properties.currencyPluralInfo.fPtr.isNull() ||
75
504k
            !properties.currencyUsage.isNull() ||
76
504k
            warehouse.affixProvider.get().hasCurrencySign());
77
504k
    CurrencyUnit currency;
78
504k
    UCurrencyUsage currencyUsage;
79
504k
    if (useCurrency) {
80
15.6k
        currency = resolveCurrency(properties, locale, status);
81
15.6k
        currencyUsage = properties.currencyUsage.getOrDefault(UCURR_USAGE_STANDARD);
82
        // NOTE: Slicing is OK.
83
15.6k
        macros.unit = currency; // NOLINT
84
15.6k
    }
85
86
    ///////////////////////
87
    // ROUNDING STRATEGY //
88
    ///////////////////////
89
90
504k
    int32_t maxInt = properties.maximumIntegerDigits;
91
504k
    int32_t minInt = properties.minimumIntegerDigits;
92
504k
    int32_t maxFrac = properties.maximumFractionDigits;
93
504k
    int32_t minFrac = properties.minimumFractionDigits;
94
504k
    int32_t minSig = properties.minimumSignificantDigits;
95
504k
    int32_t maxSig = properties.maximumSignificantDigits;
96
504k
    double roundingIncrement = properties.roundingIncrement;
97
    // Not assigning directly to macros.roundingMode here: we change
98
    // roundingMode if and when we also change macros.precision.
99
504k
    RoundingMode roundingMode = properties.roundingMode.getOrDefault(UNUM_ROUND_HALFEVEN);
100
504k
    bool explicitMinMaxFrac = minFrac != -1 || maxFrac != -1;
101
504k
    bool explicitMinMaxSig = minSig != -1 || maxSig != -1;
102
    // Resolve min/max frac for currencies, required for the validation logic and for when minFrac or
103
    // maxFrac was
104
    // set (but not both) on a currency instance.
105
    // NOTE: Increments are handled in "Precision.constructCurrency()".
106
504k
    if (useCurrency && (minFrac == -1 || maxFrac == -1)) {
107
15.6k
        int32_t digits = ucurr_getDefaultFractionDigitsForUsage(
108
15.6k
                currency.getISOCurrency(), currencyUsage, &status);
109
15.6k
        if (minFrac == -1 && maxFrac == -1) {
110
15.6k
            minFrac = digits;
111
15.6k
            maxFrac = digits;
112
15.6k
        } else if (minFrac == -1) {
113
0
            minFrac = std::min(maxFrac, digits);
114
0
        } else /* if (maxFrac == -1) */ {
115
0
            maxFrac = std::max(minFrac, digits);
116
0
        }
117
15.6k
    }
118
    // Validate min/max int/frac.
119
    // For backwards compatibility, minimum overrides maximum if the two conflict.
120
504k
    if (minInt == 0 && maxFrac != 0) {
121
192
        minFrac = (minFrac < 0 || (minFrac == 0 && maxInt == 0)) ? 1 : minFrac;
122
192
        maxFrac = maxFrac < 0 ? -1 : maxFrac < minFrac ? minFrac : maxFrac;
123
192
        minInt = 0;
124
192
        maxInt = maxInt < 0 ? -1 : maxInt > kMaxIntFracSig ? -1 : maxInt;
125
504k
    } else {
126
        // Force a digit before the decimal point.
127
504k
        minFrac = minFrac < 0 ? 0 : minFrac;
128
504k
        maxFrac = maxFrac < 0 ? -1 : maxFrac < minFrac ? minFrac : maxFrac;
129
504k
        minInt = minInt <= 0 ? 1 : minInt > kMaxIntFracSig ? 1 : minInt;
130
504k
        maxInt = maxInt < 0 ? -1 : maxInt < minInt ? minInt : maxInt > kMaxIntFracSig ? -1 : maxInt;
131
504k
    }
132
504k
    Precision precision;
133
504k
    if (!properties.currencyUsage.isNull()) {
134
0
        U_ASSERT(useCurrency);
135
0
        precision = Precision::constructCurrency(currencyUsage).withCurrency(currency);
136
504k
    } else if (roundingIncrement != 0.0) {
137
5.71k
        if (PatternStringUtils::ignoreRoundingIncrement(roundingIncrement, maxFrac)) {
138
0
            precision = Precision::constructFraction(minFrac, maxFrac);
139
5.71k
        } else {
140
            // Convert the double increment to an integer increment
141
5.71k
            precision = Precision::increment(roundingIncrement).withMinFraction(minFrac);
142
5.71k
        }
143
499k
    } else if (explicitMinMaxSig) {
144
61
        minSig = minSig < 1 ? 1 : minSig > kMaxIntFracSig ? kMaxIntFracSig : minSig;
145
61
        maxSig = maxSig < 0 ? kMaxIntFracSig : maxSig < minSig ? minSig : maxSig > kMaxIntFracSig
146
61
                                                                          ? kMaxIntFracSig : maxSig;
147
61
        precision = Precision::constructSignificant(minSig, maxSig);
148
499k
    } else if (explicitMinMaxFrac) {
149
464k
        precision = Precision::constructFraction(minFrac, maxFrac);
150
464k
    } else if (useCurrency) {
151
15.6k
        precision = Precision::constructCurrency(currencyUsage);
152
15.6k
    }
153
504k
    if (!precision.isBogus()) {
154
485k
        macros.roundingMode = roundingMode;
155
485k
        macros.precision = precision;
156
485k
    }
157
158
    ///////////////////
159
    // INTEGER WIDTH //
160
    ///////////////////
161
162
504k
    macros.integerWidth = IntegerWidth(
163
504k
            static_cast<digits_t>(minInt),
164
504k
            static_cast<digits_t>(maxInt),
165
504k
            properties.formatFailIfMoreThanMaxDigits);
166
167
    ///////////////////////
168
    // GROUPING STRATEGY //
169
    ///////////////////////
170
171
504k
    macros.grouper = Grouper::forProperties(properties);
172
173
    /////////////
174
    // PADDING //
175
    /////////////
176
177
504k
    if (properties.formatWidth > 0) {
178
2.10k
        macros.padder = Padder::forProperties(properties);
179
2.10k
    }
180
181
    ///////////////////////////////
182
    // DECIMAL MARK ALWAYS SHOWN //
183
    ///////////////////////////////
184
185
504k
    macros.decimal = properties.decimalSeparatorAlwaysShown ? UNUM_DECIMAL_SEPARATOR_ALWAYS
186
504k
                                                            : UNUM_DECIMAL_SEPARATOR_AUTO;
187
188
    ///////////////////////
189
    // SIGN ALWAYS SHOWN //
190
    ///////////////////////
191
192
504k
    macros.sign = properties.signAlwaysShown ? UNUM_SIGN_ALWAYS : UNUM_SIGN_AUTO;
193
194
    /////////////////////////
195
    // SCIENTIFIC NOTATION //
196
    /////////////////////////
197
198
504k
    if (properties.minimumExponentDigits != -1) {
199
        // Scientific notation is required.
200
        // This whole section feels like a hack, but it is needed for regression tests.
201
        // The mapping from property bag to scientific notation is nontrivial due to LDML rules.
202
12.1k
        if (maxInt > 8) {
203
            // But #13110: The maximum of 8 digits has unknown origins and is not in the spec.
204
            // If maxInt is greater than 8, it is set to minInt, even if minInt is greater than 8.
205
1.28k
            maxInt = minInt;
206
1.28k
            macros.integerWidth = IntegerWidth::zeroFillTo(minInt).truncateAt(maxInt);
207
10.9k
        } else if (maxInt > minInt && minInt > 1) {
208
            // Bug #13289: if maxInt > minInt > 1, then minInt should be 1.
209
2
            minInt = 1;
210
2
            macros.integerWidth = IntegerWidth::zeroFillTo(minInt).truncateAt(maxInt);
211
2
        }
212
12.1k
        int engineering = maxInt < 0 ? -1 : maxInt;
213
12.1k
        macros.notation = ScientificNotation(
214
                // Engineering interval:
215
12.1k
                static_cast<int8_t>(engineering),
216
                // Enforce minimum integer digits (for patterns like "000.00E0"):
217
12.1k
                (engineering == minInt),
218
                // Minimum exponent digits:
219
12.1k
                static_cast<digits_t>(properties.minimumExponentDigits),
220
                // Exponent sign always shown:
221
12.1k
                properties.exponentSignAlwaysShown ? UNUM_SIGN_ALWAYS : UNUM_SIGN_AUTO);
222
        // Scientific notation also involves overriding the rounding mode.
223
        // TODO: Overriding here is a bit of a hack. Should this logic go earlier?
224
12.1k
        if (macros.precision.fType == Precision::PrecisionType::RND_FRACTION) {
225
            // For the purposes of rounding, get the original min/max int/frac, since the local
226
            // variables have been manipulated for display purposes.
227
9.94k
            int maxInt_ = properties.maximumIntegerDigits;
228
9.94k
            int minInt_ = properties.minimumIntegerDigits;
229
9.94k
            int minFrac_ = properties.minimumFractionDigits;
230
9.94k
            int maxFrac_ = properties.maximumFractionDigits;
231
9.94k
            if (minInt_ == 0 && maxFrac_ == 0) {
232
                // Patterns like "#E0" and "##E0", which mean no rounding!
233
9.40k
                macros.precision = Precision::unlimited();
234
9.40k
            } else if (minInt_ == 0 && minFrac_ == 0) {
235
                // Patterns like "#.##E0" (no zeros in the mantissa), which mean round to maxFrac+1
236
0
                macros.precision = Precision::constructSignificant(1, maxFrac_ + 1);
237
542
            } else {
238
542
                int maxSig_ = minInt_ + maxFrac_;
239
                // Bug #20058: if maxInt_ > minInt_ > 1, then minInt_ should be 1.
240
542
                if (maxInt_ > minInt_ && minInt_ > 1) {
241
0
                    minInt_ = 1;
242
0
                }
243
542
                int minSig_ = minInt_ + minFrac_;
244
                // To avoid regression, maxSig is not reset when minInt_ set to 1.
245
                // TODO: Reset maxSig_ = 1 + minFrac_ to follow the spec.
246
542
                macros.precision = Precision::constructSignificant(minSig_, maxSig_);
247
542
            }
248
9.94k
            macros.roundingMode = roundingMode;
249
9.94k
        }
250
12.1k
    }
251
252
    //////////////////////
253
    // COMPACT NOTATION //
254
    //////////////////////
255
256
504k
    if (!properties.compactStyle.isNull()) {
257
19.0k
        if (properties.compactStyle.getNoError() == UNumberCompactStyle::UNUM_LONG) {
258
9.53k
            macros.notation = Notation::compactLong();
259
9.53k
        } else {
260
9.53k
            macros.notation = Notation::compactShort();
261
9.53k
        }
262
19.0k
    }
263
264
    /////////////////
265
    // MULTIPLIERS //
266
    /////////////////
267
268
504k
    macros.scale = scaleFromProperties(properties);
269
270
    //////////////////////
271
    // PROPERTY EXPORTS //
272
    //////////////////////
273
274
504k
    if (exportedProperties != nullptr) {
275
276
504k
        exportedProperties->currency = currency;
277
504k
        exportedProperties->roundingMode = roundingMode;
278
504k
        exportedProperties->minimumIntegerDigits = minInt;
279
504k
        exportedProperties->maximumIntegerDigits = maxInt == -1 ? INT32_MAX : maxInt;
280
281
504k
        Precision rounding_;
282
504k
        if (useCurrency && precision.fType == Precision::PrecisionType::RND_CURRENCY) {
283
15.6k
            rounding_ = precision.withCurrency(currency, status);
284
489k
        } else {
285
489k
            rounding_ = precision;
286
489k
        }
287
504k
        int minFrac_ = minFrac;
288
504k
        int maxFrac_ = maxFrac;
289
504k
        int minSig_ = minSig;
290
504k
        int maxSig_ = maxSig;
291
504k
        double increment_ = 0.0;
292
504k
        if (rounding_.fType == Precision::PrecisionType::RND_FRACTION) {
293
479k
            minFrac_ = rounding_.fUnion.fracSig.fMinFrac;
294
479k
            maxFrac_ = rounding_.fUnion.fracSig.fMaxFrac;
295
479k
        } else if (rounding_.fType == Precision::PrecisionType::RND_INCREMENT
296
25.0k
                || rounding_.fType == Precision::PrecisionType::RND_INCREMENT_ONE
297
25.0k
                || rounding_.fType == Precision::PrecisionType::RND_INCREMENT_FIVE) {
298
5.68k
            minFrac_ = rounding_.fUnion.increment.fMinFrac;
299
            // If incrementRounding is used, maxFrac is set equal to minFrac
300
5.68k
            maxFrac_ = rounding_.fUnion.increment.fMinFrac;
301
            // Convert the integer increment to a double
302
5.68k
            DecimalQuantity dq;
303
5.68k
            dq.setToLong(rounding_.fUnion.increment.fIncrement);
304
5.68k
            dq.adjustMagnitude(rounding_.fUnion.increment.fIncrementMagnitude);
305
5.68k
            increment_ = dq.toDouble();
306
19.3k
        } else if (rounding_.fType == Precision::PrecisionType::RND_SIGNIFICANT) {
307
61
            minSig_ = rounding_.fUnion.fracSig.fMinSig;
308
61
            maxSig_ = rounding_.fUnion.fracSig.fMaxSig;
309
61
        }
310
311
504k
        exportedProperties->minimumFractionDigits = minFrac_;
312
504k
        exportedProperties->maximumFractionDigits = maxFrac_;
313
504k
        exportedProperties->minimumSignificantDigits = minSig_;
314
504k
        exportedProperties->maximumSignificantDigits = maxSig_;
315
504k
        exportedProperties->roundingIncrement = increment_;
316
504k
    }
317
318
504k
    return macros;
319
504k
}
320
321
322
607k
void PropertiesAffixPatternProvider::setTo(const DecimalFormatProperties& properties, UErrorCode& status) {
323
607k
    fBogus = false;
324
325
    // There are two ways to set affixes in DecimalFormat: via the pattern string (applyPattern), and via the
326
    // explicit setters (setPositivePrefix and friends).  The way to resolve the settings is as follows:
327
    //
328
    // 1) If the explicit setting is present for the field, use it.
329
    // 2) Otherwise, follows UTS 35 rules based on the pattern string.
330
    //
331
    // Importantly, the explicit setters affect only the one field they override.  If you set the positive
332
    // prefix, that should not affect the negative prefix.
333
334
    // Convenience: Extract the properties into local variables.
335
    // Variables are named with three chars: [p/n][p/s][o/p]
336
    // [p/n] => p for positive, n for negative
337
    // [p/s] => p for prefix, s for suffix
338
    // [o/p] => o for escaped custom override string, p for pattern string
339
607k
    UnicodeString ppo = AffixUtils::escape(properties.positivePrefix);
340
607k
    UnicodeString pso = AffixUtils::escape(properties.positiveSuffix);
341
607k
    UnicodeString npo = AffixUtils::escape(properties.negativePrefix);
342
607k
    UnicodeString nso = AffixUtils::escape(properties.negativeSuffix);
343
607k
    const UnicodeString& ppp = properties.positivePrefixPattern;
344
607k
    const UnicodeString& psp = properties.positiveSuffixPattern;
345
607k
    const UnicodeString& npp = properties.negativePrefixPattern;
346
607k
    const UnicodeString& nsp = properties.negativeSuffixPattern;
347
348
607k
    if (!properties.positivePrefix.isBogus()) {
349
0
        posPrefix = ppo;
350
607k
    } else if (!ppp.isBogus()) {
351
588k
        posPrefix = ppp;
352
588k
    } else {
353
        // UTS 35: Default positive prefix is empty string.
354
19.2k
        posPrefix = u"";
355
19.2k
    }
356
357
607k
    if (!properties.positiveSuffix.isBogus()) {
358
0
        posSuffix = pso;
359
607k
    } else if (!psp.isBogus()) {
360
588k
        posSuffix = psp;
361
588k
    } else {
362
        // UTS 35: Default positive suffix is empty string.
363
19.2k
        posSuffix = u"";
364
19.2k
    }
365
366
607k
    if (!properties.negativePrefix.isBogus()) {
367
0
        negPrefix = npo;
368
607k
    } else if (!npp.isBogus()) {
369
2.97k
        negPrefix = npp;
370
604k
    } else {
371
        // UTS 35: Default negative prefix is "-" with positive prefix.
372
        // Important: We prepend the "-" to the pattern, not the override!
373
604k
        negPrefix = ppp.isBogus() ? u"-" : u"-" + ppp;
374
604k
    }
375
376
607k
    if (!properties.negativeSuffix.isBogus()) {
377
0
        negSuffix = nso;
378
607k
    } else if (!nsp.isBogus()) {
379
2.97k
        negSuffix = nsp;
380
604k
    } else {
381
        // UTS 35: Default negative prefix is the positive prefix.
382
604k
        negSuffix = psp.isBogus() ? u"" : psp;
383
604k
    }
384
385
    // For declaring if this is a currency pattern, we need to look at the
386
    // original pattern, not at any user-specified overrides.
387
607k
    isCurrencyPattern = (
388
607k
        AffixUtils::hasCurrencySymbols(ppp, status) ||
389
607k
        AffixUtils::hasCurrencySymbols(psp, status) ||
390
607k
        AffixUtils::hasCurrencySymbols(npp, status) ||
391
607k
        AffixUtils::hasCurrencySymbols(nsp, status) ||
392
607k
        properties.currencyAsDecimal);
393
394
607k
    fCurrencyAsDecimal = properties.currencyAsDecimal;
395
607k
}
396
397
66.9M
char16_t PropertiesAffixPatternProvider::charAt(int flags, int i) const {
398
66.9M
    return getStringInternal(flags).charAt(i);
399
66.9M
}
400
401
418k
int PropertiesAffixPatternProvider::length(int flags) const {
402
418k
    return getStringInternal(flags).length();
403
418k
}
404
405
208k
UnicodeString PropertiesAffixPatternProvider::getString(int32_t flags) const {
406
208k
    return getStringInternal(flags);
407
208k
}
408
409
67.6M
const UnicodeString& PropertiesAffixPatternProvider::getStringInternal(int32_t flags) const {
410
67.6M
    bool prefix = (flags & AFFIX_PREFIX) != 0;
411
67.6M
    bool negative = (flags & AFFIX_NEGATIVE_SUBPATTERN) != 0;
412
67.6M
    if (prefix && negative) {
413
2.54M
        return negPrefix;
414
65.0M
    } else if (prefix) {
415
47.9M
        return posPrefix;
416
47.9M
    } else if (negative) {
417
1.30M
        return negSuffix;
418
15.7M
    } else {
419
15.7M
        return posSuffix;
420
15.7M
    }
421
67.6M
}
422
423
0
bool PropertiesAffixPatternProvider::positiveHasPlusSign() const {
424
    // TODO: Change the internal APIs to propagate out the error?
425
0
    ErrorCode localStatus;
426
0
    return AffixUtils::containsType(posPrefix, TYPE_PLUS_SIGN, localStatus) ||
427
0
           AffixUtils::containsType(posSuffix, TYPE_PLUS_SIGN, localStatus);
428
0
}
429
430
521k
bool PropertiesAffixPatternProvider::hasNegativeSubpattern() const {
431
521k
    return (
432
521k
        (negSuffix != posSuffix) ||
433
521k
        negPrefix.tempSubString(1) != posPrefix ||
434
521k
        negPrefix.charAt(0) != u'-'
435
521k
    );
436
521k
}
437
438
2.89k
bool PropertiesAffixPatternProvider::negativeHasMinusSign() const {
439
2.89k
    ErrorCode localStatus;
440
2.89k
    return AffixUtils::containsType(negPrefix, TYPE_MINUS_SIGN, localStatus) ||
441
2.89k
           AffixUtils::containsType(negSuffix, TYPE_MINUS_SIGN, localStatus);
442
2.89k
}
443
444
1.02M
bool PropertiesAffixPatternProvider::hasCurrencySign() const {
445
1.02M
    return isCurrencyPattern;
446
1.02M
}
447
448
4.42k
bool PropertiesAffixPatternProvider::containsSymbolType(AffixPatternType type, UErrorCode& status) const {
449
4.42k
    return AffixUtils::containsType(posPrefix, type, status) ||
450
4.42k
           AffixUtils::containsType(posSuffix, type, status) ||
451
4.42k
           AffixUtils::containsType(negPrefix, type, status) ||
452
4.42k
           AffixUtils::containsType(negSuffix, type, status);
453
4.42k
}
454
455
3.80k
bool PropertiesAffixPatternProvider::hasBody() const {
456
3.80k
    return true;
457
3.80k
}
458
459
2.21k
bool PropertiesAffixPatternProvider::currencyAsDecimal() const {
460
2.21k
    return fCurrencyAsDecimal;
461
2.21k
}
462
463
464
void CurrencyPluralInfoAffixProvider::setTo(const CurrencyPluralInfo& cpi,
465
                                            const DecimalFormatProperties& properties,
466
0
                                            UErrorCode& status) {
467
    // We need to use a PropertiesAffixPatternProvider, not the simpler version ParsedPatternInfo,
468
    // because user-specified affix overrides still need to work.
469
0
    fBogus = false;
470
0
    DecimalFormatProperties pluralProperties(properties);
471
0
    for (int32_t plural = 0; plural < StandardPlural::COUNT; plural++) {
472
0
        const char* keyword = StandardPlural::getKeyword(static_cast<StandardPlural::Form>(plural));
473
0
        UnicodeString patternString;
474
0
        patternString = cpi.getCurrencyPluralPattern(keyword, patternString);
475
0
        PatternParser::parseToExistingProperties(
476
0
                patternString,
477
0
                pluralProperties,
478
0
                IGNORE_ROUNDING_NEVER,
479
0
                status);
480
0
        affixesByPlural[plural].setTo(pluralProperties, status);
481
0
    }
482
0
}
483
484
0
char16_t CurrencyPluralInfoAffixProvider::charAt(int32_t flags, int32_t i) const {
485
0
    int32_t pluralOrdinal = (flags & AFFIX_PLURAL_MASK);
486
0
    return affixesByPlural[pluralOrdinal].charAt(flags, i);
487
0
}
488
489
0
int32_t CurrencyPluralInfoAffixProvider::length(int32_t flags) const {
490
0
    int32_t pluralOrdinal = (flags & AFFIX_PLURAL_MASK);
491
0
    return affixesByPlural[pluralOrdinal].length(flags);
492
0
}
493
494
0
UnicodeString CurrencyPluralInfoAffixProvider::getString(int32_t flags) const {
495
0
    int32_t pluralOrdinal = (flags & AFFIX_PLURAL_MASK);
496
0
    return affixesByPlural[pluralOrdinal].getString(flags);
497
0
}
498
499
0
bool CurrencyPluralInfoAffixProvider::positiveHasPlusSign() const {
500
0
    return affixesByPlural[StandardPlural::OTHER].positiveHasPlusSign();
501
0
}
502
503
0
bool CurrencyPluralInfoAffixProvider::hasNegativeSubpattern() const {
504
0
    return affixesByPlural[StandardPlural::OTHER].hasNegativeSubpattern();
505
0
}
506
507
0
bool CurrencyPluralInfoAffixProvider::negativeHasMinusSign() const {
508
0
    return affixesByPlural[StandardPlural::OTHER].negativeHasMinusSign();
509
0
}
510
511
0
bool CurrencyPluralInfoAffixProvider::hasCurrencySign() const {
512
0
    return affixesByPlural[StandardPlural::OTHER].hasCurrencySign();
513
0
}
514
515
0
bool CurrencyPluralInfoAffixProvider::containsSymbolType(AffixPatternType type, UErrorCode& status) const {
516
0
    return affixesByPlural[StandardPlural::OTHER].containsSymbolType(type, status);
517
0
}
518
519
0
bool CurrencyPluralInfoAffixProvider::hasBody() const {
520
0
    return affixesByPlural[StandardPlural::OTHER].hasBody();
521
0
}
522
523
0
bool CurrencyPluralInfoAffixProvider::currencyAsDecimal() const {
524
0
    return affixesByPlural[StandardPlural::OTHER].currencyAsDecimal();
525
0
}
526
527
528
#endif /* #if !UCONFIG_NO_FORMATTING */