Coverage Report

Created: 2025-06-24 06:43

/src/icu/source/i18n/number_patternmodifier.cpp
Line
Count
Source (jump to first uncovered line)
1
// © 2017 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
#include "cstring.h"
9
#include "number_patternmodifier.h"
10
#include "unicode/dcfmtsym.h"
11
#include "unicode/ucurr.h"
12
#include "unicode/unistr.h"
13
#include "number_microprops.h"
14
15
using namespace icu;
16
using namespace icu::number;
17
using namespace icu::number::impl;
18
19
20
0
AffixPatternProvider::~AffixPatternProvider() = default;
21
22
23
MutablePatternModifier::MutablePatternModifier(bool isStrong)
24
0
        : fStrong(isStrong) {}
25
26
0
void MutablePatternModifier::setPatternInfo(const AffixPatternProvider* patternInfo, Field field) {
27
0
    fPatternInfo = patternInfo;
28
0
    fField = field;
29
0
}
30
31
void MutablePatternModifier::setPatternAttributes(
32
        UNumberSignDisplay signDisplay,
33
        bool perMille,
34
0
        bool approximately) {
35
0
    fSignDisplay = signDisplay;
36
0
    fPerMilleReplacesPercent = perMille;
37
0
    fApproximately = approximately;
38
0
}
39
40
void MutablePatternModifier::setSymbols(const DecimalFormatSymbols* symbols,
41
                                        const CurrencyUnit& currency,
42
                                        const UNumberUnitWidth unitWidth,
43
                                        const PluralRules* rules,
44
0
                                        UErrorCode& status) {
45
0
    U_ASSERT((rules != nullptr) == needsPlurals());
46
0
    fSymbols = symbols;
47
0
    fCurrencySymbols = {currency, symbols->getLocale(), *symbols, status};
48
0
    fUnitWidth = unitWidth;
49
0
    fRules = rules;
50
0
}
51
52
0
void MutablePatternModifier::setNumberProperties(Signum signum, StandardPlural::Form plural) {
53
0
    fSignum = signum;
54
0
    fPlural = plural;
55
0
}
56
57
0
bool MutablePatternModifier::needsPlurals() const {
58
0
    UErrorCode statusLocal = U_ZERO_ERROR;
59
0
    return fPatternInfo->containsSymbolType(AffixPatternType::TYPE_CURRENCY_TRIPLE, statusLocal);
60
    // Silently ignore any error codes.
61
0
}
62
63
0
ImmutablePatternModifier* MutablePatternModifier::createImmutable(UErrorCode& status) {
64
    // TODO: Move StandardPlural VALUES to standardplural.h
65
0
    static const StandardPlural::Form STANDARD_PLURAL_VALUES[] = {
66
0
            StandardPlural::Form::ZERO,
67
0
            StandardPlural::Form::ONE,
68
0
            StandardPlural::Form::TWO,
69
0
            StandardPlural::Form::FEW,
70
0
            StandardPlural::Form::MANY,
71
0
            StandardPlural::Form::OTHER};
72
73
0
    auto pm = new AdoptingModifierStore();
74
0
    if (pm == nullptr) {
75
0
        status = U_MEMORY_ALLOCATION_ERROR;
76
0
        return nullptr;
77
0
    }
78
79
0
    if (needsPlurals()) {
80
        // Slower path when we require the plural keyword.
81
0
        for (StandardPlural::Form plural : STANDARD_PLURAL_VALUES) {
82
0
            setNumberProperties(SIGNUM_POS, plural);
83
0
            pm->adoptModifier(SIGNUM_POS, plural, createConstantModifier(status));
84
0
            setNumberProperties(SIGNUM_NEG_ZERO, plural);
85
0
            pm->adoptModifier(SIGNUM_NEG_ZERO, plural, createConstantModifier(status));
86
0
            setNumberProperties(SIGNUM_POS_ZERO, plural);
87
0
            pm->adoptModifier(SIGNUM_POS_ZERO, plural, createConstantModifier(status));
88
0
            setNumberProperties(SIGNUM_NEG, plural);
89
0
            pm->adoptModifier(SIGNUM_NEG, plural, createConstantModifier(status));
90
0
        }
91
0
        if (U_FAILURE(status)) {
92
0
            delete pm;
93
0
            return nullptr;
94
0
        }
95
0
        return new ImmutablePatternModifier(pm, fRules);  // adopts pm
96
0
    } else {
97
        // Faster path when plural keyword is not needed.
98
0
        setNumberProperties(SIGNUM_POS, StandardPlural::Form::COUNT);
99
0
        pm->adoptModifierWithoutPlural(SIGNUM_POS, createConstantModifier(status));
100
0
        setNumberProperties(SIGNUM_NEG_ZERO, StandardPlural::Form::COUNT);
101
0
        pm->adoptModifierWithoutPlural(SIGNUM_NEG_ZERO, createConstantModifier(status));
102
0
        setNumberProperties(SIGNUM_POS_ZERO, StandardPlural::Form::COUNT);
103
0
        pm->adoptModifierWithoutPlural(SIGNUM_POS_ZERO, createConstantModifier(status));
104
0
        setNumberProperties(SIGNUM_NEG, StandardPlural::Form::COUNT);
105
0
        pm->adoptModifierWithoutPlural(SIGNUM_NEG, createConstantModifier(status));
106
0
        if (U_FAILURE(status)) {
107
0
            delete pm;
108
0
            return nullptr;
109
0
        }
110
0
        return new ImmutablePatternModifier(pm, nullptr);  // adopts pm
111
0
    }
112
0
}
113
114
0
ConstantMultiFieldModifier* MutablePatternModifier::createConstantModifier(UErrorCode& status) {
115
0
    FormattedStringBuilder a;
116
0
    FormattedStringBuilder b;
117
0
    insertPrefix(a, 0, status);
118
0
    insertSuffix(b, 0, status);
119
0
    if (fPatternInfo->hasCurrencySign()) {
120
0
        return new CurrencySpacingEnabledModifier(
121
0
                a, b, !fPatternInfo->hasBody(), fStrong, *fSymbols, status);
122
0
    } else {
123
0
        return new ConstantMultiFieldModifier(a, b, !fPatternInfo->hasBody(), fStrong);
124
0
    }
125
0
}
126
127
ImmutablePatternModifier::ImmutablePatternModifier(AdoptingModifierStore* pm, const PluralRules* rules)
128
0
        : pm(pm), rules(rules), parent(nullptr) {}
