• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 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];
ListPatternsSinkListFormatter::ListPatternsSink410     ListPatternsSink() {
411       uprv_memset(aliasedStyle, 0, kStyleLenMax+1);
412     }
413 #else
414     char aliasedStyle[kStyleLenMax+1] = {0};
415 
ListPatternsSinkListFormatter::ListPatternsSink416     ListPatternsSink() {}
417 #endif
418     virtual ~ListPatternsSink();
419 
setAliasedStyleListFormatter::ListPatternsSink420     void setAliasedStyle(UnicodeString alias) {
421         int32_t startIndex = alias.indexOf(aliasPrefix, kAliasPrefixLen, 0);
422         if (startIndex < 0) {
423             return;
424         }
425         startIndex += kAliasPrefixLen;
426         int32_t endIndex = alias.indexOf(solidus, startIndex);
427         if (endIndex < 0) {
428             endIndex = alias.length();
429         }
430         alias.extract(startIndex, endIndex-startIndex, aliasedStyle, kStyleLenMax+1, US_INV);
431         aliasedStyle[kStyleLenMax] = 0;
432     }
433 
handleValueForPatternListFormatter::ListPatternsSink434     void handleValueForPattern(ResourceValue &value, UnicodeString &pattern, UErrorCode &errorCode) {
435         if (pattern.isEmpty()) {
436             if (value.getType() == URES_ALIAS) {
437                 if (aliasedStyle[0] == 0) {
438                     setAliasedStyle(value.getAliasUnicodeString(errorCode));
439                 }
440             } else {
441                 pattern = value.getUnicodeString(errorCode);
442             }
443         }
444     }
445 
putListFormatter::ListPatternsSink446     virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/,
447             UErrorCode &errorCode) override {
448         aliasedStyle[0] = 0;
449         if (value.getType() == URES_ALIAS) {
450             setAliasedStyle(value.getAliasUnicodeString(errorCode));
451             return;
452         }
453         ResourceTable listPatterns = value.getTable(errorCode);
454         for (int i = 0; U_SUCCESS(errorCode) && listPatterns.getKeyAndValue(i, key, value); ++i) {
455             if (uprv_strcmp(key, "2") == 0) {
456                 handleValueForPattern(value, two, errorCode);
457             } else if (uprv_strcmp(key, "end") == 0) {
458                 handleValueForPattern(value, end, errorCode);
459             } else if (uprv_strcmp(key, "middle") == 0) {
460                 handleValueForPattern(value, middle, errorCode);
461             } else if (uprv_strcmp(key, "start") == 0) {
462                 handleValueForPattern(value, start, errorCode);
463             }
464         }
465     }
466 };
467 
468 // Virtual destructors must be defined out of line.
~ListPatternsSink()469 ListFormatter::ListPatternsSink::~ListPatternsSink() {}
470 
loadListFormatInternal(const Locale & locale,const char * style,UErrorCode & errorCode)471 ListFormatInternal* ListFormatter::loadListFormatInternal(
472         const Locale& locale, const char * style, UErrorCode& errorCode) {
473     UResourceBundle* rb = ures_open(nullptr, locale.getName(), &errorCode);
474     rb = ures_getByKeyWithFallback(rb, "listPattern", rb, &errorCode);
475     if (U_FAILURE(errorCode)) {
476         ures_close(rb);
477         return nullptr;
478     }
479     ListFormatter::ListPatternsSink sink;
480     char currentStyle[kStyleLenMax+1];
481     uprv_strncpy(currentStyle, style, kStyleLenMax);
482     currentStyle[kStyleLenMax] = 0;
483 
484     for (;;) {
485         ures_getAllItemsWithFallback(rb, currentStyle, sink, errorCode);
486         if (U_FAILURE(errorCode) || sink.aliasedStyle[0] == 0 || uprv_strcmp(currentStyle, sink.aliasedStyle) == 0) {
487             break;
488         }
489         uprv_strcpy(currentStyle, sink.aliasedStyle);
490     }
491     ures_close(rb);
492     if (U_FAILURE(errorCode)) {
493         return nullptr;
494     }
495     if (sink.two.isEmpty() || sink.start.isEmpty() || sink.middle.isEmpty() || sink.end.isEmpty()) {
496         errorCode = U_MISSING_RESOURCE_ERROR;
497         return nullptr;
498     }
499 
500     ListFormatInternal* result = new ListFormatInternal(sink.two, sink.start, sink.middle, sink.end, locale, errorCode);
501     if (result == nullptr) {
502         errorCode = U_MEMORY_ALLOCATION_ERROR;
503         return nullptr;
504     }
505     if (U_FAILURE(errorCode)) {
506         delete result;
507         return nullptr;
508     }
509     return result;
510 }
511 
createInstance(UErrorCode & errorCode)512 ListFormatter* ListFormatter::createInstance(UErrorCode& errorCode) {
513     Locale locale;  // The default locale.
514     return createInstance(locale, errorCode);
515 }
516 
createInstance(const Locale & locale,UErrorCode & errorCode)517 ListFormatter* ListFormatter::createInstance(const Locale& locale, UErrorCode& errorCode) {
518     return createInstance(locale, ULISTFMT_TYPE_AND, ULISTFMT_WIDTH_WIDE, errorCode);
519 }
520 
createInstance(const Locale & locale,UListFormatterType type,UListFormatterWidth width,UErrorCode & errorCode)521 ListFormatter* ListFormatter::createInstance(
522         const Locale& locale, UListFormatterType type, UListFormatterWidth width, UErrorCode& errorCode) {
523     const char* style = typeWidthToStyleString(type, width);
524     if (style == nullptr) {
525         errorCode = U_ILLEGAL_ARGUMENT_ERROR;
526         return nullptr;
527     }
528     return createInstance(locale, style, errorCode);
529 }
530 
createInstance(const Locale & locale,const char * style,UErrorCode & errorCode)531 ListFormatter* ListFormatter::createInstance(const Locale& locale, const char *style, UErrorCode& errorCode) {
532     const ListFormatInternal* listFormatInternal = getListFormatInternal(locale, style, errorCode);
533     if (U_FAILURE(errorCode)) {
534         return nullptr;
535     }
536     ListFormatter* p = new ListFormatter(listFormatInternal);
537     if (p == nullptr) {
538         errorCode = U_MEMORY_ALLOCATION_ERROR;
539         return nullptr;
540     }
541     return p;
542 }
543 
ListFormatter(const ListFormatData & listFormatData,UErrorCode & errorCode)544 ListFormatter::ListFormatter(const ListFormatData& listFormatData, UErrorCode &errorCode) {
545     owned = new ListFormatInternal(listFormatData, errorCode);
546     data = owned;
547 }
548 
ListFormatter(const ListFormatInternal * listFormatterInternal)549 ListFormatter::ListFormatter(const ListFormatInternal* listFormatterInternal) : owned(nullptr), data(listFormatterInternal) {
550 }
551 
~ListFormatter()552 ListFormatter::~ListFormatter() {
553     delete owned;
554 }
555 
556 namespace {
557 
558 class FormattedListBuilder {
559 public:
560     LocalPointer<FormattedListData> data;
561 
562     /** For lists of length 1+ */
FormattedListBuilder(const UnicodeString & start,UErrorCode & status)563     FormattedListBuilder(const UnicodeString& start, UErrorCode& status)
564             : data(new FormattedListData(status), status) {
565         if (U_SUCCESS(status)) {
566             data->getStringRef().append(
567                 start,
568                 {UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD},
569                 status);
570             data->appendSpanInfo(UFIELD_CATEGORY_LIST_SPAN, 0, -1, start.length(), status);
571         }
572     }
573 
574     /** For lists of length 0 */
FormattedListBuilder(UErrorCode & status)575     FormattedListBuilder(UErrorCode& status)
576             : data(new FormattedListData(status), status) {
577     }
578 
append(const SimpleFormatter & pattern,const UnicodeString & next,int32_t position,UErrorCode & status)579     void append(const SimpleFormatter& pattern, const UnicodeString& next, int32_t position, UErrorCode& status) {
580         if (U_FAILURE(status)) {
581             return;
582         }
583         if (pattern.getArgumentLimit() != 2) {
584             status = U_INTERNAL_PROGRAM_ERROR;
585             return;
586         }
587         // In the pattern, {0} are the pre-existing elements and {1} is the new element.
588         int32_t offsets[] = {0, 0};
589         UnicodeString temp = pattern.getTextWithNoArguments(offsets, 2);
590         if (offsets[0] <= offsets[1]) {
591             // prefix{0}infix{1}suffix
592             // Prepend prefix, then append infix, element, and suffix
593             data->getStringRef().insert(
594                 0,
595                 temp.tempSubStringBetween(0, offsets[0]),
596                 {UFIELD_CATEGORY_LIST, ULISTFMT_LITERAL_FIELD},
597                 status);
598             data->getStringRef().append(
599                 temp.tempSubStringBetween(offsets[0], offsets[1]),
600                 {UFIELD_CATEGORY_LIST, ULISTFMT_LITERAL_FIELD},
601                 status);
602             data->getStringRef().append(
603                 next,
604                 {UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD},
605                 status);
606             data->appendSpanInfo(UFIELD_CATEGORY_LIST_SPAN, position, -1, next.length(), status);
607             data->getStringRef().append(
608                 temp.tempSubString(offsets[1]),
609                 {UFIELD_CATEGORY_LIST, ULISTFMT_LITERAL_FIELD},
610                 status);
611         } 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             data->getStringRef().insert(
616                 0,
617                 temp.tempSubStringBetween(offsets[1], offsets[0]),
618                 {UFIELD_CATEGORY_LIST, ULISTFMT_LITERAL_FIELD},
619                 status);
620             data->getStringRef().insert(
621                 0,
622                 next,
623                 {UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD},
624                 status);
625             data->prependSpanInfo(UFIELD_CATEGORY_LIST_SPAN, position, -1, next.length(), status);
626             data->getStringRef().insert(
627                 0,
628                 temp.tempSubStringBetween(0, offsets[1]),
629                 {UFIELD_CATEGORY_LIST, ULISTFMT_LITERAL_FIELD},
630                 status);
631             data->getStringRef().append(
632                 temp.tempSubString(offsets[0]),
633                 {UFIELD_CATEGORY_LIST, ULISTFMT_LITERAL_FIELD},
634                 status);
635         }
636     }
637 };
638 
639 }
640 
format(const UnicodeString items[],int32_t nItems,UnicodeString & appendTo,UErrorCode & errorCode) const641 UnicodeString& ListFormatter::format(
642         const UnicodeString items[],
643         int32_t nItems,
644         UnicodeString& appendTo,
645         UErrorCode& errorCode) const {
646     int32_t offset;
647     return format(items, nItems, appendTo, -1, offset, errorCode);
648 }
649 
format(const UnicodeString items[],int32_t nItems,UnicodeString & appendTo,int32_t index,int32_t & offset,UErrorCode & errorCode) const650 UnicodeString& ListFormatter::format(
651         const UnicodeString items[],
652         int32_t nItems,
653         UnicodeString& appendTo,
654         int32_t index,
655         int32_t &offset,
656         UErrorCode& errorCode) const {
657     int32_t initialOffset = appendTo.length();
658     auto result = formatStringsToValue(items, nItems, errorCode);
659     UnicodeStringAppendable appendable(appendTo);
660     result.appendTo(appendable, errorCode);
661     if (index >= 0) {
662         ConstrainedFieldPosition cfpos;
663         cfpos.constrainField(UFIELD_CATEGORY_LIST_SPAN, index);
664         result.nextPosition(cfpos, errorCode);
665         offset = initialOffset + cfpos.getStart();
666     }
667     return appendTo;
668 }
669 
formatStringsToValue(const UnicodeString items[],int32_t nItems,UErrorCode & errorCode) const670 FormattedList ListFormatter::formatStringsToValue(
671         const UnicodeString items[],
672         int32_t nItems,
673         UErrorCode& errorCode) const {
674     if (nItems == 0) {
675         FormattedListBuilder result(errorCode);
676         if (U_FAILURE(errorCode)) {
677             return FormattedList(errorCode);
678         } else {
679             return FormattedList(result.data.orphan());
680         }
681     } else if (nItems == 1) {
682         FormattedListBuilder result(items[0], errorCode);
683         result.data->getStringRef().writeTerminator(errorCode);
684         if (U_FAILURE(errorCode)) {
685             return FormattedList(errorCode);
686         } else {
687             return FormattedList(result.data.orphan());
688         }
689     } else if (nItems == 2) {
690         FormattedListBuilder result(items[0], errorCode);
691         if (U_FAILURE(errorCode)) {
692             return FormattedList(errorCode);
693         }
694         result.append(
695             data->patternHandler->getTwoPattern(items[1]),
696             items[1],
697             1,
698             errorCode);
699         result.data->getStringRef().writeTerminator(errorCode);
700         if (U_FAILURE(errorCode)) {
701             return FormattedList(errorCode);
702         } else {
703             return FormattedList(result.data.orphan());
704         }
705     }
706 
707     FormattedListBuilder result(items[0], errorCode);
708     if (U_FAILURE(errorCode)) {
709         return FormattedList(errorCode);
710     }
711     result.append(
712         data->startPattern,
713         items[1],
714         1,
715         errorCode);
716     for (int32_t i = 2; i < nItems - 1; i++) {
717         result.append(
718             data->middlePattern,
719             items[i],
720             i,
721             errorCode);
722     }
723     result.append(
724         data->patternHandler->getEndPattern(items[nItems-1]),
725         items[nItems-1],
726         nItems-1,
727         errorCode);
728     result.data->getStringRef().writeTerminator(errorCode);
729     if (U_FAILURE(errorCode)) {
730         return FormattedList(errorCode);
731     } else {
732         return FormattedList(result.data.orphan());
733     }
734 }
735 
736 
737 U_NAMESPACE_END
738 
739 #endif /* #if !UCONFIG_NO_FORMATTING */
740