Coverage Report

Created: 2025-06-24 06:43

/src/icu/source/i18n/listformatter.cpp
Line
Count
Source (jump to first uncovered line)
1
// © 2016 and later: Unicode, Inc. and others.
2
// License & terms of use: http://www.unicode.org/copyright.html
3
/*
4
*******************************************************************************
5
*
6
*   Copyright (C) 2013-2016, International Business Machines
7
*   Corporation and others.  All Rights Reserved.
8
*
9
*******************************************************************************
10
*   file name:  listformatter.cpp
11
*   encoding:   UTF-8
12
*   tab size:   8 (not used)
13
*   indentation:4
14
*
15
*   created on: 2012aug27
16
*   created by: Umesh P. Nair
17
*/
18
19
#include "unicode/utypes.h"
20
21
#if !UCONFIG_NO_FORMATTING
22
23
#include "cmemory.h"
24
#include "unicode/fpositer.h"  // FieldPositionIterator
25
#include "unicode/listformatter.h"
26
#include "unicode/simpleformatter.h"
27
#include "unicode/ulistformatter.h"
28
#include "unicode/uscript.h"
29
#include "fphdlimp.h"
30
#include "mutex.h"
31
#include "hash.h"
32
#include "cstring.h"
33
#include "uarrsort.h"
34
#include "ulocimp.h"
35
#include "charstr.h"
36
#include "ucln_in.h"
37
#include "uresimp.h"
38
#include "resource.h"
39
#include "formattedval_impl.h"
40
41
U_NAMESPACE_BEGIN
42
43
namespace {
44
45
class PatternHandler : public UObject {
46
public:
47
    PatternHandler(const UnicodeString& two, const UnicodeString& end, UErrorCode& errorCode) :
48
0
        twoPattern(two, 2, 2, errorCode),
49
0
        endPattern(end, 2, 2, errorCode) {  }
50
51
    PatternHandler(const SimpleFormatter& two, const SimpleFormatter& end) :
52
0
        twoPattern(two),
53
0
        endPattern(end) { }
54
55
    virtual ~PatternHandler();
56
57
0
    virtual PatternHandler* clone() const { return new PatternHandler(twoPattern, endPattern); }
58
59
    /** Argument: final string in the list. */
60
0
    virtual const SimpleFormatter& getTwoPattern(const UnicodeString&) const {
61
0
        return twoPattern;
62
0
    }
63
64
    /** Argument: final string in the list. */
65
0
    virtual const SimpleFormatter& getEndPattern(const UnicodeString&) const {
66
0
        return endPattern;
67
0
    }
68
69
protected:
70
    SimpleFormatter twoPattern;
71
    SimpleFormatter endPattern;
72
};
73
74
0
PatternHandler::~PatternHandler() {
75
0
}
76
77
class ContextualHandler : public PatternHandler {
78
public:
79
    ContextualHandler(bool (*testFunc)(const UnicodeString& text),
80
                      const UnicodeString& thenTwo,
81
                      const UnicodeString& elseTwo,
82
                      const UnicodeString& thenEnd,
83
                      const UnicodeString& elseEnd,
84
                      UErrorCode& errorCode) :
85
0
        PatternHandler(elseTwo, elseEnd, errorCode),
86
0
        test(testFunc),
87
0
        thenTwoPattern(thenTwo, 2, 2, errorCode),
88
0
        thenEndPattern(thenEnd, 2, 2, errorCode) {  }
89
90
    ContextualHandler(bool (*testFunc)(const UnicodeString& text),
91
                      const SimpleFormatter& thenTwo, SimpleFormatter elseTwo,
92
                      const SimpleFormatter& thenEnd, SimpleFormatter elseEnd) :
93
0
      PatternHandler(elseTwo, elseEnd),
94
0
      test(testFunc),
95
0
      thenTwoPattern(thenTwo),
96
0
      thenEndPattern(thenEnd) { }
97
98
    ~ContextualHandler() override;
99
100
0
    PatternHandler* clone() const override {
101
0
        return new ContextualHandler(
102
0
            test, thenTwoPattern, twoPattern, thenEndPattern, endPattern);
103
0
    }
104
105
    const SimpleFormatter& getTwoPattern(
106
0
        const UnicodeString& text) const override {
107
0
        return (test)(text) ? thenTwoPattern : twoPattern;
108
0
    }
109
110
    const SimpleFormatter& getEndPattern(
111
0
        const UnicodeString& text) const override {
112
0
        return (test)(text) ? thenEndPattern : endPattern;
113
0
    }
114
115
private:
116
    bool (*test)(const UnicodeString&);
117
    SimpleFormatter thenTwoPattern;
118
    SimpleFormatter thenEndPattern;
119
};
120
121
0
ContextualHandler::~ContextualHandler() {
122
0
}
123
124
static const char16_t *spanishY = u"{0} y {1}";
125
static const char16_t *spanishE = u"{0} e {1}";
126
static const char16_t *spanishO = u"{0} o {1}";
127
static const char16_t *spanishU = u"{0} u {1}";
128
static const char16_t *hebrewVav = u"{0} \u05D5{1}";
129
static const char16_t *hebrewVavDash = u"{0} \u05D5-{1}";
130
131
// Condiction to change to e.
132
// Starts with "hi" or "i" but not with "hie" nor "hia"
133
0
static bool shouldChangeToE(const UnicodeString& text) {
134
0
    int32_t len = text.length();
135
0
    if (len == 0) { return false; }
136
    // Case insensitive match hi but not hie nor hia.
137
0
    if ((text[0] == u'h' || text[0] == u'H') &&
138
0
            ((len > 1) && (text[1] == u'i' || text[1] == u'I')) &&
139
0
            ((len == 2) || !(text[2] == u'a' || text[2] == u'A' || text[2] == u'e' || text[2] == u'E'))) {
140
0
        return true;
141
0
    }
142
    // Case insensitive for "start with i"
143
0
    if (text[0] == u'i' || text[0] == u'I') { return true; }
144
0
    return false;
145
0
}
146
147
// Condiction to change to u.
148
// Starts with "o", "ho", and "8". Also "11" by itself.
149
// re: ^((o|ho|8).*|11)$
150
0
static bool shouldChangeToU(const UnicodeString& text) {
151
0
    int32_t len = text.length();
152
0
    if (len == 0) { return false; }
153
    // Case insensitive match o.* and 8.*
154
0
    if (text[0] == u'o' || text[0] == u'O' || text[0] == u'8') { return true; }
155
    // Case insensitive match ho.*
156
0
    if ((text[0] == u'h' || text[0] == u'H') &&
157
0
            ((len > 1) && (text[1] == 'o' || text[1] == u'O'))) {
158
0
        return true;
159
0
    }
160
    // match "^11$" and "^11 .*"
161
0
    if ((len >= 2) && text[0] == u'1' && text[1] == u'1' && (len == 2 || text[2] == u' ')) { return true; }
162
0
    return false;
163
0
}
164
165
// Condiction to change to VAV follow by a dash.
166
// Starts with non Hebrew letter.
167
0
static bool shouldChangeToVavDash(const UnicodeString& text) {
168
0
    if (text.isEmpty()) { return false; }
169
0
    UErrorCode status = U_ZERO_ERROR;
170
0
    return uscript_getScript(text.char32At(0), &status) != USCRIPT_HEBREW;
171
0
}
172
173
PatternHandler* createPatternHandler(
174
        const char* lang, const UnicodeString& two, const UnicodeString& end,
175
0
    UErrorCode& status) {
176
0
    if (uprv_strcmp(lang, "es") == 0) {
177
        // Spanish
178
0
        UnicodeString spanishYStr(true, spanishY, -1);
179
0
        bool twoIsY = two == spanishYStr;
180
0
        bool endIsY = end == spanishYStr;
181
0
        if (twoIsY || endIsY) {
182
0
            UnicodeString replacement(true, spanishE, -1);
183
0
            return new ContextualHandler(
184
0
                shouldChangeToE,
185
0
                twoIsY ? replacement : two, two,
186
0
                endIsY ? replacement : end, end, status);
187
0
        }
188
0
        UnicodeString spanishOStr(true, spanishO, -1);
189
0
        bool twoIsO = two == spanishOStr;
190
0
        bool endIsO = end == spanishOStr;
191
0
        if (twoIsO || endIsO) {
192
0
            UnicodeString replacement(true, spanishU, -1);
193
0
            return new ContextualHandler(
194
0
                shouldChangeToU,
195
0
                twoIsO ? replacement : two, two,
196
0
                endIsO ? replacement : end, end, status);
197
0
        }
198
0
    } else if (uprv_strcmp(lang, "he") == 0 || uprv_strcmp(lang, "iw") == 0) {
199
        // Hebrew
200
0
        UnicodeString hebrewVavStr(true, hebrewVav, -1);
201
0
        bool twoIsVav = two == hebrewVavStr;
202
0
        bool endIsVav = end == hebrewVavStr;
203
0
        if (twoIsVav || endIsVav) {
204
0
            UnicodeString replacement(true, hebrewVavDash, -1);
205
0
            return new ContextualHandler(
206
0
                shouldChangeToVavDash,
207
0
                twoIsVav ? replacement : two, two,
208
0
                endIsVav ? replacement : end, end, status);
209
0
        }
210
0
    }
211
0
    return new PatternHandler(two, end, status);
212
0
}
213
214
}  // namespace
215
216
struct ListFormatInternal : public UMemory {
217
    SimpleFormatter startPattern;
218
    SimpleFormatter middlePattern;
219
    LocalPointer<PatternHandler> patternHandler;
220
221
ListFormatInternal(
222
        const UnicodeString& two,
223
        const UnicodeString& start,
224
        const UnicodeString& middle,
225
        const UnicodeString& end,
226
        const Locale& locale,
227
        UErrorCode &errorCode) :
228
0
        startPattern(start, 2, 2, errorCode),
229
0
        middlePattern(middle, 2, 2, errorCode),
230
0
        patternHandler(createPatternHandler(locale.getLanguage(), two, end, errorCode), errorCode) { }
231
232
ListFormatInternal(const ListFormatData &data, UErrorCode &errorCode) :
233
0
        startPattern(data.startPattern, errorCode),
234
0
        middlePattern(data.middlePattern, errorCode),
235
0
        patternHandler(createPatternHandler(
236
0
            data.locale.getLanguage(), data.twoPattern, data.endPattern, errorCode), errorCode) { }
237
238
ListFormatInternal(const ListFormatInternal &other) :
239
0
    startPattern(other.startPattern),
240
0
    middlePattern(other.middlePattern),
241
0
    patternHandler(other.patternHandler->clone()) { }
242
};
243
244
245
class FormattedListData : public FormattedValueStringBuilderImpl {
246
public:
247
0
    FormattedListData(UErrorCode&) : FormattedValueStringBuilderImpl(kUndefinedField) {}
248
    virtual ~FormattedListData();
249
};
250
251
0
FormattedListData::~FormattedListData() = default;
252
253
UPRV_FORMATTED_VALUE_SUBCLASS_AUTO_IMPL(FormattedList)
254
255
256
static Hashtable* listPatternHash = nullptr;
257
258
U_CDECL_BEGIN
259
0
static UBool U_CALLCONV uprv_listformatter_cleanup() {
260
0
    delete listPatternHash;
261
0
    listPatternHash = nullptr;
262
0
    return true;
263
0
}
264
265
static void U_CALLCONV
266
0
uprv_deleteListFormatInternal(void *obj) {
267
0
    delete static_cast<ListFormatInternal *>(obj);
268
0
}
269
270
U_CDECL_END
271
272
ListFormatter::ListFormatter(const ListFormatter& other) :
273
0
        owned(other.owned), data(other.data) {
274
0
    if (other.owned != nullptr) {
275
0
        owned = new ListFormatInternal(*other.owned);
276
0
        data = owned;
277
0
    }
278
0
}
279
280
0
ListFormatter& ListFormatter::operator=(const ListFormatter& other) {
281
0
    if (this == &other) {
282
0
        return *this;
283
0
    }
284
0
    delete owned;
285
0
    if (other.owned) {
286
0
        owned = new ListFormatInternal(*other.owned);
287
0
        data = owned;
288
0
    } else {
289
0
        owned = nullptr;
290
0
        data = other.data;
291
0
    }
292
0
    return *this;
293
0
}
294
295
0
void ListFormatter::initializeHash(UErrorCode& errorCode) {
296
0
    if (U_FAILURE(errorCode)) {
297
0
        return;
298
0
    }
299
300
0
    listPatternHash = new Hashtable();
301
0
    if (listPatternHash == nullptr) {
302
0
        errorCode = U_MEMORY_ALLOCATION_ERROR;
303
0
        return;
304
0
    }
305
306
0
    listPatternHash->setValueDeleter(uprv_deleteListFormatInternal);
307
0
    ucln_i18n_registerCleanup(UCLN_I18N_LIST_FORMATTER, uprv_listformatter_cleanup);
308
309
0
}
310
311
const ListFormatInternal* ListFormatter::getListFormatInternal(
312
0
        const Locale& locale, const char *style, UErrorCode& errorCode) {
313
0
    if (U_FAILURE(errorCode)) {
314
0
        return nullptr;
315
0
    }
316
0
    CharString keyBuffer(locale.getName(), errorCode);
317
0
    keyBuffer.append(':', errorCode).append(style, errorCode);
318
0
    UnicodeString key(keyBuffer.data(), -1, US_INV);
319
0
    ListFormatInternal* result = nullptr;
320
0
    static UMutex listFormatterMutex;
321
0
    {
322
0
        Mutex m(&listFormatterMutex);
323
0
        if (listPatternHash == nullptr) {
324
0
            initializeHash(errorCode);
325
0
            if (U_FAILURE(errorCode)) {
326
0
                return nullptr;
327
0
            }
328
0
        }
329
0
        result = static_cast<ListFormatInternal*>(listPatternHash->get(key));
330
0
    }
331
0
    if (result != nullptr) {
332
0
        return result;
333
0
    }
334
0
    result = loadListFormatInternal(locale, style, errorCode);
335
0
    if (U_FAILURE(errorCode)) {
336
0
        return nullptr;
337
0
    }
338
339
0
    {
340
0
        Mutex m(&listFormatterMutex);
341
0
        ListFormatInternal* temp = static_cast<ListFormatInternal*>(listPatternHash->get(key));
342
0
        if (temp != nullptr) {
343
0
            delete result;
344
0
            result = temp;
345
0
        } else {
346
0
            listPatternHash->put(key, result, errorCode);
347
0
            if (U_FAILURE(errorCode)) {
348
0
                return nullptr;
349
0
            }
350
0
        }
351
0
    }
352
0
    return result;
353
0
}
354
355
0
static const char* typeWidthToStyleString(UListFormatterType type, UListFormatterWidth width) {
356
0
    switch (type) {
357
0
        case ULISTFMT_TYPE_AND:
358
0
            switch (width) {
359
0
                case ULISTFMT_WIDTH_WIDE:
360
0
                    return "standard";
361
0
                case ULISTFMT_WIDTH_SHORT:
362
0
                    return "standard-short";
363
0
                case ULISTFMT_WIDTH_NARROW:
364
0
                    return "standard-narrow";
365
0
                default:
366
0
                    return nullptr;
367
0
            }
368
0
            break;
369
370
0
        case ULISTFMT_TYPE_OR:
371
0
            switch (width) {
372
0
                case ULISTFMT_WIDTH_WIDE:
373
0
                    return "or";
374
0
                case ULISTFMT_WIDTH_SHORT:
375
0
                    return "or-short";
376
0
                case ULISTFMT_WIDTH_NARROW:
377
0
                    return "or-narrow";
378
0
                default:
379
0
                    return nullptr;
380
0
            }
381
0
            break;
382
383
0
        case ULISTFMT_TYPE_UNITS:
384
0
            switch (width) {
385
0
                case ULISTFMT_WIDTH_WIDE:
386
0
                    return "unit";
387
0
                case ULISTFMT_WIDTH_SHORT:
388
0
                    return "unit-short";
389
0
                case ULISTFMT_WIDTH_NARROW:
390
0
                    return "unit-narrow";
391
0
                default:
392
0
                    return nullptr;
393
0
            }
394
0
    }
395
396
0
    return nullptr;
397
0
}
398
399
static const UChar solidus = 0x2F;
400
static const UChar aliasPrefix[] = { 0x6C,0x69,0x73,0x74,0x50,0x61,0x74,0x74,0x65,0x72,0x6E,0x2F }; // "listPattern/"
401
enum {
402
    kAliasPrefixLen = UPRV_LENGTHOF(aliasPrefix),
403
    kStyleLenMax = 24 // longest currently is 14
404
};
405
406
struct ListFormatter::ListPatternsSink : public ResourceSink {
407
    UnicodeString two, start, middle, end;
408
#if ((U_PLATFORM == U_PF_AIX) || (U_PLATFORM == U_PF_OS390)) && (U_CPLUSPLUS_VERSION < 11)
409
    char aliasedStyle[kStyleLenMax+1];
410
    ListPatternsSink() {
411
      uprv_memset(aliasedStyle, 0, kStyleLenMax+1);
412
    }
413
#else
414
    char aliasedStyle[kStyleLenMax+1] = {0};
415
416
0
    ListPatternsSink() {}
417
#endif
418
    virtual ~ListPatternsSink();
419
420
0
    void setAliasedStyle(UnicodeString alias) {
421
0
        int32_t startIndex = alias.indexOf(aliasPrefix, kAliasPrefixLen, 0);
422
0
        if (startIndex < 0) {
423
0
            return;
424
0
        }
425
0
        startIndex += kAliasPrefixLen;
426
0
        int32_t endIndex = alias.indexOf(solidus, startIndex);
427
0
        if (endIndex < 0) {
428
0
            endIndex = alias.length();
429
0
        }
430
0
        alias.extract(startIndex, endIndex-startIndex, aliasedStyle, kStyleLenMax+1, US_INV);
431
0
        aliasedStyle[kStyleLenMax] = 0;
432
0
    }
433
434
0
    void handleValueForPattern(ResourceValue &value, UnicodeString &pattern, UErrorCode &errorCode) {
435
0
        if (pattern.isEmpty()) {
436
0
            if (value.getType() == URES_ALIAS) {
437
0
                if (aliasedStyle[0] == 0) {
438
0
                    setAliasedStyle(value.getAliasUnicodeString(errorCode));
439
0
                }
440
0
            } else {
441
0
                pattern = value.getUnicodeString(errorCode);
442
0
            }
443
0
        }
444
0
    }
445
446
    virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/,
447
0
            UErrorCode &errorCode) {
448
0
        aliasedStyle[0] = 0;
449
0
        if (value.getType() == URES_ALIAS) {
450
0
            setAliasedStyle(value.getAliasUnicodeString(errorCode));
451
0
            return;
452
0
        }
453
0
        ResourceTable listPatterns = value.getTable(errorCode);
454
0
        for (int i = 0; U_SUCCESS(errorCode) && listPatterns.getKeyAndValue(i, key, value); ++i) {
455
0
            if (uprv_strcmp(key, "2") == 0) {
456
0
                handleValueForPattern(value, two, errorCode);
457
0
            } else if (uprv_strcmp(key, "end") == 0) {
458
0
                handleValueForPattern(value, end, errorCode);
459
0
            } else if (uprv_strcmp(key, "middle") == 0) {
460
0
                handleValueForPattern(value, middle, errorCode);
461
0
            } else if (uprv_strcmp(key, "start") == 0) {
462
0
                handleValueForPattern(value, start, errorCode);
463
0
            }
464
0
        }
465
0
    }
