Coverage Report

Created: 2018-09-25 14:53

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