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) {
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(0, 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(position, 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(position, 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