466
};
467
468
// Virtual destructors must be defined out of line.
469
0
ListFormatter::ListPatternsSink::~ListPatternsSink() {}
470
471
ListFormatInternal* ListFormatter::loadListFormatInternal(
472
0
        const Locale& locale, const char * style, UErrorCode& errorCode) {
473
0
    UResourceBundle* rb = ures_open(nullptr, locale.getName(), &errorCode);
474
0
    rb = ures_getByKeyWithFallback(rb, "listPattern", rb, &errorCode);
475
0
    if (U_FAILURE(errorCode)) {
476
0
        ures_close(rb);
477
0
        return nullptr;
478
0
    }
479
0
    ListFormatter::ListPatternsSink sink;
480
0
    char currentStyle[kStyleLenMax+1];
481
0
    uprv_strncpy(currentStyle, style, kStyleLenMax);
482
0
    currentStyle[kStyleLenMax] = 0;
483
484
0
    for (;;) {
485
0
        ures_getAllItemsWithFallback(rb, currentStyle, sink, errorCode);
486
0
        if (U_FAILURE(errorCode) || sink.aliasedStyle[0] == 0 || uprv_strcmp(currentStyle, sink.aliasedStyle) == 0) {
487
0
            break;
488
0
        }
489
0
        uprv_strcpy(currentStyle, sink.aliasedStyle);
490
0
    }
491
0
    ures_close(rb);
492
0
    if (U_FAILURE(errorCode)) {
493
0
        return nullptr;
494
0
    }
495
0
    if (sink.two.isEmpty() || sink.start.isEmpty() || sink.middle.isEmpty() || sink.end.isEmpty()) {
496
0
        errorCode = U_MISSING_RESOURCE_ERROR;
497
0
        return nullptr;
498
0
    }
499
500
0
    ListFormatInternal* result = new ListFormatInternal(sink.two, sink.start, sink.middle, sink.end, locale, errorCode);
501
0
    if (result == nullptr) {
502
0
        errorCode = U_MEMORY_ALLOCATION_ERROR;
503
0
        return nullptr;
504
0
    }
505
0
    if (U_FAILURE(errorCode)) {
506
0
        delete result;
507
0
        return nullptr;
508
0
    }
509
0
    return result;
510
0
}
511
512
0
ListFormatter* ListFormatter::createInstance(UErrorCode& errorCode) {
513
0
    Locale locale;  // The default locale.
514
0
    return createInstance(locale, errorCode);
515
0
}
516
517
0
ListFormatter* ListFormatter::createInstance(const Locale& locale, UErrorCode& errorCode) {
518
0
    return createInstance(locale, ULISTFMT_TYPE_AND, ULISTFMT_WIDTH_WIDE, errorCode);
519
0
}
520
521
ListFormatter* ListFormatter::createInstance(
522
0
        const Locale& locale, UListFormatterType type, UListFormatterWidth width, UErrorCode& errorCode) {
523
0
    const char* style = typeWidthToStyleString(type, width);
524
0
    if (style == nullptr) {
525
0
        errorCode = U_ILLEGAL_ARGUMENT_ERROR;
526
0
        return nullptr;
527
0
    }
528
0
    return createInstance(locale, style, errorCode);
529
0
}
530
531
0
ListFormatter* ListFormatter::createInstance(const Locale& locale, const char *style, UErrorCode& errorCode) {
532
0
    const ListFormatInternal* listFormatInternal = getListFormatInternal(locale, style, errorCode);
533
0
    if (U_FAILURE(errorCode)) {
534
0
        return nullptr;
535
0
    }
536
0
    ListFormatter* p = new ListFormatter(listFormatInternal);
537
0
    if (p == nullptr) {
538
0
        errorCode = U_MEMORY_ALLOCATION_ERROR;
539
0
        return nullptr;
540
0
    }
541
0
    return p;
542
0
}
543
544
0
ListFormatter::ListFormatter(const ListFormatData& listFormatData, UErrorCode &errorCode) {
545
0
    owned = new ListFormatInternal(listFormatData, errorCode);
546
0
    data = owned;
547
0
}
548
549
0
ListFormatter::ListFormatter(const ListFormatInternal* listFormatterInternal) : owned(nullptr), data(listFormatterInternal) {
550
0
}
551
552
0
ListFormatter::~ListFormatter() {
553
0
    delete owned;
554
0
}
555
556
namespace {
557
558
class FormattedListBuilder {
559
public:
560
    LocalPointer<FormattedListData> data;
561
562
    /** For lists of length 1+ */
563
    FormattedListBuilder(const UnicodeString& start, UErrorCode& status)
564
0
            : data(new FormattedListData(status), status) {
565
0
        if (U_SUCCESS(status)) {
566
0
            data->getStringRef().append(
567
0
                start,
568
0
                {UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD},
569
0
                status);
570
0
            data->appendSpanInfo(UFIELD_CATEGORY_LIST_SPAN, 0, -1, start.length(), status);
571
0
        }
572
0
    }
573
574
    /** For lists of length 0 */
575
    FormattedListBuilder(UErrorCode& status)
576
0
            : data(new FormattedListData(status), status) {
577
0
    }
578
579
0
    void append(const SimpleFormatter& pattern, const UnicodeString& next, int32_t position, UErrorCode& status) {
580
0
        if (U_FAILURE(status)) {
581
0
            return;
582
0
        }
583
0
        if (pattern.getArgumentLimit() != 2) {
584
0
            status = U_INTERNAL_PROGRAM_ERROR;
585
0
            return;
586
0
        }
587
        // In the pattern, {0} are the pre-existing elements and {1} is the new element.
588
0
        int32_t offsets[] = {0, 0};
589
0
        UnicodeString temp = pattern.getTextWithNoArguments(offsets, 2);
590
0
        if (offsets[0] <= offsets[1]) {
591
            // prefix{0}infix{1}suffix
592
            // Prepend prefix, then append infix, element, and suffix
593
0
            data->getStringRef().insert(
594
0
                0,
595
0
                temp.tempSubStringBetween(0, offsets[0]),
596
0
                {UFIELD_CATEGORY_LIST, ULISTFMT_LITERAL_FIELD},
597
0
                status);
598
0
            data->getStringRef().append(
599
0
                temp.tempSubStringBetween(offsets[0], offsets[1]),
600
0
                {UFIELD_CATEGORY_LIST, ULISTFMT_LITERAL_FIELD},
601
0
                status);
602
0
            data->getStringRef().append(
603
0
                next,
604
0
                {UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD},
605
0
                status);
606
0
            data->appendSpanInfo(UFIELD_CATEGORY_LIST_SPAN, position, -1, next.length(), status);
607
0
            data->getStringRef().append(
608
0
                temp.tempSubString(offsets[1]),
609
0
                {UFIELD_CATEGORY_LIST, ULISTFMT_LITERAL_FIELD},
610
0
                status);
611
0
        } else {
612
            // prefix{1}infix{0}suffix
613
            // Prepend infix, element, and prefix, then append suffix.
614
            // (We prepend in reverse order because prepending at index 0 is fast.)
615
0
            data->getStringRef().insert(
616
0
                0,
617
0
                temp.tempSubStringBetween(offsets[1], offsets[0]),
618
0
                {UFIELD_CATEGORY_LIST, ULISTFMT_LITERAL_FIELD},
619
0
                status);
620
0
            data->getStringRef().insert(
621
0
                0,
622
0
                next,
623
0
                {UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD},
624
0
                status);
625
0
            data->prependSpanInfo(UFIELD_CATEGORY_LIST_SPAN, position, -1, next.length(), status);
626
0
            data->getStringRef().insert(
627
0
                0,
628
0
                temp.tempSubStringBetween(0, offsets[1]),
629
0
                {UFIELD_CATEGORY_LIST, ULISTFMT_LITERAL_FIELD},
630
0
                status);
631
0
            data->getStringRef().append(
632
0
                temp.tempSubString(offsets[0]),
633
0
                {UFIELD_CATEGORY_LIST, ULISTFMT_LITERAL_FIELD},
634
0
                status);
635
0
        }
636
0
    }
637
};
638
639
}
640
641
UnicodeString& ListFormatter::format(
642
        const UnicodeString items[],
643
        int32_t nItems,
644
        UnicodeString& appendTo,
645
0
        UErrorCode& errorCode) const {
646
0
    int32_t offset;
647
0
    return format(items, nItems, appendTo, -1, offset, errorCode);
648
0
}
649
650
UnicodeString& ListFormatter::format(
651
        const UnicodeString items[],
652
        int32_t nItems,
653
        UnicodeString& appendTo,
654
        int32_t index,
655
        int32_t &offset,
656
0
        UErrorCode& errorCode) const {
657
0
    int32_t initialOffset = appendTo.length();
658
0
    auto result = formatStringsToValue(items, nItems, errorCode);
659
0
    UnicodeStringAppendable appendable(appendTo);
660
0
    result.appendTo(appendable, errorCode);
661
0
    if (index >= 0) {
662
0
        ConstrainedFieldPosition cfpos;
663
0
        cfpos.constrainField(UFIELD_CATEGORY_LIST_SPAN, index);
664
0
        result.nextPosition(cfpos, errorCode);
665
0
        offset = initialOffset + cfpos.getStart();
666
0
    }
667
0
    return appendTo;
668
0
}
669
670
FormattedList ListFormatter::formatStringsToValue(
671
        const UnicodeString items[],
672
        int32_t nItems,
673
0
        UErrorCode& errorCode) const {
674
0
    if (nItems == 0) {
675
0
        FormattedListBuilder result(errorCode);
676
0
        if (U_FAILURE(errorCode)) {
677
0
            return FormattedList(errorCode);
678
0
        } else {
679
0
            return FormattedList(result.data.orphan());
680
0
        }
681
0
    } else if (nItems == 1) {
682
0
        FormattedListBuilder result(items[0], errorCode);
683
0
        result.data->getStringRef().writeTerminator(errorCode);
684
0
        if (U_FAILURE(errorCode)) {
685
0
            return FormattedList(errorCode);
686
0
        } else {
687
0
            return FormattedList(result.data.orphan());
688
0
        }
689
0
    } else if (nItems == 2) {
690
0
        FormattedListBuilder result(items[0], errorCode);
691
0
        if (U_FAILURE(errorCode)) {
692
0
            return FormattedList(errorCode);
693
0
        }
694
0
        result.append(
695
0
            data->patternHandler->getTwoPattern(items[1]),
696
0
            items[1],
697
0
            1,
698
0
            errorCode);
699
0
        result.data->getStringRef().writeTerminator(errorCode);
700
0
        if (U_FAILURE(errorCode)) {
701
0
            return FormattedList(errorCode);
702
0
        } else {
703
0
            return FormattedList(result.data.orphan());
704
0
        }
705
0
    }
706
707
0
    FormattedListBuilder result(items[0], errorCode);
708
0
    if (U_FAILURE(errorCode)) {
709
0
        return FormattedList(errorCode);
710
0
    }
711
0
    result.append(
712
0
        data->startPattern,
713
0
        items[1],
714
0
        1,
715
0
        errorCode);
716
0
    for (int32_t i = 2; i < nItems - 1; i++) {
717
0
        result.append(
718
0
            data->middlePattern,
719
0
            items[i],
720
0
            i,
721
0
            errorCode);
722
0
    }
723
0
    result.append(
724
0
        data->patternHandler->getEndPattern(items[nItems-1]),
725
0
        items[nItems-1],
726
0
        nItems-1,
727
0
        errorCode);
728
0
    result.data->getStringRef().writeTerminator(errorCode);
729
0
    if (U_FAILURE(errorCode)) {
730
0
        return FormattedList(errorCode);
731
0
    } else {
732
0
        return FormattedList(result.data.orphan());
733
0
    }
734
0
}
735
736
737
U_NAMESPACE_END
738
739
#endif /* #if !UCONFIG_NO_FORMATTING */