129
130
void ImmutablePatternModifier::processQuantity(DecimalQuantity& quantity, MicroProps& micros,
131
0
                                               UErrorCode& status) const {
132
0
    parent->processQuantity(quantity, micros, status);
133
0
    micros.rounder.apply(quantity, status);
134
0
    if (micros.modMiddle != nullptr) {
135
0
        return;
136
0
    }
137
0
    applyToMicros(micros, quantity, status);
138
0
}
139
140
void ImmutablePatternModifier::applyToMicros(
141
0
        MicroProps& micros, const DecimalQuantity& quantity, UErrorCode& status) const {
142
0
    if (rules == nullptr) {
143
0
        micros.modMiddle = pm->getModifierWithoutPlural(quantity.signum());
144
0
    } else {
145
0
        StandardPlural::Form pluralForm = utils::getPluralSafe(micros.rounder, rules, quantity, status);
146
0
        micros.modMiddle = pm->getModifier(quantity.signum(), pluralForm);
147
0
    }
148
0
}
149
150
0
const Modifier* ImmutablePatternModifier::getModifier(Signum signum, StandardPlural::Form plural) const {
151
0
    if (rules == nullptr) {
152
0
        return pm->getModifierWithoutPlural(signum);
153
0
    } else {
154
0
        return pm->getModifier(signum, plural);
155
0
    }
156
0
}
157
158
0
void ImmutablePatternModifier::addToChain(const MicroPropsGenerator* parent) {
159
0
    this->parent = parent;
160
0
}
161
162
163
/** Used by the unsafe code path. */
164
0
MicroPropsGenerator& MutablePatternModifier::addToChain(const MicroPropsGenerator* parent) {
165
0
    fParent = parent;
166
0
    return *this;
167
0
}
168
169
void MutablePatternModifier::processQuantity(DecimalQuantity& fq, MicroProps& micros,
170
0
                                             UErrorCode& status) const {
171
0
    fParent->processQuantity(fq, micros, status);
172
0
    micros.rounder.apply(fq, status);
173
0
    if (micros.modMiddle != nullptr) {
174
0
        return;
175
0
    }
176
    // The unsafe code path performs self-mutation, so we need a const_cast.
177
    // This method needs to be const because it overrides a const method in the parent class.
178
0
    auto nonConstThis = const_cast<MutablePatternModifier*>(this);
179
0
    if (needsPlurals()) {
180
0
        StandardPlural::Form pluralForm = utils::getPluralSafe(micros.rounder, fRules, fq, status);
181
0
        nonConstThis->setNumberProperties(fq.signum(), pluralForm);
182
0
    } else {
183
0
        nonConstThis->setNumberProperties(fq.signum(), StandardPlural::Form::COUNT);
184
0
    }
185
0
    micros.modMiddle = this;
186
0
}
187
188
int32_t MutablePatternModifier::apply(FormattedStringBuilder& output, int32_t leftIndex, int32_t rightIndex,
189
0
                                      UErrorCode& status) const {
190
    // The unsafe code path performs self-mutation, so we need a const_cast.
191
    // This method needs to be const because it overrides a const method in the parent class.
192
0
    auto nonConstThis = const_cast<MutablePatternModifier*>(this);
193
0
    int32_t prefixLen = nonConstThis->insertPrefix(output, leftIndex, status);
194
0
    int32_t suffixLen = nonConstThis->insertSuffix(output, rightIndex + prefixLen, status);
195
    // If the pattern had no decimal stem body (like #,##0.00), overwrite the value.
196
0
    int32_t overwriteLen = 0;
197
0
    if (!fPatternInfo->hasBody()) {
198
0
        overwriteLen = output.splice(
199
0
                leftIndex + prefixLen,
200
0
                rightIndex + prefixLen,
201
0
                UnicodeString(),
202
0
                0,
203
0
                0,
204
0
                kUndefinedField,
205
0
                status);
206
0
    }
207
0
    CurrencySpacingEnabledModifier::applyCurrencySpacing(
208
0
            output,
209
0
            leftIndex,
210
0
            prefixLen,
211
0
            rightIndex + overwriteLen + prefixLen,
212
0
            suffixLen,
213
0
            *fSymbols,
214
0
            status);
215
0
    return prefixLen + overwriteLen + suffixLen;
216
0
}
217
218
0
int32_t MutablePatternModifier::getPrefixLength() const {
219
    // The unsafe code path performs self-mutation, so we need a const_cast.
220
    // This method needs to be const because it overrides a const method in the parent class.
221
0
    auto nonConstThis = const_cast<MutablePatternModifier*>(this);
222
223
    // Enter and exit CharSequence Mode to get the length.
224
0
    UErrorCode status = U_ZERO_ERROR; // status fails only with an iilegal argument exception
225
0
    nonConstThis->prepareAffix(true);
226
0
    int result = AffixUtils::unescapedCodePointCount(currentAffix, *this, status);  // prefix length
227
0
    return result;
228
0
}
229
230
0
int32_t MutablePatternModifier::getCodePointCount() const {
231
    // The unsafe code path performs self-mutation, so we need a const_cast.
232
    // This method needs to be const because it overrides a const method in the parent class.
233
0
    auto nonConstThis = const_cast<MutablePatternModifier*>(this);
234
235
    // Render the affixes to get the length
236
0
    UErrorCode status = U_ZERO_ERROR; // status fails only with an iilegal argument exception
237
0
    nonConstThis->prepareAffix(true);
238
0
    int result = AffixUtils::unescapedCodePointCount(currentAffix, *this, status);  // prefix length
239
0
    nonConstThis->prepareAffix(false);
240
0
    result += AffixUtils::unescapedCodePointCount(currentAffix, *this, status);  // suffix length
241
0
    return result;
242
0
}
243
244
0
bool MutablePatternModifier::isStrong() const {
245
0
    return fStrong;
246
0
}
247
248
0
bool MutablePatternModifier::containsField(Field field) const {
249
0
    (void)field;
250
    // This method is not currently used.
251
0
    UPRV_UNREACHABLE;
252
0
}
253
254
0
void MutablePatternModifier::getParameters(Parameters& output) const {
255
0
    (void)output;
256
    // This method is not currently used.
257
0
    UPRV_UNREACHABLE;
258
0
}
259
260
0
bool MutablePatternModifier::semanticallyEquivalent(const Modifier& other) const {
261
0
    (void)other;
262
    // This method is not currently used.
263
0
    UPRV_UNREACHABLE;
264
0
}
265
266
0
int32_t MutablePatternModifier::insertPrefix(FormattedStringBuilder& sb, int position, UErrorCode& status) {
267
0
    prepareAffix(true);
268
0
    int32_t length = AffixUtils::unescape(currentAffix, sb, position, *this, fField, status);
269
0
    return length;
270
0
}
271
272
0
int32_t MutablePatternModifier::insertSuffix(FormattedStringBuilder& sb, int position, UErrorCode& status) {
273
0
    prepareAffix(false);
274
0
    int32_t length = AffixUtils::unescape(currentAffix, sb, position, *this, fField, status);
275
0
    return length;
276
0
}
277
278
/** This method contains the heart of the logic for rendering LDML affix strings. */
279
0
void MutablePatternModifier::prepareAffix(bool isPrefix) {
280
0
    PatternStringUtils::patternInfoToStringBuilder(
281
0
            *fPatternInfo,
282
0
            isPrefix,
283
0
            PatternStringUtils::resolveSignDisplay(fSignDisplay, fSignum),
284
0
            fApproximately,
285
0
            fPlural,
286
0
            fPerMilleReplacesPercent,
287
0
            currentAffix);
288
0
}
289
290
0
UnicodeString MutablePatternModifier::getSymbol(AffixPatternType type) const {
291
0
    UErrorCode localStatus = U_ZERO_ERROR;
292
0
    switch (type) {
293
0
        case AffixPatternType::TYPE_MINUS_SIGN:
294
0
            return fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kMinusSignSymbol);
295
0
        case AffixPatternType::TYPE_PLUS_SIGN:
296
0
            return fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPlusSignSymbol);
