Coverage Report

Created: 2023-02-22 06:51

/src/icu/source/i18n/formattedval_sbimpl.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
// This file contains one implementation of FormattedValue.
9
// Other independent implementations should go into their own cpp file for
10
// better dependency modularization.
11
12
#include "unicode/ustring.h"
13
#include "formattedval_impl.h"
14
#include "number_types.h"
15
#include "formatted_string_builder.h"
16
#include "number_utils.h"
17
#include "static_unicode_sets.h"
18
#include "unicode/listformatter.h"
19
20
U_NAMESPACE_BEGIN
21
22
23
typedef FormattedStringBuilder::Field Field;
24
25
26
FormattedValueStringBuilderImpl::FormattedValueStringBuilderImpl(Field numericField)
27
0
        : fNumericField(numericField) {
28
0
}
29
30
0
FormattedValueStringBuilderImpl::~FormattedValueStringBuilderImpl() {
31
0
}
32
33
34
0
UnicodeString FormattedValueStringBuilderImpl::toString(UErrorCode&) const {
35
0
    return fString.toUnicodeString();
36
0
}
37
38
0
UnicodeString FormattedValueStringBuilderImpl::toTempString(UErrorCode&) const {
39
0
    return fString.toTempUnicodeString();
40
0
}
41
42
0
Appendable& FormattedValueStringBuilderImpl::appendTo(Appendable& appendable, UErrorCode&) const {
43
0
    appendable.appendString(fString.chars(), fString.length());
44
0
    return appendable;
45
0
}
46
47
0
UBool FormattedValueStringBuilderImpl::nextPosition(ConstrainedFieldPosition& cfpos, UErrorCode& status) const {
48
    // NOTE: MSVC sometimes complains when implicitly converting between bool and UBool
49
0
    return nextPositionImpl(cfpos, fNumericField, status) ? true : false;
50
0
}
51
52
0
UBool FormattedValueStringBuilderImpl::nextFieldPosition(FieldPosition& fp, UErrorCode& status) const {
53
0
    int32_t rawField = fp.getField();
54
55
0
    if (rawField == FieldPosition::DONT_CARE) {
56
0
        return false;
57
0
    }
58
59
0
    if (rawField < 0 || rawField >= UNUM_FIELD_COUNT) {
60
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
61
0
        return false;
62
0
    }
63
64
0
    ConstrainedFieldPosition cfpos;
65
0
    cfpos.constrainField(UFIELD_CATEGORY_NUMBER, rawField);
66
0
    cfpos.setState(UFIELD_CATEGORY_NUMBER, rawField, fp.getBeginIndex(), fp.getEndIndex());
67
0
    if (nextPositionImpl(cfpos, kUndefinedField, status)) {
68
0
        fp.setBeginIndex(cfpos.getStart());
69
0
        fp.setEndIndex(cfpos.getLimit());
70
0
        return true;
71
0
    }
72
73
    // Special case: fraction should start after integer if fraction is not present
74
0
    if (rawField == UNUM_FRACTION_FIELD && fp.getEndIndex() == 0) {
75
0
        bool inside = false;
76
0
        int32_t i = fString.fZero;
77
0
        for (; i < fString.fZero + fString.fLength; i++) {
78
0
            if (isIntOrGroup(fString.getFieldPtr()[i]) || fString.getFieldPtr()[i] == Field(UFIELD_CATEGORY_NUMBER, UNUM_DECIMAL_SEPARATOR_FIELD)) {
79
0
                inside = true;
80
0
            } else if (inside) {
81
0
                break;
82
0
            }
83
0
        }
84
0
        fp.setBeginIndex(i - fString.fZero);
85
0
        fp.setEndIndex(i - fString.fZero);
86
0
    }
87
88
0
    return false;
89
0
}
90
91
void FormattedValueStringBuilderImpl::getAllFieldPositions(FieldPositionIteratorHandler& fpih,
92
0
                                               UErrorCode& status) const {
93
0
    ConstrainedFieldPosition cfpos;
94
0
    while (nextPositionImpl(cfpos, kUndefinedField, status)) {
95
0
        fpih.addAttribute(cfpos.getField(), cfpos.getStart(), cfpos.getLimit());
96
0
    }
97
0
}
98
99
// Signal the end of the string using a field that doesn't exist and that is
100
// different from kUndefinedField, which is used for "null field".
101
static constexpr Field kEndField = Field(0xf, 0xf);
102
103
0
bool FormattedValueStringBuilderImpl::nextPositionImpl(ConstrainedFieldPosition& cfpos, Field numericField, UErrorCode& /*status*/) const {
104
0
    int32_t fieldStart = -1;
105
0
    Field currField = kUndefinedField;
106
0
    bool prevIsSpan = false;
107
0
    int32_t nextSpanStart = -1;
108
0
    if (spanIndicesCount > 0) {
109
0
        int64_t si = cfpos.getInt64IterationContext();
110
0
        U_ASSERT(si <= spanIndicesCount);
111
0
        if (si < spanIndicesCount) {
112
0
            nextSpanStart = spanIndices[si].start;
113
0
        }
114
0
        if (si > 0) {
115
0
            prevIsSpan = cfpos.getCategory() == spanIndices[si-1].category
116
0
                && cfpos.getField() == spanIndices[si-1].spanValue;
117
0
        }
118
0
    }
119
0
    bool prevIsNumeric = false;
120
0
    if (numericField != kUndefinedField) {
121
0
        prevIsNumeric = cfpos.getCategory() == numericField.getCategory()
122
0
            && cfpos.getField() == numericField.getField();
123
0
    }
124
0
    bool prevIsInteger = cfpos.getCategory() == UFIELD_CATEGORY_NUMBER
125
0
        && cfpos.getField() == UNUM_INTEGER_FIELD;
126
127
0
    for (int32_t i = fString.fZero + cfpos.getLimit(); i <= fString.fZero + fString.fLength; i++) {
128
0
        Field _field = (i < fString.fZero + fString.fLength) ? fString.getFieldPtr()[i] : kEndField;
129
        // Case 1: currently scanning a field.
130
0
        if (currField != kUndefinedField) {
131
0
            if (currField != _field) {
132
0
                int32_t end = i - fString.fZero;
133
                // Grouping separators can be whitespace; don't throw them out!
134
0
                if (isTrimmable(currField)) {
135
0
                    end = trimBack(i - fString.fZero);
136
0
                }
137
0
                if (end <= fieldStart) {
138
                    // Entire field position is ignorable; skip.
139
0
                    fieldStart = -1;
140
0
                    currField = kUndefinedField;
141
0
                    i--;  // look at this index again
142
0
                    continue;
143
0
                }
144
0
                int32_t start = fieldStart;
145
0
                if (isTrimmable(currField)) {
146
0
                    start = trimFront(start);
147
0
                }
148
0
                cfpos.setState(currField.getCategory(), currField.getField(), start, end);
149
0
                return true;
150
0
            }
151
0
            continue;
152
0
        }
153
        // Special case: emit normalField if we are pointing at the end of spanField.
154
0
        if (i > fString.fZero && prevIsSpan) {
155
0
            int64_t si = cfpos.getInt64IterationContext() - 1;
156
0
            U_ASSERT(si >= 0);
157
0
            int32_t previ = i - spanIndices[si].length;
158
0
            U_ASSERT(previ >= fString.fZero);
159
0
            Field prevField = fString.getFieldPtr()[previ];
160
0
            if (prevField == Field(UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD)) {
161
                // Special handling for ULISTFMT_ELEMENT_FIELD
162
0
                if (cfpos.matchesField(UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD)) {
163
0
                    fieldStart = i - fString.fZero - spanIndices[si].length;
164
0
                    int32_t end = fieldStart + spanIndices[si].length;
165
0
                    cfpos.setState(
166
0
                        UFIELD_CATEGORY_LIST,
167
0
                        ULISTFMT_ELEMENT_FIELD,
168
0
                        fieldStart,
169
0
                        end);
170
0
                    return true;
171
0
                } else {
172
0
                    prevIsSpan = false;
173
0
                }
174
0
            } else {
175
                // Re-wind, since there may be multiple fields in the span.
176
0
                i = previ;
177
0
                _field = prevField;
178
0
            }
179
0
        }
180
        // Special case: coalesce the INTEGER if we are pointing at the end of the INTEGER.
181
0
        if (cfpos.matchesField(UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD)
182
0
                && i > fString.fZero
183
0
                && !prevIsInteger
184
0
                && !prevIsNumeric
185
0
                && isIntOrGroup(fString.getFieldPtr()[i - 1])
186
0
                && !isIntOrGroup(_field)) {
187
0
            int j = i - 1;
188
0
            for (; j >= fString.fZero && isIntOrGroup(fString.getFieldPtr()[j]); j--) {}
189
0
            cfpos.setState(
190
0
                UFIELD_CATEGORY_NUMBER,
191
0
                UNUM_INTEGER_FIELD,
192
0
                j - fString.fZero + 1,
193
0
                i - fString.fZero);
194
0
            return true;
195
0
        }
196
        // Special case: coalesce NUMERIC if we are pointing at the end of the NUMERIC.
197
0
        if (numericField != kUndefinedField
198
0
                && cfpos.matchesField(numericField.getCategory(), numericField.getField())
199
0
                && i > fString.fZero
200
0
                && !prevIsNumeric
201
0
                && fString.getFieldPtr()[i - 1].isNumeric()
202
0
                && !_field.isNumeric()) {
203
            // Re-wind to the beginning of the field and then emit it
204
0
            int32_t j = i - 1;
205
0
            for (; j >= fString.fZero && fString.getFieldPtr()[j].isNumeric(); j--) {}
206
0
            cfpos.setState(
207
0
                numericField.getCategory(),
208
0
                numericField.getField(),
209
0
                j - fString.fZero + 1,
210
0
                i - fString.fZero);
211
0
            return true;
212
0
        }
213
        // Check for span field
214
0
        if (!prevIsSpan && (
215
0
                _field == Field(UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD) ||
216
0
                i - fString.fZero == nextSpanStart)) {
217
0
            int64_t si = cfpos.getInt64IterationContext();
218
0
            if (si >= spanIndicesCount) {
219
0
                break;
220
0
            }
221
0
            UFieldCategory spanCategory = spanIndices[si].category;
222
0
            int32_t spanValue = spanIndices[si].spanValue;
223
0
            int32_t length = spanIndices[si].length;
224
0
            cfpos.setInt64IterationContext(si + 1);
225
0
            if (si + 1 < spanIndicesCount) {
226
0
                nextSpanStart = spanIndices[si + 1].start;
227
0
            }
228
0
            if (cfpos.matchesField(spanCategory, spanValue)) {
229
0
                fieldStart = i - fString.fZero;
230
0
                int32_t end = fieldStart + length;
231
0
                cfpos.setState(
232
0
                    spanCategory,
233
0
                    spanValue,
234
0
                    fieldStart,
235
0
                    end);
236
0
                return true;
237
0
            } else if (_field == Field(UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD)) {
238
                // Special handling for ULISTFMT_ELEMENT_FIELD
239
0
                if (cfpos.matchesField(UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD)) {
240
0
                    fieldStart = i - fString.fZero;
241
0
                    int32_t end = fieldStart + length;
242
0
                    cfpos.setState(
243
0
                        UFIELD_CATEGORY_LIST,
244
0
                        ULISTFMT_ELEMENT_FIELD,
245
0
                        fieldStart,
246
0
                        end);
247
0
                    return true;
248
0
                } else {
249
                    // Failed to match; jump ahead
250
0
                    i += length - 1;
251
                    // goto loopend
252
0
                }
253
0
            }
254
0
        }
255
        // Special case: skip over INTEGER; will be coalesced later.
256
0
        else if (_field == Field(UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD)) {
257
0
            _field = kUndefinedField;
258
0
        }
259
        // No field starting at this position.
260
0
        else if (_field.isUndefined() || _field == kEndField) {
261
            // goto loopend
262
0
        }
263
        // No SpanField
264
0
        else if (cfpos.matchesField(_field.getCategory(), _field.getField())) {
265
0
            fieldStart = i - fString.fZero;
266
0
            currField = _field;
267
0
        }
268
        // loopend:
269
0
        prevIsSpan = false;
270
0
        prevIsNumeric = false;
271
0
        prevIsInteger = false;
272
0
    }
273
274
0
    U_ASSERT(currField == kUndefinedField);
275
    // Always set the position to the end so that we don't revisit previous sections
276
0
    cfpos.setState(
277
0
        cfpos.getCategory(),
278
0
        cfpos.getField(),
279
0
        fString.fLength,
280
0
        fString.fLength);
281
0
    return false;
282
0
}
283
284
0
void FormattedValueStringBuilderImpl::appendSpanInfo(UFieldCategory category, int32_t spanValue, int32_t start, int32_t length, UErrorCode& status) {
285
0
    if (U_FAILURE(status)) { return; }
286
0
    U_ASSERT(spanIndices.getCapacity() >= spanValue);
287
0
    if (spanIndices.getCapacity() == spanValue) {
288
0
        if (!spanIndices.resize(spanValue * 2, spanValue)) {
289
0
            status = U_MEMORY_ALLOCATION_ERROR;
290
0
            return;
291
0
        }
292
0
    }
293
0
    spanIndices[spanValue] = {category, spanValue, start, length};
294
0
    spanIndicesCount++;
295
0
}
296
297
0
void FormattedValueStringBuilderImpl::prependSpanInfo(UFieldCategory category, int32_t spanValue, int32_t start, int32_t length, UErrorCode& status) {
298
0
    if (U_FAILURE(status)) { return; }
299
0
    U_ASSERT(spanIndices.getCapacity() >= spanValue);
300
0
    if (spanIndices.getCapacity() == spanValue) {
301
0
        if (!spanIndices.resize(spanValue * 2, spanValue)) {
302
0
            status = U_MEMORY_ALLOCATION_ERROR;
303
0
            return;
304
0
        }
305
0
    }
306
0
    for (int32_t i = spanValue - 1; i >= 0; i--) {
307
0
        spanIndices[i+1] = spanIndices[i];
308
0
    }
309
0
    spanIndices[0] = {category, spanValue, start, length};
310
0
    spanIndicesCount++;
311
0
}
312
313
0
bool FormattedValueStringBuilderImpl::isIntOrGroup(Field field) {
314
0
    return field == Field(UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD)
315
0
        || field == Field(UFIELD_CATEGORY_NUMBER, UNUM_GROUPING_SEPARATOR_FIELD);
316
0
}
317
318
0
bool FormattedValueStringBuilderImpl::isTrimmable(Field field) {
319
0
    return field != Field(UFIELD_CATEGORY_NUMBER, UNUM_GROUPING_SEPARATOR_FIELD)
320
0
        && field.getCategory() != UFIELD_CATEGORY_LIST;
321
0
}
322
323
0
int32_t FormattedValueStringBuilderImpl::trimBack(int32_t limit) const {
324
0
    return unisets::get(unisets::DEFAULT_IGNORABLES)->spanBack(
325
0
        fString.getCharPtr() + fString.fZero,
326
0
        limit,
327
0
        USET_SPAN_CONTAINED);
328
0
}
329
330
0
int32_t FormattedValueStringBuilderImpl::trimFront(int32_t start) const {
331
0
    return start + unisets::get(unisets::DEFAULT_IGNORABLES)->span(
332
0
        fString.getCharPtr() + fString.fZero + start,
333
0
        fString.fLength - start,
334
0
        USET_SPAN_CONTAINED);
335
0
}
336
337
338
U_NAMESPACE_END
339
340
#endif /* #if !UCONFIG_NO_FORMATTING */