Coverage Report

Created: 2026-06-23 06:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/icu/icu4c/source/i18n/quantityformatter.cpp
Line
Count
Source
1
// © 2016 and later: Unicode, Inc. and others.
2
// License & terms of use: http://www.unicode.org/copyright.html
3
/*
4
******************************************************************************
5
* Copyright (C) 2014-2016, International Business Machines
6
* Corporation and others.  All Rights Reserved.
7
******************************************************************************
8
* quantityformatter.cpp
9
*/
10
11
#include "unicode/utypes.h"
12
13
#if !UCONFIG_NO_FORMATTING
14
15
#include "unicode/simpleformatter.h"
16
#include "quantityformatter.h"
17
#include "uassert.h"
18
#include "unicode/unistr.h"
19
#include "unicode/decimfmt.h"
20
#include "cstring.h"
21
#include "unicode/plurrule.h"
22
#include "charstr.h"
23
#include "unicode/fmtable.h"
24
#include "unicode/fieldpos.h"
25
#include "standardplural.h"
26
#include "uassert.h"
27
#include "number_decimalquantity.h"
28
#include "number_utypes.h"
29
#include "formatted_string_builder.h"
30
31
U_NAMESPACE_BEGIN
32
33
0
QuantityFormatter::QuantityFormatter() {
34
0
    for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) {
35
0
        formatters[i] = nullptr;
36
0
    }
37
0
}
38
39
0
QuantityFormatter::QuantityFormatter(const QuantityFormatter &other) {
40
0
    for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) {
41
0
        if (other.formatters[i] == nullptr) {
42
0
            formatters[i] = nullptr;
43
0
        } else {
44
0
            formatters[i] = new SimpleFormatter(*other.formatters[i]);
45
0
        }
46
0
    }
47
0
}
48
49
QuantityFormatter &QuantityFormatter::operator=(
50
0
        const QuantityFormatter& other) {
51
0
    if (this == &other) {
52
0
        return *this;
53
0
    }
54
0
    for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) {
55
0
        delete formatters[i];
56
0
        if (other.formatters[i] == nullptr) {
57
0
            formatters[i] = nullptr;
58
0
        } else {
59
0
            formatters[i] = new SimpleFormatter(*other.formatters[i]);
60
0
        }
61
0
    }
62
0
    return *this;
63
0
}
64
65
0
QuantityFormatter::~QuantityFormatter() {
66
0
    for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) {
67
0
        delete formatters[i];
68
0
    }
69
0
}
70
71
0
void QuantityFormatter::reset() {
72
0
    for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) {
73
0
        delete formatters[i];
74
0
        formatters[i] = nullptr;
75
0
    }
