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",