297
0
        case AffixPatternType::TYPE_APPROXIMATELY_SIGN:
298
0
            return fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kApproximatelySignSymbol);
299
0
        case AffixPatternType::TYPE_PERCENT:
300
0
            return fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPercentSymbol);
301
0
        case AffixPatternType::TYPE_PERMILLE:
302
0
            return fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPerMillSymbol);
303
0
        case AffixPatternType::TYPE_CURRENCY_SINGLE: {
304
0
            switch (fUnitWidth) {
305
0
            case UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW:
306
0
                return fCurrencySymbols.getNarrowCurrencySymbol(localStatus);
307
0
            case UNumberUnitWidth::UNUM_UNIT_WIDTH_SHORT:
308
0
                return fCurrencySymbols.getCurrencySymbol(localStatus);
309
0
            case UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE:
310
0
                return fCurrencySymbols.getIntlCurrencySymbol(localStatus);
311
0
            case UNumberUnitWidth::UNUM_UNIT_WIDTH_FORMAL:
312
0
                return fCurrencySymbols.getFormalCurrencySymbol(localStatus);
313
0
            case UNumberUnitWidth::UNUM_UNIT_WIDTH_VARIANT:
314
0
                return fCurrencySymbols.getVariantCurrencySymbol(localStatus);
315
0
            case UNumberUnitWidth::UNUM_UNIT_WIDTH_HIDDEN:
316
0
                return UnicodeString();
317
0
            default:
318
0
                return fCurrencySymbols.getCurrencySymbol(localStatus);
319
0
            }
320
0
        }