76
0
}
77
78
UBool QuantityFormatter::addIfAbsent(
79
        const char *variant,
80
        const UnicodeString &rawPattern,
81
0
        UErrorCode &status) {
82
0
    int32_t pluralIndex = StandardPlural::indexFromString(variant, status);
83
0
    if (U_FAILURE(status)) {
84
0
        return false;
85
0
    }
86
0
    if (formatters[pluralIndex] != nullptr) {
87
0
        return true;
88
0
    }
89
0
    SimpleFormatter *newFmt = new SimpleFormatter(rawPattern, 0, 1, status);
90
0
    if (newFmt == nullptr) {
91
0
        status = U_MEMORY_ALLOCATION_ERROR;
92
0
        return false;
93
0
    }
94
0
    if (U_FAILURE(status)) {
95
0
        delete newFmt;
96
0
        return false;
97
0
    }
98
0
    formatters[pluralIndex] = newFmt;
99
0
    return true;
100
0
}
101
102
0
UBool QuantityFormatter::isValid() const {
103
0
    return formatters[StandardPlural::OTHER] != nullptr;
104
0
}
105
106
const SimpleFormatter *QuantityFormatter::getByVariant(
107
0
        const char *variant) const {
108
0
    U_ASSERT(isValid());
109
0
    int32_t pluralIndex = StandardPlural::indexOrOtherIndexFromString(variant);
110
0
    const SimpleFormatter *pattern = formatters[pluralIndex];
111
0
    if (pattern == nullptr) {
112
0
        pattern = formatters[StandardPlural::OTHER];
113
0
    }
114
0
    return pattern;
115
0
}
116
117
UnicodeString &QuantityFormatter::format(
118
            const Formattable &number,
119
            const NumberFormat &fmt,
120
            const PluralRules &rules,
121
            UnicodeString &appendTo,
122
            FieldPosition &pos,
123
0
            UErrorCode &status) const {
124
0
    UnicodeString formattedNumber;
125
0
    StandardPlural::Form p = selectPlural(number, fmt, rules, formattedNumber, pos, status);
126
0
    if (U_FAILURE(status)) {
127
0
        return appendTo;
128
0
    }
129
0
    const SimpleFormatter *pattern = formatters[p];
130
0
    if (pattern == nullptr) {
131
0
        pattern = formatters[StandardPlural::OTHER];
132
0
        if (pattern == nullptr) {
133
0
            status = U_INVALID_STATE_ERROR;
134
0
            return appendTo;
135
0
        }
136
0
    }
137
0
    return format(*pattern, formattedNumber, appendTo, pos, status);
138
0
}
139
140
// The following methods live here so that class PluralRules does not depend on number formatting,
141
// and the SimpleFormatter does not depend on FieldPosition.
142
143
StandardPlural::Form QuantityFormatter::selectPlural(
144
            const Formattable &number,
145
            const NumberFormat &fmt,
146
            const PluralRules &rules,
147
            UnicodeString &formattedNumber,
148
            FieldPosition &pos,
149
0
            UErrorCode &status) {
150
0
    if (U_FAILURE(status)) {
151
0
        return StandardPlural::OTHER;
152
0
    }
153
0
    UnicodeString pluralKeyword;
154
0
    const DecimalFormat *decFmt = dynamic_cast<const DecimalFormat *>(&fmt);
155
0
    if (decFmt != nullptr) {
156
0
        number::impl::DecimalQuantity dq;
157
0
        decFmt->formatToDecimalQuantity(number, dq, status);
158
0
        if (U_FAILURE(status)) {
159
0
            return StandardPlural::OTHER;
160
0
        }
161
0
        pluralKeyword = rules.select(dq);
162
0
        decFmt->format(number, formattedNumber, pos, status);
163
0
    } else {
164
0
        if (number.getType() == Formattable::kDouble) {
165
0
            pluralKeyword = rules.select(number.getDouble());
166
0
        } else if (number.getType() == Formattable::kLong) {
167
0
            pluralKeyword = rules.select(number.getLong());
168
0
        } else if (number.getType() == Formattable::kInt64) {
169
0
            pluralKeyword = rules.select(static_cast<double>(number.getInt64()));
170
0
        } else {
171
0
            status = U_ILLEGAL_ARGUMENT_ERROR;
172
0
            return StandardPlural::OTHER;
173
0
        }
174
0
        fmt.format(number, formattedNumber, pos, status);
175
0
    }
176
0
    return StandardPlural::orOtherFromString(pluralKeyword);
177
0
}
178
179
void QuantityFormatter::formatAndSelect(
180
        double quantity,
181
        const NumberFormat& fmt,
182
        const PluralRules& rules,
183
        FormattedStringBuilder& output,
184
        StandardPlural::Form& pluralForm,
185
0
        UErrorCode& status) {
186
0
    UnicodeString pluralKeyword;
187
0
    const DecimalFormat* df = dynamic_cast<const DecimalFormat*>(&fmt);
188
0
    if (df != nullptr) {
189
0
        number::impl::UFormattedNumberData fn;
190
0
        fn.quantity.setToDouble(quantity);
191
0
        const number::LocalizedNumberFormatter* lnf = df->toNumberFormatter(status);
192
0
        if (U_FAILURE(status)) {
193
0
            return;
194
0
        }
195
0
        lnf->formatImpl(&fn, status);
196
0
        if (U_FAILURE(status)) {
197
0
            return;
198
0
        }
199
0
        output = std::move(fn.getStringRef());
200
0
        pluralKeyword = rules.select(fn.quantity);
201
0
    } else {
202
0
        UnicodeString result;
203
0
        fmt.format(quantity, result, status);
204
0
        if (U_FAILURE(status)) {
205
0
            return;
206
0
        }
207
        // This code path is probably RBNF. Use the generic numeric field.
208
0
        output.append(result, kGeneralNumericField, status);
209
0
        if (U_FAILURE(status)) {
210
0
            return;
211
0
        }
212
0
        pluralKeyword = rules.select(quantity);
213
0
    }
214
0
    pluralForm = StandardPlural::orOtherFromString(pluralKeyword);
215
0
}
216
217
UnicodeString &QuantityFormatter::format(
218
            const SimpleFormatter &pattern,
219
            const UnicodeString &value,
220
            UnicodeString &appendTo,
221
            FieldPosition &pos,
222
0
            UErrorCode &status) {
223
0
    if (U_FAILURE(status)) {
224
0
        return appendTo;
225
0
    }
226
0
    const UnicodeString *param = &value;
227
0
    int32_t offset;
228
0
    pattern.formatAndAppend(&param, 1, appendTo, &offset, 1, status);
229
0
    if (pos.getBeginIndex() != 0 || pos.getEndIndex() != 0) {
230
0
        if (offset >= 0) {
231
0
            pos.setBeginIndex(pos.getBeginIndex() + offset);
232
0
            pos.setEndIndex(pos.getEndIndex() + offset);
233
0
        } else {
234
0
            pos.setBeginIndex(0);
235
0
            pos.setEndIndex(0);
236
0
        }
237
0
    }
238
0
    return appendTo;
239
0
}
240
241
U_NAMESPACE_END
242
243
#endif /* #if !UCONFIG_NO_FORMATTING */