Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/intl/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) {
27
0
    this->patternInfo = patternInfo;
28
0
}
29
30
0
void MutablePatternModifier::setPatternAttributes(UNumberSignDisplay signDisplay, bool perMille) {
31
0
    this->signDisplay = signDisplay;
32
0
    this->perMilleReplacesPercent = perMille;
33
0
}
34
35
void MutablePatternModifier::setSymbols(const DecimalFormatSymbols* symbols,
36
                                        const CurrencySymbols* currencySymbols,
37
0
                                        const UNumberUnitWidth unitWidth, const PluralRules* rules) {
38
0
    U_ASSERT((rules != nullptr) == needsPlurals());
39
0
    this->symbols = symbols;
40
0
    this->currencySymbols = currencySymbols;
41
0
    this->unitWidth = unitWidth;
42
0
    this->rules = rules;
43
0
}
44
45
0
void MutablePatternModifier::setNumberProperties(int8_t signum, StandardPlural::Form plural) {
46
0
    this->signum = signum;
47
0
    this->plural = plural;
48
0
}
49
50
0
bool MutablePatternModifier::needsPlurals() const {
51
0
    UErrorCode statusLocal = U_ZERO_ERROR;
52
0
    return patternInfo->containsSymbolType(AffixPatternType::TYPE_CURRENCY_TRIPLE, statusLocal);
53
0
    // Silently ignore any error codes.
54
0
}
55
56
0
ImmutablePatternModifier* MutablePatternModifier::createImmutable(UErrorCode& status) {
57
0
    return createImmutableAndChain(nullptr, status);
58
0
}
59
60
ImmutablePatternModifier*
61
0
MutablePatternModifier::createImmutableAndChain(const MicroPropsGenerator* parent, UErrorCode& status) {
62
0
63
0
    // TODO: Move StandardPlural VALUES to standardplural.h
64
0
    static const StandardPlural::Form STANDARD_PLURAL_VALUES[] = {
65
0
            StandardPlural::Form::ZERO,
66
0
            StandardPlural::Form::ONE,
67
0
            StandardPlural::Form::TWO,
68
0
            StandardPlural::Form::FEW,
69
0
            StandardPlural::Form::MANY,
70
0
            StandardPlural::Form::OTHER};
71
0
72
0
    auto pm = new ParameterizedModifier();
73
0
    if (pm == nullptr) {
74
0
        status = U_MEMORY_ALLOCATION_ERROR;
75
0
        return nullptr;
76
0
    }
77
0
78
0
    if (needsPlurals()) {
79
0
        // Slower path when we require the plural keyword.
80
0
        for (StandardPlural::Form plural : STANDARD_PLURAL_VALUES) {
81
0
            setNumberProperties(1, plural);
82
0
            pm->adoptSignPluralModifier(1, plural, createConstantModifier(status));
83
0
            setNumberProperties(0, plural);
84
0
            pm->adoptSignPluralModifier(0, plural, createConstantModifier(status));
85
0
            setNumberProperties(-1, plural);
86
0
            pm->adoptSignPluralModifier(-1, plural, createConstantModifier(status));
87
0
        }
88
0
        if (U_FAILURE(status)) {
89
0
            delete pm;
90
0
            return nullptr;
91
0
        }
92
0
        return new ImmutablePatternModifier(pm, rules, parent);  // adopts pm
93
0
    } else {
94
0
        // Faster path when plural keyword is not needed.
95
0
        setNumberProperties(1, StandardPlural::Form::COUNT);
96
0
        Modifier* positive = createConstantModifier(status);
97
0
        setNumberProperties(0, StandardPlural::Form::COUNT);
98
0
        Modifier* zero = createConstantModifier(status);
99
0
        setNumberProperties(-1, StandardPlural::Form::COUNT);
100
0
        Modifier* negative = createConstantModifier(status);
101
0
        pm->adoptPositiveNegativeModifiers(positive, zero, negative);
102
0
        if (U_FAILURE(status)) {
103
0
            delete pm;
104
0
            return nullptr;
105
0
        }
106
0
        return new ImmutablePatternModifier(pm, nullptr, parent);  // adopts pm
107
0
    }
108
0
}
109
110
0
ConstantMultiFieldModifier* MutablePatternModifier::createConstantModifier(UErrorCode& status) {
111
0
    NumberStringBuilder a;
112
0
    NumberStringBuilder b;
113
0
    insertPrefix(a, 0, status);
114
0
    insertSuffix(b, 0, status);
115
0
    if (patternInfo->hasCurrencySign()) {
116
0
        return new CurrencySpacingEnabledModifier(
117
0
                a, b, !patternInfo->hasBody(), fStrong, *symbols, status);
118
0
    } else {
119
0
        return new ConstantMultiFieldModifier(a, b, !patternInfo->hasBody(), fStrong);
120
0
    }
121
0
}
122
123
ImmutablePatternModifier::ImmutablePatternModifier(ParameterizedModifier* pm, const PluralRules* rules,
124
                                                   const MicroPropsGenerator* parent)
