Coverage Report

Created: 2023-03-04 07:00

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