"Fossies" - the Fresh Open Source Software Archive

Member "icu/source/i18n/listformatter.cpp" (22 Apr 2020, 26578 Bytes) of package /linux/misc/icu4c-67_1-src.tgz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "listformatter.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes reports: 67rc_vs_67_1 or 66_1_vs_67_1.

    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 "cmemory.h"
   20 #include "unicode/fpositer.h"  // FieldPositionIterator
   21 #include "unicode/listformatter.h"
   22 #include "unicode/simpleformatter.h"
   23 #include "unicode/ulistformatter.h"
   24 #include "unicode/uscript.h"
   25 #include "fphdlimp.h"
   26 #include "mutex.h"
   27 #include "hash.h"
   28 #include "cstring.h"
   29 #include "uarrsort.h"
   30 #include "ulocimp.h"
   31 #include "charstr.h"
   32 #include "ucln_in.h"
   33 #include "uresimp.h"
   34 #include "resource.h"
   35 #include "formattedval_impl.h"
   36 
   37 U_NAMESPACE_BEGIN
   38 
   39 namespace {
   40 
   41 class PatternHandler : public UObject {
   42 public:
   43     PatternHandler(const UnicodeString& two, const UnicodeString& end, UErrorCode& errorCode) :
   44         twoPattern(two, 2, 2, errorCode),
   45         endPattern(end, 2, 2, errorCode) {  }
   46 
   47     PatternHandler(const SimpleFormatter& two, const SimpleFormatter& end) :
   48         twoPattern(two),
   49         endPattern(end) { }
   50 
   51     virtual ~PatternHandler();
   52 
   53     virtual PatternHandler* clone() const { return new PatternHandler(twoPattern, endPattern); }
   54 
   55     virtual const SimpleFormatter& getTwoPattern(const UnicodeString&) const {
   56         return twoPattern;
   57     }
   58 
   59     virtual const SimpleFormatter& getEndPattern(const UnicodeString&) const {
   60         return endPattern;
   61     }
   62 
   63 protected:
   64     SimpleFormatter twoPattern;
   65     SimpleFormatter endPattern;
   66 };
   67 
   68 PatternHandler::~PatternHandler() {
   69 }
   70 
   71 class ContextualHandler : public PatternHandler {
   72 public:
   73     ContextualHandler(bool (*testFunc)(const UnicodeString& text),
   74                       const UnicodeString& thenTwo,
   75                       const UnicodeString& elseTwo,
   76                       const UnicodeString& thenEnd,
   77                       const UnicodeString& elseEnd,
   78                       UErrorCode& errorCode) :
   79         PatternHandler(elseTwo, elseEnd, errorCode),
   80         test(testFunc),
   81         thenTwoPattern(thenTwo, 2, 2, errorCode),
   82         thenEndPattern(thenEnd, 2, 2, errorCode) {  }
   83 
   84     ContextualHandler(bool (*testFunc)(const UnicodeString& text),
   85                       const SimpleFormatter& thenTwo, SimpleFormatter elseTwo,
   86                       const SimpleFormatter& thenEnd, SimpleFormatter elseEnd) :
   87       PatternHandler(elseTwo, elseEnd),
   88       test(testFunc),
   89       thenTwoPattern(thenTwo),
   90       thenEndPattern(thenEnd) { }
   91 
   92     ~ContextualHandler() override;
   93 
   94     PatternHandler* clone() const override {
   95         return new ContextualHandler(
   96             test, thenTwoPattern, twoPattern, thenEndPattern, endPattern);
   97     }
   98 
   99     const SimpleFormatter& getTwoPattern(
  100         const UnicodeString& text) const override {
  101         return (test)(text) ? thenTwoPattern : twoPattern;
  102     }
  103 
  104     const SimpleFormatter& getEndPattern(
  105         const UnicodeString& text) const override {
  106         return (test)(text) ? thenEndPattern : endPattern;
  107     }
  108 
  109 private:
  110     bool (*test)(const UnicodeString&);
  111     SimpleFormatter thenTwoPattern;
  112     SimpleFormatter thenEndPattern;
  113 };
  114 
  115 ContextualHandler::~ContextualHandler() {
  116 }
  117 
  118 static const char16_t *spanishY = u"{0} y {1}";
  119 static const char16_t *spanishE = u"{0} e {1}";
  120 static const char16_t *spanishO = u"{0} o {1}";
  121 static const char16_t *spanishU = u"{0} u {1}";
  122 static const char16_t *hebrewVav = u"{0} \u05D5{1}";
  123 static const char16_t *hebrewVavDash = u"{0} \u05D5-{1}";
  124 
  125 // Condiction to change to e.
  126 // Starts with "hi" or "i" but not with "hie" nor "hia"
  127 static bool shouldChangeToE(const UnicodeString& text) {
  128     int32_t len = text.length();
  129     if (len == 0) { return false; }
  130     // Case insensitive match hi but not hie nor hia.
  131     if ((text[0] == u'h' || text[0] == u'H') &&
  132             ((len > 1) && (text[1] == u'i' || text[1] == u'I')) &&
  133             ((len == 2) || !(text[2] == u'a' || text[2] == u'A' || text[2] == u'e' || text[2] == u'E'))) {
  134         return true;
  135     }
  136     // Case insensitive for "start with i"
  137     if (text[0] == u'i' || text[0] == u'I') { return true; }
  138     return false;
  139 }
  140 
  141 // Condiction to change to u.
  142 // Starts with "o", "ho", and "8". Also "11" by itself.
  143 // re: ^((o|ho|8).*|11)$
  144 static bool shouldChangeToU(const UnicodeString& text) {
  145     int32_t len = text.length();
  146     if (len == 0) { return false; }
  147     // Case insensitive match o.* and 8.*
  148     if (text[0] == u'o' || text[0] == u'O' || text[0] == u'8') { return true; }
  149     // Case insensitive match ho.*
  150     if ((text[0] == u'h' || text[0] == u'H') &&
  151             ((len > 1) && (text[1] == 'o' || text[1] == u'O'))) {
  152         return true;
  153     }
  154     // match "^11$" and "^11 .*"
  155     if ((len >= 2) && text[0] == u'1' && text[1] == u'1' && (len == 2 || text[2] == u' ')) { return true; }
  156     return false;
  157 }
  158 
  159 // Condiction to change to VAV follow by a dash.
  160 // Starts with non Hebrew letter.
  161 static bool shouldChangeToVavDash(const UnicodeString& text) {
  162     if (text.isEmpty()) { return false; }
  163     UErrorCode status = U_ZERO_ERROR;
  164     return uscript_getScript(text.char32At(0), &status) != USCRIPT_HEBREW;
  165 }
  166 
  167 PatternHandler* createPatternHandler(
  168         const char* lang, const UnicodeString& two, const UnicodeString& end,
  169     UErrorCode& status) {
  170     if (uprv_strcmp(lang, "es") == 0) {
  171         // Spanish
  172         UnicodeString spanishYStr(TRUE, spanishY, -1);
  173         bool twoIsY = two == spanishYStr;
  174         bool endIsY = end == spanishYStr;
  175         if (twoIsY || endIsY) {
  176             UnicodeString replacement(TRUE, spanishE, -1);
  177             return new ContextualHandler(
  178                 shouldChangeToE,
  179                 twoIsY ? replacement : two, two,
  180                 endIsY ? replacement : end, end, status);
  181         }
  182         UnicodeString spanishOStr(TRUE, spanishO, -1);
  183         bool twoIsO = two == spanishOStr;
  184         bool endIsO = end == spanishOStr;
  185         if (twoIsO || endIsO) {
  186             UnicodeString replacement(TRUE, spanishU, -1);
  187             return new ContextualHandler(
  188                 shouldChangeToU,
  189                 twoIsO ? replacement : two, two,
  190                 endIsO ? replacement : end, end, status);
  191         }
  192     } else if (uprv_strcmp(lang, "he") == 0 || uprv_strcmp(lang, "iw") == 0) {
  193         // Hebrew
  194         UnicodeString hebrewVavStr(TRUE, hebrewVav, -1);
  195         bool twoIsVav = two == hebrewVavStr;
  196         bool endIsVav = end == hebrewVavStr;
  197         if (twoIsVav || endIsVav) {
  198             UnicodeString replacement(TRUE, hebrewVavDash, -1);
  199             return new ContextualHandler(
  200                 shouldChangeToVavDash,
  201                 twoIsVav ? replacement : two, two,
  202                 endIsVav ? replacement : end, end, status);
  203         }
  204     }
  205     return new PatternHandler(two, end, status);
  206 }
  207 
  208 }  // namespace
  209 
  210 struct ListFormatInternal : public UMemory {
  211     SimpleFormatter startPattern;
  212     SimpleFormatter middlePattern;
  213     LocalPointer<PatternHandler> patternHandler;
  214 
  215 ListFormatInternal(
  216         const UnicodeString& two,
  217         const UnicodeString& start,
  218         const UnicodeString& middle,
  219         const UnicodeString& end,
  220         const Locale& locale,
  221         UErrorCode &errorCode) :
  222         startPattern(start, 2, 2, errorCode),
  223         middlePattern(middle, 2, 2, errorCode),
  224         patternHandler(createPatternHandler(locale.getLanguage(), two, end, errorCode), errorCode) { }
  225 
  226 ListFormatInternal(const ListFormatData &data, UErrorCode &errorCode) :
  227         startPattern(data.startPattern, errorCode),
  228         middlePattern(data.middlePattern, errorCode),
  229         patternHandler(createPatternHandler(
  230             data.locale.getLanguage(), data.twoPattern, data.endPattern, errorCode), errorCode) { }
  231 
  232 ListFormatInternal(const ListFormatInternal &other) :
  233     startPattern(other.startPattern),
  234     middlePattern(other.middlePattern),
  235     patternHandler(other.patternHandler->clone()) { }
  236 };
  237 
  238 
  239 #if !UCONFIG_NO_FORMATTING
  240 class FormattedListData : public FormattedValueFieldPositionIteratorImpl {
  241 public:
  242     FormattedListData(UErrorCode& status) : FormattedValueFieldPositionIteratorImpl(5, status) {}
  243     virtual ~FormattedListData();
  244 };
  245 
  246 FormattedListData::~FormattedListData() = default;
  247 
  248 UPRV_FORMATTED_VALUE_SUBCLASS_AUTO_IMPL(FormattedList)
  249 #endif
  250 
  251 
  252 static Hashtable* listPatternHash = nullptr;
  253 
  254 U_CDECL_BEGIN
  255 static UBool U_CALLCONV uprv_listformatter_cleanup() {
  256     delete listPatternHash;
  257     listPatternHash = nullptr;
  258     return TRUE;
  259 }
  260 
  261 static void U_CALLCONV
  262 uprv_deleteListFormatInternal(void *obj) {
  263     delete static_cast<ListFormatInternal *>(obj);
  264 }
  265 
  266 U_CDECL_END
  267 
  268 ListFormatter::ListFormatter(const ListFormatter& other) :
  269         owned(other.owned), data(other.data) {
  270     if (other.owned != nullptr) {
  271         owned = new ListFormatInternal(*other.owned);
  272         data = owned;
  273     }
  274 }
  275 
  276 ListFormatter& ListFormatter::operator=(const ListFormatter& other) {
  277     if (this == &other) {
  278         return *this;
  279     }
  280     delete owned;
  281     if (other.owned) {
  282         owned = new ListFormatInternal(*other.owned);
  283         data = owned;
  284     } else {
  285         owned = nullptr;
  286         data = other.data;
  287     }
  288     return *this;
  289 }
  290 
  291 void ListFormatter::initializeHash(UErrorCode& errorCode) {
  292     if (U_FAILURE(errorCode)) {
  293         return;
  294     }
  295 
  296     listPatternHash = new Hashtable();
  297     if (listPatternHash == nullptr) {
  298         errorCode = U_MEMORY_ALLOCATION_ERROR;
  299         return;
  300     }
  301 
  302     listPatternHash->setValueDeleter(uprv_deleteListFormatInternal);
  303     ucln_i18n_registerCleanup(UCLN_I18N_LIST_FORMATTER, uprv_listformatter_cleanup);
  304 
  305 }
  306 
  307 const ListFormatInternal* ListFormatter::getListFormatInternal(
  308         const Locale& locale, const char *style, UErrorCode& errorCode) {
  309     if (U_FAILURE(errorCode)) {
  310         return nullptr;
  311     }
  312     CharString keyBuffer(locale.getName(), errorCode);
  313     keyBuffer.append(':', errorCode).append(style, errorCode);
  314     UnicodeString key(keyBuffer.data(), -1, US_INV);
  315     ListFormatInternal* result = nullptr;
  316     static UMutex listFormatterMutex;
  317     {
  318         Mutex m(&listFormatterMutex);
  319         if (listPatternHash == nullptr) {
  320             initializeHash(errorCode);
  321             if (U_FAILURE(errorCode)) {
  322                 return nullptr;
  323             }
  324         }
  325         result = static_cast<ListFormatInternal*>(listPatternHash->get(key));
  326     }
  327     if (result != nullptr) {
  328         return result;
  329     }
  330     result = loadListFormatInternal(locale, style, errorCode);
  331     if (U_FAILURE(errorCode)) {
  332         return nullptr;
  333     }
  334 
  335     {
  336         Mutex m(&listFormatterMutex);
  337         ListFormatInternal* temp = static_cast<ListFormatInternal*>(listPatternHash->get(key));
  338         if (temp != nullptr) {
  339             delete result;
  340             result = temp;
  341         } else {
  342             listPatternHash->put(key, result, errorCode);
  343             if (U_FAILURE(errorCode)) {
  344                 return nullptr;
  345             }
  346         }
  347     }
  348     return result;
  349 }
  350 
  351 #if !UCONFIG_NO_FORMATTING
  352 static const char* typeWidthToStyleString(UListFormatterType type, UListFormatterWidth width) {
  353     switch (type) {
  354         case ULISTFMT_TYPE_AND:
  355             switch (width) {
  356                 case ULISTFMT_WIDTH_WIDE:
  357                     return "standard";
  358                 case ULISTFMT_WIDTH_SHORT:
  359                     return "standard-short";
  360                 case ULISTFMT_WIDTH_NARROW:
  361                     return "standard-narrow";
  362                 default:
  363                     return nullptr;
  364             }
  365             break;
  366 
  367         case ULISTFMT_TYPE_OR:
  368             switch (width) {
  369                 case ULISTFMT_WIDTH_WIDE:
  370                     return "or";
  371                 case ULISTFMT_WIDTH_SHORT:
  372                     return "or-short";
  373                 case ULISTFMT_WIDTH_NARROW:
  374                     return "or-narrow";
  375                 default:
  376                     return nullptr;
  377             }
  378             break;
  379 
  380         case ULISTFMT_TYPE_UNITS:
  381             switch (width) {
  382                 case ULISTFMT_WIDTH_WIDE:
  383                     return "unit";
  384                 case ULISTFMT_WIDTH_SHORT:
  385                     return "unit-short";
  386                 case ULISTFMT_WIDTH_NARROW:
  387                     return "unit-narrow";
  388                 default:
  389                     return nullptr;
  390             }
  391     }
  392 
  393     return nullptr;
  394 }
  395 #endif
  396 
  397 static const UChar solidus = 0x2F;
  398 static const UChar aliasPrefix[] = { 0x6C,0x69,0x73,0x74,0x50,0x61,0x74,0x74,0x65,0x72,0x6E,0x2F }; // "listPattern/"
  399 enum {
  400     kAliasPrefixLen = UPRV_LENGTHOF(aliasPrefix),
  401     kStyleLenMax = 24 // longest currently is 14
  402 };
  403 
  404 struct ListFormatter::ListPatternsSink : public ResourceSink {
  405     UnicodeString two, start, middle, end;
  406 #if ((U_PLATFORM == U_PF_AIX) || (U_PLATFORM == U_PF_OS390)) && (U_CPLUSPLUS_VERSION < 11)
  407     char aliasedStyle[kStyleLenMax+1];
  408     ListPatternsSink() {
  409       uprv_memset(aliasedStyle, 0, kStyleLenMax+1);
  410     }
  411 #else
  412     char aliasedStyle[kStyleLenMax+1] = {0};
  413 
  414     ListPatternsSink() {}
  415 #endif
  416     virtual ~ListPatternsSink();
  417 
  418     void setAliasedStyle(UnicodeString alias) {
  419         int32_t startIndex = alias.indexOf(aliasPrefix, kAliasPrefixLen, 0);
  420         if (startIndex < 0) {
  421             return;
  422         }
  423         startIndex += kAliasPrefixLen;
  424         int32_t endIndex = alias.indexOf(solidus, startIndex);
  425         if (endIndex < 0) {
  426             endIndex = alias.length();
  427         }
  428         alias.extract(startIndex, endIndex-startIndex, aliasedStyle, kStyleLenMax+1, US_INV);
  429         aliasedStyle[kStyleLenMax] = 0;
  430     }
  431 
  432     void handleValueForPattern(ResourceValue &value, UnicodeString &pattern, UErrorCode &errorCode) {
  433         if (pattern.isEmpty()) {
  434             if (value.getType() == URES_ALIAS) {
  435                 if (aliasedStyle[0] == 0) {
  436                     setAliasedStyle(value.getAliasUnicodeString(errorCode));
  437                 }
  438             } else {
  439                 pattern = value.getUnicodeString(errorCode);
  440             }
  441         }
  442     }
  443 
  444     virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/,
  445             UErrorCode &errorCode) {
  446         aliasedStyle[0] = 0;
  447         if (value.getType() == URES_ALIAS) {
  448             setAliasedStyle(value.getAliasUnicodeString(errorCode));
  449             return;
  450         }
  451         ResourceTable listPatterns = value.getTable(errorCode);
  452         for (int i = 0; U_SUCCESS(errorCode) && listPatterns.getKeyAndValue(i, key, value); ++i) {
  453             if (uprv_strcmp(key, "2") == 0) {
  454                 handleValueForPattern(value, two, errorCode);
  455             } else if (uprv_strcmp(key, "end") == 0) {
  456                 handleValueForPattern(value, end, errorCode);
  457             } else if (uprv_strcmp(key, "middle") == 0) {
  458                 handleValueForPattern(value, middle, errorCode);
  459             } else if (uprv_strcmp(key, "start") == 0) {
  460                 handleValueForPattern(value, start, errorCode);
  461             }
  462         }
  463     }
  464 };
  465 
  466 // Virtual destructors must be defined out of line.
  467 ListFormatter::ListPatternsSink::~ListPatternsSink() {}
  468 
  469 ListFormatInternal* ListFormatter::loadListFormatInternal(
  470         const Locale& locale, const char * style, UErrorCode& errorCode) {
  471     UResourceBundle* rb = ures_open(nullptr, locale.getName(), &errorCode);
  472     rb = ures_getByKeyWithFallback(rb, "listPattern", rb, &errorCode);
  473     if (U_FAILURE(errorCode)) {
  474         ures_close(rb);
  475         return nullptr;
  476     }
  477     ListFormatter::ListPatternsSink sink;
  478     char currentStyle[kStyleLenMax+1];
  479     uprv_strncpy(currentStyle, style, kStyleLenMax);
  480     currentStyle[kStyleLenMax] = 0;
  481 
  482     for (;;) {
  483         ures_getAllItemsWithFallback(rb, currentStyle, sink, errorCode);
  484         if (U_FAILURE(errorCode) || sink.aliasedStyle[0] == 0 || uprv_strcmp(currentStyle, sink.aliasedStyle) == 0) {
  485             break;
  486         }
  487         uprv_strcpy(currentStyle, sink.aliasedStyle);
  488     }
  489     ures_close(rb);
  490     if (U_FAILURE(errorCode)) {
  491         return nullptr;
  492     }
  493     if (sink.two.isEmpty() || sink.start.isEmpty() || sink.middle.isEmpty() || sink.end.isEmpty()) {
  494         errorCode = U_MISSING_RESOURCE_ERROR;
  495         return nullptr;
  496     }
  497 
  498     ListFormatInternal* result = new ListFormatInternal(sink.two, sink.start, sink.middle, sink.end, locale, errorCode);
  499     if (result == nullptr) {
  500         errorCode = U_MEMORY_ALLOCATION_ERROR;
  501         return nullptr;
  502     }
  503     if (U_FAILURE(errorCode)) {
  504         delete result;
  505         return nullptr;
  506     }
  507     return result;
  508 }
  509 
  510 ListFormatter* ListFormatter::createInstance(UErrorCode& errorCode) {
  511     Locale locale;  // The default locale.
  512     return createInstance(locale, errorCode);
  513 }
  514 
  515 ListFormatter* ListFormatter::createInstance(const Locale& locale, UErrorCode& errorCode) {
  516 #if !UCONFIG_NO_FORMATTING
  517     return createInstance(locale, ULISTFMT_TYPE_AND, ULISTFMT_WIDTH_WIDE, errorCode);
  518 #else
  519     return createInstance(locale, "standard", errorCode);
  520 #endif
  521 }
  522 
  523 #if !UCONFIG_NO_FORMATTING
  524 ListFormatter* ListFormatter::createInstance(
  525         const Locale& locale, UListFormatterType type, UListFormatterWidth width, UErrorCode& errorCode) {
  526     const char* style = typeWidthToStyleString(type, width);
  527     if (style == nullptr) {
  528         errorCode = U_ILLEGAL_ARGUMENT_ERROR;
  529         return nullptr;
  530     }
  531     return createInstance(locale, style, errorCode);
  532 }
  533 #endif
  534 
  535 ListFormatter* ListFormatter::createInstance(const Locale& locale, const char *style, UErrorCode& errorCode) {
  536     const ListFormatInternal* listFormatInternal = getListFormatInternal(locale, style, errorCode);
  537     if (U_FAILURE(errorCode)) {
  538         return nullptr;
  539     }
  540     ListFormatter* p = new ListFormatter(listFormatInternal);
  541     if (p == nullptr) {
  542         errorCode = U_MEMORY_ALLOCATION_ERROR;
  543         return nullptr;
  544     }
  545     return p;
  546 }
  547 
  548 ListFormatter::ListFormatter(const ListFormatData& listFormatData, UErrorCode &errorCode) {
  549     owned = new ListFormatInternal(listFormatData, errorCode);
  550     data = owned;
  551 }
  552 
  553 ListFormatter::ListFormatter(const ListFormatInternal* listFormatterInternal) : owned(nullptr), data(listFormatterInternal) {
  554 }
  555 
  556 ListFormatter::~ListFormatter() {
  557     delete owned;
  558 }
  559 
  560 /**
  561  * Joins first and second using the pattern pat.
  562  * On entry offset is an offset into first or -1 if offset unspecified.
  563  * On exit offset is offset of second in result if recordOffset was set
  564  * Otherwise if it was >=0 it is set to point into result where it used
  565  * to point into first. On exit, result is the join of first and second
  566  * according to pat. Any previous value of result gets replaced.
  567  */
  568 static void joinStringsAndReplace(
  569         const SimpleFormatter& pat,
  570         const UnicodeString& first,
  571         const UnicodeString& second,
  572         UnicodeString &result,
  573         UBool recordOffset,
  574         int32_t &offset,
  575         int32_t *offsetFirst,
  576         int32_t *offsetSecond,
  577         UErrorCode& errorCode) {
  578     if (U_FAILURE(errorCode)) {
  579         return;
  580     }
  581     const UnicodeString *params[2] = {&first, &second};
  582     int32_t offsets[2];
  583     pat.formatAndReplace(
  584             params,
  585             UPRV_LENGTHOF(params),
  586             result,
  587             offsets,
  588             UPRV_LENGTHOF(offsets),
  589             errorCode);
  590     if (U_FAILURE(errorCode)) {
  591         return;
  592     }
  593     if (offsets[0] == -1 || offsets[1] == -1) {
  594         errorCode = U_INVALID_FORMAT_ERROR;
  595         return;
  596     }
  597     if (recordOffset) {
  598         offset = offsets[1];
  599     } else if (offset >= 0) {
  600         offset += offsets[0];
  601     }
  602     if (offsetFirst != nullptr) *offsetFirst = offsets[0];
  603     if (offsetSecond != nullptr) *offsetSecond = offsets[1];
  604 }
  605 
  606 UnicodeString& ListFormatter::format(
  607         const UnicodeString items[],
  608         int32_t nItems,
  609         UnicodeString& appendTo,
  610         UErrorCode& errorCode) const {
  611     int32_t offset;
  612     return format(items, nItems, appendTo, -1, offset, errorCode);
  613 }
  614 
  615 UnicodeString& ListFormatter::format(
  616         const UnicodeString items[],
  617         int32_t nItems,
  618         UnicodeString& appendTo,
  619         int32_t index,
  620         int32_t &offset,
  621         UErrorCode& errorCode) const {
  622   return format_(items, nItems, appendTo, index, offset, nullptr, errorCode);
  623 }
  624 
  625 #if !UCONFIG_NO_FORMATTING
  626 FormattedList ListFormatter::formatStringsToValue(
  627         const UnicodeString items[],
  628         int32_t nItems,
  629         UErrorCode& errorCode) const {
  630     LocalPointer<FormattedListData> result(new FormattedListData(errorCode), errorCode);
  631     if (U_FAILURE(errorCode)) {
  632         return FormattedList(errorCode);
  633     }
  634     UnicodeString string;
  635     int32_t offset;
  636     auto handler = result->getHandler(errorCode);
  637     handler.setCategory(UFIELD_CATEGORY_LIST);
  638     format_(items, nItems, string, -1, offset, &handler, errorCode);
  639     handler.getError(errorCode);
  640     result->appendString(string, errorCode);
  641     if (U_FAILURE(errorCode)) {
  642         return FormattedList(errorCode);
  643     }
  644 
  645     // Add span fields and sort
  646     ConstrainedFieldPosition cfpos;
  647     cfpos.constrainField(UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD);
  648     int32_t i = 0;
  649     handler.setCategory(UFIELD_CATEGORY_LIST_SPAN);
  650     while (result->nextPosition(cfpos, errorCode)) {
  651         handler.addAttribute(i++, cfpos.getStart(), cfpos.getLimit());
  652     }
  653     handler.getError(errorCode);
  654     if (U_FAILURE(errorCode)) {
  655         return FormattedList(errorCode);
  656     }
  657     result->sort();
  658 
  659     return FormattedList(result.orphan());
  660 }
  661 #endif
  662 
  663 UnicodeString& ListFormatter::format_(
  664         const UnicodeString items[],
  665         int32_t nItems,
  666         UnicodeString& appendTo,
  667         int32_t index,
  668         int32_t &offset,
  669         FieldPositionHandler* handler,
  670         UErrorCode& errorCode) const {
  671 #if !UCONFIG_NO_FORMATTING
  672     offset = -1;
  673     if (U_FAILURE(errorCode)) {
  674         return appendTo;
  675     }
  676     if (data == nullptr) {
  677         errorCode = U_INVALID_STATE_ERROR;
  678         return appendTo;
  679     }
  680 
  681     if (nItems <= 0) {
  682         return appendTo;
  683     }
  684     if (nItems == 1) {
  685         if (index == 0) {
  686             offset = appendTo.length();
  687         }
  688         if (handler != nullptr) {
  689             handler->addAttribute(ULISTFMT_ELEMENT_FIELD,
  690                                   appendTo.length(),
  691                                   appendTo.length() + items[0].length());
  692         }
  693         appendTo.append(items[0]);
  694         return appendTo;
  695     }
  696     UnicodeString result(items[0]);
  697     if (index == 0) {
  698         offset = 0;
  699     }
  700     int32_t offsetFirst = 0;
  701     int32_t offsetSecond = 0;
  702     int32_t prefixLength = 0;
  703     // for n items, there are 2 * (n + 1) boundary including 0 and the upper
  704     // edge.
  705     MaybeStackArray<int32_t, 10> offsets((handler != nullptr) ? 2 * (nItems + 1): 0);
  706     if (nItems == 2) {
  707         joinStringsAndReplace(
  708                 data->patternHandler->getTwoPattern(items[1]),
  709                 result,
  710                 items[1],
  711                 result,
  712                 index == 1,
  713                 offset,
  714                 &offsetFirst,
  715                 &offsetSecond,
  716                 errorCode);
  717     } else {
  718         joinStringsAndReplace(
  719                 data->startPattern,
  720                 result,
  721                 items[1],
  722                 result,
  723                 index == 1,
  724                 offset,
  725                 &offsetFirst,
  726                 &offsetSecond,
  727                 errorCode);
  728     }
  729     if (handler != nullptr) {
  730         offsets[0] = 0;
  731         prefixLength += offsetFirst;
  732         offsets[1] = offsetSecond - prefixLength;
  733     }
  734     if (nItems > 2) {
  735         for (int32_t i = 2; i < nItems - 1; ++i) {
  736              joinStringsAndReplace(
  737                      data->middlePattern,
  738                      result,
  739                      items[i],
  740                      result,
  741                      index == i,
  742                      offset,
  743                      &offsetFirst,
  744                      &offsetSecond,
  745                      errorCode);
  746             if (handler != nullptr) {
  747                 prefixLength += offsetFirst;
  748                 offsets[i] = offsetSecond - prefixLength;
  749             }
  750         }
  751         joinStringsAndReplace(
  752                 data->patternHandler->getEndPattern(items[nItems - 1]),
  753                 result,
  754                 items[nItems - 1],
  755                 result,
  756                 index == nItems - 1,
  757                 offset,
  758                 &offsetFirst,
  759                 &offsetSecond,
  760                 errorCode);
  761         if (handler != nullptr) {
  762             prefixLength += offsetFirst;
  763             offsets[nItems - 1] = offsetSecond - prefixLength;
  764         }
  765     }
  766     if (handler != nullptr) {
  767         // If there are already some data in appendTo, we need to adjust the index
  768         // by shifting that lenght while insert into handler.
  769         int32_t shift = appendTo.length() + prefixLength;
  770         // Output the ULISTFMT_ELEMENT_FIELD in the order of the input elements
  771         for (int32_t i = 0; i < nItems; ++i) {
  772             offsets[i + nItems] = offsets[i] + items[i].length() + shift;
  773             offsets[i] += shift;
  774             handler->addAttribute(
  775                 ULISTFMT_ELEMENT_FIELD,  // id
  776                 offsets[i],  // index
  777                 offsets[i + nItems]);  // limit
  778         }
  779         // The locale pattern may reorder the items (such as in ur-IN locale),
  780         // so we cannot assume the array is in accendning order.
  781         // To handle the edging case, just insert the two ends into the array
  782         // and sort. Then we output ULISTFMT_LITERAL_FIELD if the indecies
  783         // between the even and odd position are not the same in the sorted array.
  784         offsets[2 * nItems] = shift - prefixLength;
  785         offsets[2 * nItems + 1] = result.length() + shift - prefixLength;
  786         uprv_sortArray(offsets.getAlias(), 2 * (nItems + 1), sizeof(int32_t),
  787                uprv_int32Comparator, nullptr,
  788                false, &errorCode);
  789         for (int32_t i = 0; i <= nItems; ++i) {
  790           if (offsets[i * 2] != offsets[i * 2 + 1]) {
  791             handler->addAttribute(
  792                 ULISTFMT_LITERAL_FIELD,  // id
  793                 offsets[i * 2],  // index
  794                 offsets[i * 2 + 1]);  // limit
  795           }
  796         }
  797     }
  798     if (U_SUCCESS(errorCode)) {
  799         if (offset >= 0) {
  800             offset += appendTo.length();
  801         }
  802         appendTo += result;
  803     }
  804 #endif  
  805     return appendTo;
  806 }
  807  
  808 U_NAMESPACE_END