125
0
        : pm(pm), rules(rules), parent(parent) {}
126
127
void ImmutablePatternModifier::processQuantity(DecimalQuantity& quantity, MicroProps& micros,
128
0
                                               UErrorCode& status) const {
129
0
    parent->processQuantity(quantity, micros, status);
130
0
    applyToMicros(micros, quantity);
131
0
}
132
133
0
void ImmutablePatternModifier::applyToMicros(MicroProps& micros, DecimalQuantity& quantity) const {
134
0
    if (rules == nullptr) {
135
0
        micros.modMiddle = pm->getModifier(quantity.signum());
136
0
    } else {
137
0
        // TODO: Fix this. Avoid the copy.
138
0
        DecimalQuantity copy(quantity);
139
0
        copy.roundToInfinity();
140
0
        StandardPlural::Form plural = utils::getStandardPlural(rules, copy);
141
0
        micros.modMiddle = pm->getModifier(quantity.signum(), plural);
142
0
    }
143
0
}
144
145
0
const Modifier* ImmutablePatternModifier::getModifier(int8_t signum, StandardPlural::Form plural) const {
146
0
    if (rules == nullptr) {
147
0
        return pm->getModifier(signum);
148
0
    } else {
149
0
        return pm->getModifier(signum, plural);
150
0
    }
151
0
}
152
153
154
/** Used by the unsafe code path. */
155
0
MicroPropsGenerator& MutablePatternModifier::addToChain(const MicroPropsGenerator* parent) {
156
0
    this->parent = parent;
157
0
    return *this;
158
0
}
159
160
void MutablePatternModifier::processQuantity(DecimalQuantity& fq, MicroProps& micros,
161
0
                                             UErrorCode& status) const {
162
0
    parent->processQuantity(fq, micros, status);
163
0
    // The unsafe code path performs self-mutation, so we need a const_cast.
164
0
    // This method needs to be const because it overrides a const method in the parent class.
165
0
    auto nonConstThis = const_cast<MutablePatternModifier*>(this);
166
0
    if (needsPlurals()) {
167
0
        // TODO: Fix this. Avoid the copy.
168
0
        DecimalQuantity copy(fq);
169
0
        micros.rounder.apply(copy, status);
170
0
        nonConstThis->setNumberProperties(fq.signum(), utils::getStandardPlural(rules, copy));
171
0
    } else {
172
0
        nonConstThis->setNumberProperties(fq.signum(), StandardPlural::Form::COUNT);
173
0
    }
174
0
    micros.modMiddle = this;
175
0
}
176
177
int32_t MutablePatternModifier::apply(NumberStringBuilder& output, int32_t leftIndex, int32_t rightIndex,
178
0
                                      UErrorCode& status) const {
179
0
    // The unsafe code path performs self-mutation, so we need a const_cast.
180
0
    // This method needs to be const because it overrides a const method in the parent class.
181
0
    auto nonConstThis = const_cast<MutablePatternModifier*>(this);
182
0
    int32_t prefixLen = nonConstThis->insertPrefix(output, leftIndex, status);
183
0
    int32_t suffixLen = nonConstThis->insertSuffix(output, rightIndex + prefixLen, status);
184
0
    // If the pattern had no decimal stem body (like #,##0.00), overwrite the value.
185
0
    int32_t overwriteLen = 0;
186
0
    if (!patternInfo->hasBody()) {
187
0
        overwriteLen = output.splice(
188
0
                leftIndex + prefixLen,
189
0
                rightIndex + prefixLen,
190
0
                UnicodeString(),
191
0
                0,
192
0
                0,
193
0
                UNUM_FIELD_COUNT,
194
0
                status);
195
0
    }
196
0
    CurrencySpacingEnabledModifier::applyCurrencySpacing(
197
0
            output,
198
0
            leftIndex,
199
0
            prefixLen,
200
0
            rightIndex + overwriteLen + prefixLen,
201
0
            suffixLen,
202
0
            *symbols,
203
0
            status);
204
0
    return prefixLen + overwriteLen + suffixLen;
205
0
}
206
207
0
int32_t MutablePatternModifier::getPrefixLength(UErrorCode& status) const {
208
0
    // The unsafe code path performs self-mutation, so we need a const_cast.
209
0
    // This method needs to be const because it overrides a const method in the parent class.
210
0
    auto nonConstThis = const_cast<MutablePatternModifier*>(this);
211
0
212
0
    // Enter and exit CharSequence Mode to get the length.
213
0
    nonConstThis->prepareAffix(true);
214
0
    int result = AffixUtils::unescapedCodePointCount(currentAffix, *this, status);  // prefix length
215
0
    return result;
216
0
}
217
218
0
int32_t MutablePatternModifier::getCodePointCount(UErrorCode& status) const {
219
0
    // The unsafe code path performs self-mutation, so we need a const_cast.
220
0
    // 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
0
223
0
    // Render the affixes to get the length
224
0
    nonConstThis->prepareAffix(true);
225
0
    int result = AffixUtils::unescapedCodePointCount(currentAffix, *this, status);  // prefix length
226
0
    nonConstThis->prepareAffix(false);
227
0
    result += AffixUtils::unescapedCodePointCount(currentAffix, *this, status);  // suffix length
228
0
    return result;
229
0
}
230
231
0
bool MutablePatternModifier::isStrong() const {
232
0
    return fStrong;
233
0
}
234
235
0
int32_t MutablePatternModifier::insertPrefix(NumberStringBuilder& sb, int position, UErrorCode& status) {
236
0
    prepareAffix(true);
237
0
    int length = AffixUtils::unescape(currentAffix, sb, position, *this, status);
238
0
    return length;
239
0
}
240
241
0
int32_t MutablePatternModifier::insertSuffix(NumberStringBuilder& sb, int position, UErrorCode& status) {
242
0
    prepareAffix(false);
243
0
    int length = AffixUtils::unescape(currentAffix, sb, position, *this, status);
244
0
    return length;
245
0
}
246
247
/** This method contains the heart of the logic for rendering LDML affix strings. */
248
0
void MutablePatternModifier::prepareAffix(bool isPrefix) {
249
0
    PatternStringUtils::patternInfoToStringBuilder(
250
0
            *patternInfo, isPrefix, signum, signDisplay, plural, perMilleReplacesPercent, currentAffix);
251
0
}
252
253
0
UnicodeString MutablePatternModifier::getSymbol(AffixPatternType type) const {
254
0
    UErrorCode localStatus = U_ZERO_ERROR;
255
0
    switch (type) {
256
0
        case AffixPatternType::TYPE_MINUS_SIGN:
257
0
            return symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kMinusSignSymbol);
258
0
        case AffixPatternType::TYPE_PLUS_SIGN:
259
0
            return symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPlusSignSymbol);
