From 7cf0adb29a5b9daeb1e947349f3ecf625a864fdf Mon Sep 17 00:00:00 2001 From: Peter Edberg Date: Thu, 15 Feb 2018 08:38:20 +0000 Subject: [PATCH] ICU-12740 getFieldDisplayName C/C++ initial implementation & C tests; TODO: C++ tests and Java imp X-SVN-Rev: 40922 --- icu4c/source/i18n/dtptngen.cpp | 88 +++++++++++++++++------- icu4c/source/i18n/udatpg.cpp | 19 +++++ icu4c/source/i18n/unicode/dtptngen.h | 25 +++++-- icu4c/source/i18n/unicode/udatpg.h | 53 +++++++++++++- icu4c/source/test/cintltst/udatpg_test.c | 72 +++++++++++++++++++ 5 files changed, 229 insertions(+), 28 deletions(-) diff --git a/icu4c/source/i18n/dtptngen.cpp b/icu4c/source/i18n/dtptngen.cpp index 187342e4af..7336dd39e9 100644 --- a/icu4c/source/i18n/dtptngen.cpp +++ b/icu4c/source/i18n/dtptngen.cpp @@ -261,12 +261,19 @@ static const char* const CLDR_FIELD_APPEND[] = { "Hour", "Minute", "Second", "*", "Timezone" }; -static const char* const CLDR_FIELD_NAME[] = { +static const char* const CLDR_FIELD_NAME[UDATPG_FIELD_COUNT] = { "era", "year", "quarter", "month", "week", "weekOfMonth", "weekday", "dayOfYear", "weekdayOfMonth", "day", "dayperiod", // The UDATPG_x_FIELD constants and these fields have a different order than in ICU4J "hour", "minute", "second", "*", "zone" }; +static const char* const CLDR_FIELD_WIDTH[UDATPG_WIDTH_COUNT] = { + "", "-short", "-narrow" +}; + +static constexpr UDateTimePGDisplayWidth UDATPG_WIDTH_APPENDITEM = UDATPG_WIDE; +static constexpr int32_t UDATPG_FIELD_KEY_MAX = 24; // max length of CLDR field tag (type + width) + // For appendItems static const UChar UDATPG_ItemFormat[]= {0x7B, 0x30, 0x7D, 0x20, 0x251C, 0x7B, 0x32, 0x7D, 0x3A, 0x20, 0x7B, 0x31, 0x7D, 0x2524, 0}; // {0} \u251C{2}: {1}\u2524 @@ -379,10 +386,11 @@ DateTimePatternGenerator::operator=(const DateTimePatternGenerator& other) { } for (int32_t i=0; i< UDATPG_FIELD_COUNT; ++i ) { appendItemFormats[i] = other.appendItemFormats[i]; - appendItemNames[i] = other.appendItemNames[i]; - // NUL-terminate for the C API. - appendItemFormats[i].getTerminatedBuffer(); - appendItemNames[i].getTerminatedBuffer(); + appendItemFormats[i].getTerminatedBuffer(); // NUL-terminate for the C API. + for (int32_t j=0; j< UDATPG_WIDTH_COUNT; ++j ) { + fieldDisplayNames[i][j] = other.fieldDisplayNames[i][j]; + fieldDisplayNames[i][j].getTerminatedBuffer(); // NUL-terminate for the C API. + } } UErrorCode status = U_ZERO_ERROR; patternMap->copyFrom(*other.patternMap, status); @@ -399,10 +407,14 @@ DateTimePatternGenerator::operator==(const DateTimePatternGenerator& other) cons if ((pLocale==other.pLocale) && (patternMap->equals(*other.patternMap)) && (dateTimeFormat==other.dateTimeFormat) && (decimal==other.decimal)) { for ( int32_t i=0 ; i0; --i) { + if (uprv_strcmp(CLDR_FIELD_WIDTH[i], hyphenPtr)==0) { + *widthP=(UDateTimePGDisplayWidth)i; + break; + } + } + *hyphenPtr = 0; // now delete width portion of key + } for (int32_t i=0; igetFieldDisplayName(field,width); + if (fieldName == NULL) { + return result.length(); + } + return result.extract(fieldName, capacity, *pErrorCode); +} + U_CAPI void U_EXPORT2 udatpg_setDateTimeFormat(const UDateTimePatternGenerator *dtpg, const UChar *dtFormat, int32_t length) { diff --git a/icu4c/source/i18n/unicode/dtptngen.h b/icu4c/source/i18n/unicode/dtptngen.h index 5712edbb9f..b19f22282e 100644 --- a/icu4c/source/i18n/unicode/dtptngen.h +++ b/icu4c/source/i18n/unicode/dtptngen.h @@ -263,14 +263,29 @@ public: /** * Getter corresponding to setAppendItemNames. Values below 0 or at or above - * UDATPG_FIELD_COUNT are illegal arguments. + * UDATPG_FIELD_COUNT are illegal arguments. Note: The more general method + * for getting date/time field display names is getFieldDisplayName. * * @param field such as UDATPG_ERA_FIELD. * @return name for field + * @see getFieldDisplayName * @stable ICU 3.8 */ const UnicodeString& getAppendItemName(UDateTimePatternField field) const; +#ifndef U_HIDE_DRAFT_API + /** + * The general interface to get a display name for a particular date/time field, + * in one of several possible display widths. + * + * @param field The desired UDateTimePatternField, such as UDATPG_ERA_FIELD. + * @param width The desired UDateTimePGDisplayWidth, such as UDATPG_ABBREVIATED. + * @return. The display name for field + * @draft ICU 61 + */ + UnicodeString getFieldDisplayName(UDateTimePatternField field, UDateTimePGDisplayWidth width) const; +#endif // U_HIDE_DRAFT_API + /** * The DateTimeFormat is a message format pattern used to compose date and * time patterns. The default pattern in the root locale is "{1} {0}", where @@ -513,7 +528,8 @@ private: DistanceInfo *distanceInfo; PatternMap *patternMap; UnicodeString appendItemFormats[UDATPG_FIELD_COUNT]; - UnicodeString appendItemNames[UDATPG_FIELD_COUNT]; + #define UDATPG_WIDTH_COUNT (UDATPG_NARROW + 1) + UnicodeString fieldDisplayNames[UDATPG_FIELD_COUNT][UDATPG_WIDTH_COUNT]; UnicodeString dateTimeFormat; UnicodeString decimal; DateTimeMatcher *skipMatcher; @@ -543,8 +559,9 @@ private: void setDateTimeFromCalendar(const Locale& locale, UErrorCode& status); void setDecimalSymbols(const Locale& locale, UErrorCode& status); UDateTimePatternField getAppendFormatNumber(const char* field) const; - UDateTimePatternField getAppendNameNumber(const char* field) const; - UnicodeString& getMutableAppendItemName(UDateTimePatternField field); + UDateTimePatternField getFieldAndWidthIndices(const char* key, UDateTimePGDisplayWidth* widthP) const; + void setFieldDisplayName(UDateTimePatternField field, UDateTimePGDisplayWidth width, const UnicodeString& value); + UnicodeString& getMutableFieldDisplayName(UDateTimePatternField field, UDateTimePGDisplayWidth width); void getAppendName(UDateTimePatternField field, UnicodeString& value); UnicodeString mapSkeletonMetacharacters(const UnicodeString& patternForm, int32_t* flags, UErrorCode& status); int32_t getCanonicalIndex(const UnicodeString& field); diff --git a/icu4c/source/i18n/unicode/udatpg.h b/icu4c/source/i18n/unicode/udatpg.h index 76baa3da58..beae756d87 100644 --- a/icu4c/source/i18n/unicode/udatpg.h +++ b/icu4c/source/i18n/unicode/udatpg.h @@ -95,6 +95,21 @@ typedef enum UDateTimePatternField { UDATPG_FIELD_COUNT } UDateTimePatternField; +#ifndef U_HIDE_DRAFT_API +/** + * Field display name width constants for udatpg_getFieldDisplayName(). + * @draft ICU 61 + */ +typedef enum UDateTimePGDisplayWidth { + /** @draft ICU 61 */ + UDATPG_WIDE, + /** @draft ICU 61 */ + UDATPG_ABBREVIATED, + /** @draft ICU 61 */ + UDATPG_NARROW +} UDateTimePGDisplayWidth; +#endif // U_HIDE_DRAFT_API + /** * Masks to control forcing the length of specified fields in the returned * pattern to match those in the skeleton (when this would not happen @@ -410,12 +425,14 @@ udatpg_setAppendItemName(UDateTimePatternGenerator *dtpg, /** * Getter corresponding to setAppendItemNames. Values below 0 or at or above - * UDATPG_FIELD_COUNT are illegal arguments. + * UDATPG_FIELD_COUNT are illegal arguments. Note: The more general function + * for getting date/time field display names is udatpg_getFieldDisplayName. * * @param dtpg a pointer to UDateTimePatternGenerator. * @param field UDateTimePatternField, such as UDATPG_ERA_FIELD * @param pLength A pointer that will receive the length of the name for field. * @return name for field + * @see udatpg_getFieldDisplayName * @stable ICU 3.8 */ U_STABLE const UChar * U_EXPORT2 @@ -423,6 +440,40 @@ udatpg_getAppendItemName(const UDateTimePatternGenerator *dtpg, UDateTimePatternField field, int32_t *pLength); +#ifndef U_HIDE_DRAFT_API +/** + * The general interface to get a display name for a particular date/time field, + * in one of several possible display widths. + * + * @param dtpg + * A pointer to the UDateTimePatternGenerator object with the localized + * display names. + * @param field + * The desired UDateTimePatternField, such as UDATPG_ERA_FIELD. + * @param width + * The desired UDateTimePGDisplayWidth, such as UDATPG_ABBREVIATED. + * @param fieldName + * A pointer to a buffer to receive the NULL-terminated display name. If the name + * fits into fieldName but cannot be NULL-terminated (length == capacity) then + * the error code is set to U_STRING_NOT_TERMINATED_WARNING. If the name doesn't + * fit into fieldName then the error code is set to U_BUFFER_OVERFLOW_ERROR. + * @param capacity + * The size of fieldName (in UChars). + * @param pErrorCode + * A pointer to a UErrorCode to receive any errors + * @return + * The full length of the name; if greater than capacity, fieldName contains a + * truncated result. + * @draft ICU 61 + */ +U_DRAFT int32_t U_EXPORT2 +udatpg_getFieldDisplayName(const UDateTimePatternGenerator *dtpg, + UDateTimePatternField field, + UDateTimePGDisplayWidth width, + UChar *fieldName, int32_t capacity, + UErrorCode *pErrorCode); +#endif // U_HIDE_DRAFT_API + /** * The DateTimeFormat is a message format pattern used to compose date and * time patterns. The default pattern in the root locale is "{1} {0}", where diff --git a/icu4c/source/test/cintltst/udatpg_test.c b/icu4c/source/test/cintltst/udatpg_test.c index 8895c508ac..a594560290 100644 --- a/icu4c/source/test/cintltst/udatpg_test.c +++ b/icu4c/source/test/cintltst/udatpg_test.c @@ -42,12 +42,14 @@ static void TestOpenClose(void); static void TestUsage(void); static void TestBuilder(void); static void TestOptions(void); +static void TestGetFieldDisplayNames(void); void addDateTimePatternGeneratorTest(TestNode** root) { TESTCASE(TestOpenClose); TESTCASE(TestUsage); TESTCASE(TestBuilder); TESTCASE(TestOptions); + TESTCASE(TestGetFieldDisplayNames); } /* @@ -438,4 +440,74 @@ static void TestOptions() { } } +typedef struct FieldDisplayNameData { + const char * locale; + UDateTimePatternField field; + UDateTimePGDisplayWidth width; + const char * expected; +} FieldDisplayNameData; +enum { kFieldDisplayNameMax = 32, kFieldDisplayNameBytesMax = 64}; + +static void TestGetFieldDisplayNames() { + const FieldDisplayNameData testData[] = { + /*loc field width expectedName */ + { "de", UDATPG_QUARTER_FIELD, UDATPG_WIDE, "Quartal" }, + { "de", UDATPG_QUARTER_FIELD, UDATPG_ABBREVIATED, "Quart." }, + { "de", UDATPG_QUARTER_FIELD, UDATPG_NARROW, "Q" }, + { "en", UDATPG_DAY_OF_WEEK_IN_MONTH_FIELD, UDATPG_WIDE, "weekday of the month" }, + { "en", UDATPG_DAY_OF_WEEK_IN_MONTH_FIELD, UDATPG_ABBREVIATED, "wkday. of mo." }, + { "en", UDATPG_DAY_OF_WEEK_IN_MONTH_FIELD, UDATPG_NARROW, "wkday. of mo." }, + { "en_GB", UDATPG_DAY_OF_WEEK_IN_MONTH_FIELD, UDATPG_WIDE, "weekday of the month" }, + { "en_GB", UDATPG_DAY_OF_WEEK_IN_MONTH_FIELD, UDATPG_ABBREVIATED, "wkday of mo" }, + { "en_GB", UDATPG_DAY_OF_WEEK_IN_MONTH_FIELD, UDATPG_NARROW, "wkday of mo" }, + { "it", UDATPG_SECOND_FIELD, UDATPG_WIDE, "secondo" }, + { "it", UDATPG_SECOND_FIELD, UDATPG_ABBREVIATED, "s" }, + { "it", UDATPG_SECOND_FIELD, UDATPG_NARROW, "s" }, + }; + + int count = UPRV_LENGTHOF(testData); + const FieldDisplayNameData * testDataPtr = testData; + for (; count-- > 0; ++testDataPtr) { + UErrorCode status = U_ZERO_ERROR; + UDateTimePatternGenerator * dtpgen = udatpg_open(testDataPtr->locale, &status); + if ( U_FAILURE(status) ) { + log_data_err("ERROR udatpg_open failed for locale %s : %s - (Are you missing data?)\n", testDataPtr->locale, myErrorName(status)); + } else { + UChar expName[kFieldDisplayNameMax]; + UChar getName[kFieldDisplayNameMax]; + u_unescape(testDataPtr->expected, expName, kFieldDisplayNameMax); + + int32_t getLen = udatpg_getFieldDisplayName(dtpgen, testDataPtr->field, testDataPtr->width, + getName, kFieldDisplayNameMax, &status); + if ( U_FAILURE(status) ) { + log_err("ERROR udatpg_getFieldDisplayName locale %s field %d width %d, got status %s, len %d\n", + testDataPtr->locale, testDataPtr->field, testDataPtr->width, u_errorName(status), getLen); + } else if ( u_strncmp(expName, getName, kFieldDisplayNameMax) != 0 ) { + char expNameB[kFieldDisplayNameBytesMax]; + char getNameB[kFieldDisplayNameBytesMax]; + log_err("ERROR udatpg_getFieldDisplayName locale %s field %d width %d, expected %s, got %s, status %s\n", + testDataPtr->locale, testDataPtr->field, testDataPtr->width, + u_austrncpy(expNameB,expName,kFieldDisplayNameBytesMax), + u_austrncpy(getNameB,getName,kFieldDisplayNameBytesMax), u_errorName(status) ); + } else if (testDataPtr->width == UDATPG_WIDE && getLen > 1) { + // test preflight & inadequate buffer + int32_t getNewLen; + status = U_ZERO_ERROR; + getNewLen = udatpg_getFieldDisplayName(dtpgen, testDataPtr->field, UDATPG_WIDE, NULL, 0, &status); + if (U_FAILURE(status) || getNewLen != getLen) { + log_err("ERROR udatpg_getFieldDisplayName locale %s field %d width %d, preflight expected len %d, got %d, status %s\n", + testDataPtr->locale, testDataPtr->field, testDataPtr->width, getLen, getNewLen, u_errorName(status) ); + } + status = U_ZERO_ERROR; + getNewLen = udatpg_getFieldDisplayName(dtpgen, testDataPtr->field, UDATPG_WIDE, getName, getLen-1, &status); + if (status!=U_BUFFER_OVERFLOW_ERROR || getNewLen != getLen) { + log_err("ERROR udatpg_getFieldDisplayName locale %s field %d width %d, overflow expected len %d & BUFFER_OVERFLOW_ERROR, got %d & status %s\n", + testDataPtr->locale, testDataPtr->field, testDataPtr->width, getLen, getNewLen, u_errorName(status) ); + } + } + udatpg_close(dtpgen); + } + } +} + #endif