321
0
        case AffixPatternType::TYPE_CURRENCY_DOUBLE:
322
0
            return fCurrencySymbols.getIntlCurrencySymbol(localStatus);
323
0
        case AffixPatternType::TYPE_CURRENCY_TRIPLE:
324
            // NOTE: This is the code path only for patterns containing "¤¤¤".
325
            // Plural currencies set via the API are formatted in LongNameHandler.
326
            // This code path is used by DecimalFormat via CurrencyPluralInfo.
327
0
            U_ASSERT(fPlural != StandardPlural::Form::COUNT);
328
0
            return fCurrencySymbols.getPluralName(fPlural, localStatus);
329
0
        case AffixPatternType::TYPE_CURRENCY_QUAD:
330
0
            return UnicodeString(u"\uFFFD");
331
0
        case AffixPatternType::TYPE_CURRENCY_QUINT:
332
0
            return UnicodeString(u"\uFFFD");
333
0
        default:
334
0
            UPRV_UNREACHABLE;
335
0
    }
336
0
}
337
338
0
UnicodeString MutablePatternModifier::toUnicodeString() const {
339
    // Never called by AffixUtils
340
0
    UPRV_UNREACHABLE;
341
0
}
342
343
#endif /* #if !UCONFIG_NO_FORMATTING */