Cherry-pick: ticket:12994: Have C++ ListFormatter use ures_getAllItemsWithFallback to load resources; add tests for C and J

http://bugs.icu-project.org/trac/changeset/39926

Bug fix: This fixes a regression from ICU 57.

Test: mmma libcore external/icu
Test: ant check
Test: make check
Test: CtsIcuTestCases
Test: CtsLibcoreTestCases
Change-Id: Id1a7a8c718acb35ea7d30c5665e575d6819e212f
diff --git a/icu4c/source/common/listformatter.cpp b/icu4c/source/common/listformatter.cpp
index 9225c22..263d5eb 100644
--- a/icu4c/source/common/listformatter.cpp
+++ b/icu4c/source/common/listformatter.cpp
@@ -25,6 +25,7 @@
 #include "charstr.h"
 #include "ucln_cmn.h"
 #include "uresimp.h"
+#include "resource.h"
 
 U_NAMESPACE_BEGIN
 
@@ -78,17 +79,6 @@
 
 U_CDECL_END
 
-static ListFormatInternal* loadListFormatInternal(
-        const Locale& locale,
-        const char* style,
-        UErrorCode& errorCode);
-
-static void getStringByKey(
-        const UResourceBundle* rb,
-        const char* key,
-        UnicodeString& result,
-        UErrorCode& errorCode);
-
 ListFormatter::ListFormatter(const ListFormatter& other) :
         owned(other.owned), data(other.data) {
     if (other.owned != NULL) {
@@ -171,30 +161,100 @@
     return result;
 }
 
-static ListFormatInternal* loadListFormatInternal(
+static const UChar solidus = 0x2F;
+static const UChar aliasPrefix[] = { 0x6C,0x69,0x73,0x74,0x50,0x61,0x74,0x74,0x65,0x72,0x6E,0x2F }; // "listPattern/"
+enum {
+    kAliasPrefixLen = UPRV_LENGTHOF(aliasPrefix),
+    kStyleLenMax = 24 // longest currently is 14
+};
+
+struct ListFormatter::ListPatternsSink : public ResourceSink {
+    UnicodeString two, start, middle, end;
+    char aliasedStyle[kStyleLenMax+1] = {0};
+
+    ListPatternsSink() {}
+    virtual ~ListPatternsSink();
+
+    void setAliasedStyle(UnicodeString alias) {
+        int32_t startIndex = alias.indexOf(aliasPrefix, kAliasPrefixLen, 0);
+        if (startIndex < 0) {
+            return;
+        }
+        startIndex += kAliasPrefixLen;
+        int32_t endIndex = alias.indexOf(solidus, startIndex);
+        if (endIndex < 0) {
+            endIndex = alias.length();
+        }
+        alias.extract(startIndex, endIndex-startIndex, aliasedStyle, kStyleLenMax+1, US_INV);
+        aliasedStyle[kStyleLenMax] = 0;
+    }
+
+    void handleValueForPattern(ResourceValue &value, UnicodeString &pattern, UErrorCode &errorCode) {
+        if (pattern.isEmpty()) {
+            if (value.getType() == URES_ALIAS) {
+                if (aliasedStyle[0] == 0) {
+                    setAliasedStyle(value.getAliasUnicodeString(errorCode));
+                }
+            } else {
+                pattern = value.getUnicodeString(errorCode);
+            }
+        }
+    }
+
+    virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/,
+            UErrorCode &errorCode) {
+        aliasedStyle[0] = 0;
+        if (value.getType() == URES_ALIAS) {
+            setAliasedStyle(value.getAliasUnicodeString(errorCode));
+            return;
+        }
+        ResourceTable listPatterns = value.getTable(errorCode);
+        for (int i = 0; U_SUCCESS(errorCode) && listPatterns.getKeyAndValue(i, key, value); ++i) {
+            if (uprv_strcmp(key, "2") == 0) {
+                handleValueForPattern(value, two, errorCode);
+            } else if (uprv_strcmp(key, "end") == 0) {
+                handleValueForPattern(value, end, errorCode);
+            } else if (uprv_strcmp(key, "middle") == 0) {
+                handleValueForPattern(value, middle, errorCode);
+            } else if (uprv_strcmp(key, "start") == 0) {
+                handleValueForPattern(value, start, errorCode);
+            }
+        }
+    }
+};
+
+// Virtual destructors must be defined out of line.
+ListFormatter::ListPatternsSink::~ListPatternsSink() {}
+
+ListFormatInternal* ListFormatter::loadListFormatInternal(
         const Locale& locale, const char * style, UErrorCode& errorCode) {
     UResourceBundle* rb = ures_open(NULL, locale.getName(), &errorCode);
-    if (U_FAILURE(errorCode)) {
-        ures_close(rb);
-        return NULL;
-    }
     rb = ures_getByKeyWithFallback(rb, "listPattern", rb, &errorCode);
-    rb = ures_getByKeyWithFallback(rb, style, rb, &errorCode);
-
     if (U_FAILURE(errorCode)) {
         ures_close(rb);
         return NULL;
     }
-    UnicodeString two, start, middle, end;
-    getStringByKey(rb, "2", two, errorCode);
-    getStringByKey(rb, "start", start, errorCode);
-    getStringByKey(rb, "middle", middle, errorCode);
-    getStringByKey(rb, "end", end, errorCode);
+    ListFormatter::ListPatternsSink sink;
+    char currentStyle[kStyleLenMax+1];
+    uprv_strncpy(currentStyle, style, kStyleLenMax);
+    currentStyle[kStyleLenMax] = 0;
+
+    for (;;) {
+        ures_getAllItemsWithFallback(rb, currentStyle, sink, errorCode);
+        if (U_FAILURE(errorCode) || sink.aliasedStyle[0] == 0 || uprv_strcmp(currentStyle, sink.aliasedStyle) == 0) {
+            break;
+        }
+        uprv_strcpy(currentStyle, sink.aliasedStyle);
+    }
     ures_close(rb);
     if (U_FAILURE(errorCode)) {
         return NULL;
     }
-    ListFormatInternal* result = new ListFormatInternal(two, start, middle, end, errorCode);
+    if (sink.two.isEmpty() || sink.start.isEmpty() || sink.middle.isEmpty() || sink.end.isEmpty()) {
+        errorCode = U_MISSING_RESOURCE_ERROR;
+        return NULL;
+    }
+    ListFormatInternal* result = new ListFormatInternal(sink.two, sink.start, sink.middle, sink.end, errorCode);
     if (result == NULL) {
         errorCode = U_MEMORY_ALLOCATION_ERROR;
         return NULL;
@@ -206,15 +266,6 @@
     return result;
 }
 
-static void getStringByKey(const UResourceBundle* rb, const char* key, UnicodeString& result, UErrorCode& errorCode) {
-    int32_t len;
-    const UChar* ustr = ures_getStringByKeyWithFallback(rb, key, &len, &errorCode);
-    if (U_FAILURE(errorCode)) {
-      return;
-    }
-    result.setTo(ustr, len);
-}
-
 ListFormatter* ListFormatter::createInstance(UErrorCode& errorCode) {
     Locale locale;  // The default locale.
     return createInstance(locale, errorCode);
diff --git a/icu4c/source/common/unicode/listformatter.h b/icu4c/source/common/unicode/listformatter.h
index f2c8988..93eb7f3 100644
--- a/icu4c/source/common/unicode/listformatter.h
+++ b/icu4c/source/common/unicode/listformatter.h
@@ -157,6 +157,8 @@
   private:
     static void initializeHash(UErrorCode& errorCode);
     static const ListFormatInternal* getListFormatInternal(const Locale& locale, const char *style, UErrorCode& errorCode);
+    struct ListPatternsSink;
+    static ListFormatInternal* loadListFormatInternal(const Locale& locale, const char* style, UErrorCode& errorCode);
 
     ListFormatter();
 
diff --git a/icu4c/source/test/intltest/listformattertest.cpp b/icu4c/source/test/intltest/listformattertest.cpp
index b7aaea2..ad0afa5 100644
--- a/icu4c/source/test/intltest/listformattertest.cpp
+++ b/icu4c/source/test/intltest/listformattertest.cpp
@@ -147,6 +147,48 @@
     CheckFourCases("en_US", one, two, three, four, results);
 }
 
+// Tests resource loading and inheritance when region sublocale
+// has only partial data for the listPattern element (overriding
+// some of the parent data). #12994
+void ListFormatterTest::TestEnglishGB() {
+    UnicodeString results[4] = {
+        one,
+        one + " and " + two,
+        one + ", " + two + " and " + three,
+        one + ", " + two + ", " + three + " and " + four
+    };
+
+    CheckFourCases("en_GB", one, two, three, four, results);
+}
+
+// Tests resource loading and inheritance when region sublocale
+// has only partial data for the listPattern element (overriding
+// some of the parent data). #12994
+void ListFormatterTest::TestNynorsk() {
+    UnicodeString results[4] = {
+        one,
+        one + " og " + two,
+        one + ", " + two + " og " + three,
+        one + ", " + two + ", " + three + " og " + four
+    };
+
+    CheckFourCases("nn", one, two, three, four, results);
+}
+
+// Tests resource loading and inheritance when region sublocale
+// has only partial data for the listPattern element (overriding
+// some of the parent data). #12994
+void ListFormatterTest::TestChineseTradHK() {
+    UnicodeString results[4] = {
+        one,
+        one + "\u53CA" + two,
+        one + "\u3001" + two + "\u53CA" + three,
+        one + "\u3001" + two + "\u3001" + three + "\u53CA" + four
+    };
+
+    CheckFourCases("zh_Hant_HK", one, two, three, four, results);
+}
+
 // Formatting in Russian.
 // "\\u0438" is used before the last element, and all elements up to (but not including) the penultimate are followed by a comma.
 void ListFormatterTest::TestRussian() {
@@ -229,6 +271,9 @@
         case 6: name = "TestZulu"; if (exec) TestZulu(); break;
         case 7: name = "TestOutOfOrderPatterns"; if (exec) TestOutOfOrderPatterns(); break;
         case 8: name = "Test9946"; if (exec) Test9946(); break;
+        case 9: name = "TestEnglishGB"; if (exec) TestEnglishGB(); break;
+        case 10: name = "TestNynorsk"; if (exec) TestNynorsk(); break;
+        case 11: name = "TestChineseTradHK"; if (exec) TestChineseTradHK(); break;
 
         default: name = ""; break;
     }
diff --git a/icu4c/source/test/intltest/listformattertest.h b/icu4c/source/test/intltest/listformattertest.h
index 1281306..70686d7 100644
--- a/icu4c/source/test/intltest/listformattertest.h
+++ b/icu4c/source/test/intltest/listformattertest.h
@@ -33,6 +33,9 @@
     void TestBogus();
     void TestEnglish();
     void TestEnglishUS();
+    void TestEnglishGB();
+    void TestNynorsk();
+    void TestChineseTradHK();
     void TestRussian();
     void TestMalayalam();
     void TestZulu();
diff --git a/icu4c/source/test/intltest/measfmttest.cpp b/icu4c/source/test/intltest/measfmttest.cpp
index 510146b..801e590 100644
--- a/icu4c/source/test/intltest/measfmttest.cpp
+++ b/icu4c/source/test/intltest/measfmttest.cpp
@@ -1645,6 +1645,8 @@
     helperTestManyLocaleDurations("de", UMEASFMT_WIDTH_NUMERIC, measures, UPRV_LENGTHOF(measures), "5:37");
     helperTestManyLocaleDurations("en", UMEASFMT_WIDTH_NARROW,  measures, UPRV_LENGTHOF(measures), "5h 37m");
     helperTestManyLocaleDurations("en", UMEASFMT_WIDTH_NUMERIC, measures, UPRV_LENGTHOF(measures), "5:37");
+    helperTestManyLocaleDurations("en_GB", UMEASFMT_WIDTH_NARROW,  measures, UPRV_LENGTHOF(measures), "5h 37m");
+    helperTestManyLocaleDurations("en_GB", UMEASFMT_WIDTH_NUMERIC, measures, UPRV_LENGTHOF(measures), "5:37");
     helperTestManyLocaleDurations("es", UMEASFMT_WIDTH_NARROW,  measures, UPRV_LENGTHOF(measures), "5h 37min");
     helperTestManyLocaleDurations("es", UMEASFMT_WIDTH_NUMERIC, measures, UPRV_LENGTHOF(measures), "5:37");
     helperTestManyLocaleDurations("fi", UMEASFMT_WIDTH_NARROW,  measures, UPRV_LENGTHOF(measures), "5t 37min");
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/ListFormatterTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/ListFormatterTest.java
index 2a42f7c..9f52775 100644
--- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/ListFormatterTest.java
+++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/ListFormatterTest.java
@@ -53,6 +53,40 @@
         }
     }
 
+    // Tests resource loading and inheritance when region sublocale
+    // has only partial data for the listPattern element (overriding
+    // some of the parent data). #12994
+    String[] EnglishGBTestData = {
+            "",
+            "A",
+            "A and B",
+            "A, B and C",
+            "A, B, C and D",
+            "A, B, C, D and E"
+    };
+
+    @Test
+    public void TestEnglishGB() {
+        checkData(ListFormatter.getInstance(new ULocale("en_GB")), EnglishGBTestData);
+    }
+
+    // Tests resource loading and inheritance when region sublocale
+    // has only partial data for the listPattern element (overriding
+    // some of the parent data). #12994
+    String[] ChineseTradHKTestData = {
+            "",
+            "A",
+            "A\u53CAB",
+            "A\u3001B\u53CAC",
+            "A\u3001B\u3001C\u53CAD",
+            "A\u3001B\u3001C\u3001D\u53CAE"
+    };
+
+    @Test
+    public void TestChineseTradHK() {
+        checkData(ListFormatter.getInstance(new ULocale("zh_Hant_HK")), ChineseTradHKTestData);
+    }
+
     String[] JapaneseTestData = {
             "",
             "A",