From 971135761714f3b2227a898e8f9504083dcd0a21 Mon Sep 17 00:00:00 2001 From: Peter Edberg Date: Fri, 24 Mar 2017 08:13:10 +0000 Subject: [PATCH] ICU-12994 Have C++ ListFormatter use ures_getAllItemsWithFallback to load resources; add tests for C and J X-SVN-Rev: 39926 --- icu4c/source/common/listformatter.cpp | 117 +++++++++++++----- icu4c/source/common/unicode/listformatter.h | 2 + .../test/intltest/listformattertest.cpp | 45 +++++++ .../source/test/intltest/listformattertest.h | 3 + icu4c/source/test/intltest/measfmttest.cpp | 2 + .../dev/test/format/ListFormatterTest.java | 34 +++++ 6 files changed, 170 insertions(+), 33 deletions(-) diff --git a/icu4c/source/common/listformatter.cpp b/icu4c/source/common/listformatter.cpp index ea77c635397..a0ab80a4905 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 @@ uprv_deleteListFormatInternal(void *obj) { 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 @@ const ListFormatInternal* ListFormatter::getListFormatInternal( 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 @@ static ListFormatInternal* loadListFormatInternal( 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 670fa115116..180fbcb5cde 100644 --- a/icu4c/source/common/unicode/listformatter.h +++ b/icu4c/source/common/unicode/listformatter.h @@ -157,6 +157,8 @@ class U_COMMON_API ListFormatter : public UObject{ 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 427d903f035..31a29cd382b 100644 --- a/icu4c/source/test/intltest/listformattertest.cpp +++ b/icu4c/source/test/intltest/listformattertest.cpp @@ -147,6 +147,48 @@ void ListFormatterTest::TestEnglishUS() { 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 @@ void ListFormatterTest::runIndexedTest(int32_t index, UBool exec, 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 5c1bbd2f578..a59a7e01897 100644 --- a/icu4c/source/test/intltest/listformattertest.h +++ b/icu4c/source/test/intltest/listformattertest.h @@ -33,6 +33,9 @@ class ListFormatterTest : public IntlTest { 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 f34d8397c01..0bd9fee1bd2 100644 --- a/icu4c/source/test/intltest/measfmttest.cpp +++ b/icu4c/source/test/intltest/measfmttest.cpp @@ -1790,6 +1790,8 @@ void MeasureFormatTest::TestManyLocaleDurations() { 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 2a42f7c6b08..9f52775b384 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 @@ public class ListFormatterTest extends TestFmwk { } } + // 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", -- 2.40.0