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