260
0
        case AffixPatternType::TYPE_PERCENT:
261
0
            return symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPercentSymbol);
262
0
        case AffixPatternType::TYPE_PERMILLE:
263
0
            return symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPerMillSymbol);
264
0
        case AffixPatternType::TYPE_CURRENCY_SINGLE: {
265
0
            // UnitWidth ISO and HIDDEN overrides the singular currency symbol.
266
0
            if (unitWidth == UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE) {
267
0
                return currencySymbols->getIntlCurrencySymbol(localStatus);
268
0
            } else if (unitWidth == UNumberUnitWidth::UNUM_UNIT_WIDTH_HIDDEN) {
269
0
                return UnicodeString();
270
0
            } else if (unitWidth == UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW) {
271
0
                return currencySymbols->getNarrowCurrencySymbol(localStatus);
272
0
            } else {
273
0
                return currencySymbols->getCurrencySymbol(localStatus);
274
0
            }
275
0
        }
276
0
        case AffixPatternType::TYPE_CURRENCY_DOUBLE:
277
0
            return currencySymbols->getIntlCurrencySymbol(localStatus);
278
0
        case AffixPatternType::TYPE_CURRENCY_TRIPLE:
279
0
            // NOTE: This is the code path only for patterns containing "¤¤¤".
280
0
            // Plural currencies set via the API are formatted in LongNameHandler.
281
0
            // This code path is used by DecimalFormat via CurrencyPluralInfo.
282
0
            U_ASSERT(plural != StandardPlural::Form::COUNT);
283
0
            return currencySymbols->getPluralName(plural, localStatus);
284
0
        case AffixPatternType::TYPE_CURRENCY_QUAD:
285
0
            return UnicodeString(u"\uFFFD");
286
0
        case AffixPatternType::TYPE_CURRENCY_QUINT:
287
0
            return UnicodeString(u"\uFFFD");
288
0
        default:
289
0
            U_ASSERT(false);
290
0
            return UnicodeString();
291
0
    }
292
0
}
293
294
0
UnicodeString MutablePatternModifier::toUnicodeString() const {
295
0
    // Never called by AffixUtils
296
0
    U_ASSERT(false);
297
0
    return UnicodeString();
298
0
}
299
300
#endif /* #if !UCONFIG_NO_FORMATTING */