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