listformatter.cpp (24359B)
1 // © 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html 3 /* 4 ******************************************************************************* 5 * 6 * Copyright (C) 2013-2016, International Business Machines 7 * Corporation and others. All Rights Reserved. 8 * 9 ******************************************************************************* 10 * file name: listformatter.cpp 11 * encoding: UTF-8 12 * tab size: 8 (not used) 13 * indentation:4 14 * 15 * created on: 2012aug27 16 * created by: Umesh P. Nair 17 */ 18 19 #include "unicode/utypes.h" 20 21 #if !UCONFIG_NO_FORMATTING 22 23 #include "cmemory.h" 24 #include "unicode/fpositer.h" // FieldPositionIterator 25 #include "unicode/listformatter.h" 26 #include "unicode/simpleformatter.h" 27 #include "unicode/ulistformatter.h" 28 #include "unicode/uscript.h" 29 #include "fphdlimp.h" 30 #include "mutex.h" 31 #include "hash.h" 32 #include "cstring.h" 33 #include "uarrsort.h" 34 #include "ulocimp.h" 35 #include "charstr.h" 36 #include "ucln_in.h" 37 #include "uresimp.h" 38 #include "resource.h" 39 #include "formattedval_impl.h" 40 41 U_NAMESPACE_BEGIN 42 43 namespace { 44 45 class PatternHandler : public UObject { 46 public: 47 PatternHandler(const UnicodeString& two, const UnicodeString& end, UErrorCode& errorCode) : 48 twoPattern(two, 2, 2, errorCode), 49 endPattern(end, 2, 2, errorCode) { } 50 51 PatternHandler(const SimpleFormatter& two, const SimpleFormatter& end) : 52 twoPattern(two), 53 endPattern(end) { } 54 55 virtual ~PatternHandler(); 56 57 virtual PatternHandler* clone() const { return new PatternHandler(twoPattern, endPattern); } 58 59 /** Argument: final string in the list. */ 60 virtual const SimpleFormatter& getTwoPattern(const UnicodeString&) const { 61 return twoPattern; 62 } 63 64 /** Argument: final string in the list. */ 65 virtual const SimpleFormatter& getEndPattern(const UnicodeString&) const { 66 return endPattern; 67 } 68 69 protected: 70 SimpleFormatter twoPattern; 71 SimpleFormatter endPattern; 72 }; 73 74 PatternHandler::~PatternHandler() { 75 } 76 77 class ContextualHandler : public PatternHandler { 78 public: 79 ContextualHandler(bool (*testFunc)(const UnicodeString& text), 80 const UnicodeString& thenTwo, 81 const UnicodeString& elseTwo, 82 const UnicodeString& thenEnd, 83 const UnicodeString& elseEnd, 84 UErrorCode& errorCode) : 85 PatternHandler(elseTwo, elseEnd, errorCode), 86 test(testFunc), 87 thenTwoPattern(thenTwo, 2, 2, errorCode), 88 thenEndPattern(thenEnd, 2, 2, errorCode) { } 89 90 ContextualHandler(bool (*testFunc)(const UnicodeString& text), 91 const SimpleFormatter& thenTwo, SimpleFormatter elseTwo, 92 const SimpleFormatter& thenEnd, SimpleFormatter elseEnd) : 93 PatternHandler(elseTwo, elseEnd), 94 test(testFunc), 95 thenTwoPattern(thenTwo), 96 thenEndPattern(thenEnd) { } 97 98 ~ContextualHandler() override; 99 100 PatternHandler* clone() const override { 101 return new ContextualHandler( 102 test, thenTwoPattern, twoPattern, thenEndPattern, endPattern); 103 } 104 105 const SimpleFormatter& getTwoPattern( 106 const UnicodeString& text) const override { 107 return (test)(text) ? thenTwoPattern : twoPattern; 108 } 109 110 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 121 ContextualHandler::~ContextualHandler() { 122 } 123 124 const char16_t* spanishY = u"{0} y {1}"; 125 const char16_t* spanishE = u"{0} e {1}"; 126 const char16_t* spanishO = u"{0} o {1}"; 127 const char16_t* spanishU = u"{0} u {1}"; 128 const char16_t* hebrewVav = u"{0} \u05D5{1}"; 129 const char16_t* hebrewVavDash = u"{0} \u05D5-{1}"; 130 131 // Condiction to change to e. 132 // Starts with "hi" or "i" but not with "hie" nor "hia" 133 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)$ 150 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. 167 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 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 221 ListFormatInternal( 222 const UnicodeString& two, 223 const UnicodeString& start, 224 const UnicodeString& middle, 225 const UnicodeString& end, 226 const Locale& locale, 227 UErrorCode &errorCode) : 228 startPattern(start, 2, 2, errorCode), 229 middlePattern(middle, 2, 2, errorCode), 230 patternHandler(createPatternHandler(locale.getLanguage(), two, end, errorCode), errorCode) { } 231 232 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 238 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: 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 259 static UBool U_CALLCONV uprv_listformatter_cleanup() { 260 delete listPatternHash; 261 listPatternHash = nullptr; 262 return true; 263 } 264 265 static void U_CALLCONV 266 uprv_deleteListFormatInternal(void *obj) { 267 delete static_cast<ListFormatInternal *>(obj); 268 } 269 270 U_CDECL_END 271 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 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 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 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 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 410 ListPatternsSink() {} 411 virtual ~ListPatternsSink(); 412 413 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 427 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 439 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. 462 ListFormatter::ListPatternsSink::~ListPatternsSink() {} 463 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 505 ListFormatter* ListFormatter::createInstance(UErrorCode& errorCode) { 506 Locale locale; // The default locale. 507 return createInstance(locale, errorCode); 508 } 509 510 ListFormatter* ListFormatter::createInstance(const Locale& locale, UErrorCode& errorCode) { 511 return createInstance(locale, ULISTFMT_TYPE_AND, ULISTFMT_WIDTH_WIDE, errorCode); 512 } 513 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 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 537 ListFormatter::ListFormatter(const ListFormatData& listFormatData, UErrorCode &errorCode) { 538 owned = new ListFormatInternal(listFormatData, errorCode); 539 data = owned; 540 } 541 542 ListFormatter::ListFormatter(const ListFormatInternal* listFormatterInternal) : owned(nullptr), data(listFormatterInternal) { 543 } 544 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+ */ 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 */ 568 FormattedListBuilder(UErrorCode& status) 569 : data(new FormattedListData(status), status) { 570 } 571 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 634 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 643 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 663 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 */