]> granicus.if.org Git - icu/commitdiff
ICU-12740 getFieldDisplayName C/C++ initial implementation & C tests; TODO: C++ tests...
authorPeter Edberg <pedberg@unicode.org>
Thu, 15 Feb 2018 08:38:20 +0000 (08:38 +0000)
committerPeter Edberg <pedberg@unicode.org>
Thu, 15 Feb 2018 08:38:20 +0000 (08:38 +0000)
X-SVN-Rev: 40922

icu4c/source/i18n/dtptngen.cpp
icu4c/source/i18n/udatpg.cpp
icu4c/source/i18n/unicode/dtptngen.h
icu4c/source/i18n/unicode/udatpg.h
icu4c/source/test/cintltst/udatpg_test.c

index 187342e4af2efce910a38a3a2d869273e3bc985a..7336dd39e9e300e809a2d0667f865305033f43c4 100644 (file)
@@ -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 ; i<UDATPG_FIELD_COUNT; ++i ) {
-           if ((appendItemFormats[i] != other.appendItemFormats[i]) ||
-               (appendItemNames[i] != other.appendItemNames[i]) ) {
-               return FALSE;
-           }
+            if (appendItemFormats[i] != other.appendItemFormats[i]) {
+                return FALSE;
+            }
+            for (int32_t j=0; j< UDATPG_WIDTH_COUNT; ++j ) {
+                if (fieldDisplayNames[i][j] != other.fieldDisplayNames[i][j]) {
+                    return FALSE;
+                }
+            }
         }
         return TRUE;
     }
@@ -824,15 +836,16 @@ struct DateTimePatternGenerator::AppendItemNamesSink : public ResourceSink {
         ResourceTable itemsTable = value.getTable(errorCode);
         if (U_FAILURE(errorCode)) { return; }
         for (int32_t i = 0; itemsTable.getKeyAndValue(i, key, value); ++i) {
-            UDateTimePatternField field = dtpg.getAppendNameNumber(key);
+            UDateTimePGDisplayWidth width;
+            UDateTimePatternField field = dtpg.getFieldAndWidthIndices(key, &width);
             if (field == UDATPG_FIELD_COUNT) { continue; }
             ResourceTable detailsTable = value.getTable(errorCode);
             if (U_FAILURE(errorCode)) { return; }
             for (int32_t j = 0; detailsTable.getKeyAndValue(j, key, value); ++j) {
                 if (uprv_strcmp(key, "dn") != 0) { continue; }
                 const UnicodeString& valueStr = value.getUnicodeString(errorCode);
-                if (dtpg.getAppendItemName(field).isEmpty() && !valueStr.isEmpty()) {
-                    dtpg.setAppendItemName(field, valueStr);
+                if (dtpg.getFieldDisplayName(field,width).isEmpty() && !valueStr.isEmpty()) {
+                    dtpg.setFieldDisplayName(field,width,valueStr);
                 }
                 break;
             }
@@ -841,8 +854,7 @@ struct DateTimePatternGenerator::AppendItemNamesSink : public ResourceSink {
 
     void fillInMissing() {
         for (int32_t i = 0; i < UDATPG_FIELD_COUNT; i++) {
-            UDateTimePatternField field = (UDateTimePatternField)i;
-            UnicodeString& valueStr = dtpg.getMutableAppendItemName(field);
+            UnicodeString& valueStr = dtpg.getMutableFieldDisplayName((UDateTimePatternField)i, UDATPG_WIDE);
             if (valueStr.isEmpty()) {
                 valueStr = CAP_F;
                 U_ASSERT(i < 20);
@@ -857,6 +869,12 @@ struct DateTimePatternGenerator::AppendItemNamesSink : public ResourceSink {
                 // NUL-terminate for the C API.
                 valueStr.getTerminatedBuffer();
             }
+            for (int32_t j = 1; j < UDATPG_WIDTH_COUNT; j++) {
+                UnicodeString& valueStr = dtpg.getMutableFieldDisplayName((UDateTimePatternField)i, (UDateTimePGDisplayWidth)j);
+                if (valueStr.isEmpty()) {
+                    valueStr = dtpg.getFieldDisplayName((UDateTimePatternField)i, (UDateTimePGDisplayWidth)(j-1));
+                }
+            }
         }
     }
 };
@@ -969,25 +987,35 @@ DateTimePatternGenerator::getAppendItemFormat(UDateTimePatternField field) const
 
 void
 DateTimePatternGenerator::setAppendItemName(UDateTimePatternField field, const UnicodeString& value) {
-    appendItemNames[field] = value;
-    // NUL-terminate for the C API.
-    appendItemNames[field].getTerminatedBuffer();
+    setFieldDisplayName(field, UDATPG_WIDTH_APPENDITEM, value);
 }
 
 const UnicodeString&
 DateTimePatternGenerator::getAppendItemName(UDateTimePatternField field) const {
-    return appendItemNames[field];
+    return fieldDisplayNames[field][UDATPG_WIDTH_APPENDITEM];
+}
+
+void
+DateTimePatternGenerator::setFieldDisplayName(UDateTimePatternField field, UDateTimePGDisplayWidth width, const UnicodeString& value) {
+    fieldDisplayNames[field][width] = value;
+    // NUL-terminate for the C API.
+    fieldDisplayNames[field][width].getTerminatedBuffer();
+}
+
+UnicodeString
+DateTimePatternGenerator::getFieldDisplayName(UDateTimePatternField field, UDateTimePGDisplayWidth width) const {
+    return fieldDisplayNames[field][width];
 }
 
 UnicodeString&
-DateTimePatternGenerator::getMutableAppendItemName(UDateTimePatternField field) {
-    return appendItemNames[field];
+DateTimePatternGenerator::getMutableFieldDisplayName(UDateTimePatternField field, UDateTimePGDisplayWidth width) {
+    return fieldDisplayNames[field][width];
 }
 
 void
 DateTimePatternGenerator::getAppendName(UDateTimePatternField field, UnicodeString& value) {
     value = SINGLE_QUOTE;
-    value += appendItemNames[field];
+    value += fieldDisplayNames[field][UDATPG_WIDTH_APPENDITEM];
     value += SINGLE_QUOTE;
 }
 
@@ -1312,9 +1340,23 @@ DateTimePatternGenerator::getAppendFormatNumber(const char* field) const {
 }
 
 UDateTimePatternField
-DateTimePatternGenerator::getAppendNameNumber(const char* field) const {
+DateTimePatternGenerator::getFieldAndWidthIndices(const char* key, UDateTimePGDisplayWidth* widthP) const {
+    char cldrFieldKey[UDATPG_FIELD_KEY_MAX + 1];
+    uprv_strncpy(cldrFieldKey, key, UDATPG_FIELD_KEY_MAX);
+    cldrFieldKey[UDATPG_FIELD_KEY_MAX]=0; // ensure termination
+    *widthP = UDATPG_WIDE;
+    char* hyphenPtr = uprv_strchr(cldrFieldKey, '-');
+    if (hyphenPtr) {
+        for (int32_t i=UDATPG_WIDTH_COUNT-1; i>0; --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; i<UDATPG_FIELD_COUNT; ++i ) {
-        if (uprv_strcmp(CLDR_FIELD_NAME[i],field)==0) {
+        if (uprv_strcmp(CLDR_FIELD_NAME[i],cldrFieldKey)==0) {
             return (UDateTimePatternField)i;
         }
     }
index 9ba82b529c507bdeefcd11ebde5776377d05296a..febf73b3ce499fdcaf4ea3504d80bc1a7f8bed63 100644 (file)
@@ -181,6 +181,25 @@ udatpg_getAppendItemName(const UDateTimePatternGenerator *dtpg,
     return result.getBuffer();
 }
 
+U_CAPI int32_t U_EXPORT2
+udatpg_getFieldDisplayName(const UDateTimePatternGenerator *dtpg,
+                           UDateTimePatternField field,
+                           UDateTimePGDisplayWidth width,
+                           UChar *fieldName, int32_t capacity,
+                           UErrorCode *pErrorCode) {
+    if (U_FAILURE(*pErrorCode))
+        return -1;
+    if (fieldName == NULL ? capacity != 0 : capacity < 0) {
+        *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR;
+        return -1;
+    }
+    UnicodeString result = ((const DateTimePatternGenerator *)dtpg)->getFieldDisplayName(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) {
index 5712edbb9ff60721a298c35f45ac521b49399720..b19f22282efb9dad50626c6bb69afd6c417d42e0 100644 (file)
@@ -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);
index 76baa3da58940fe22c2c3d78ed50279399c552da..beae756d87ddd81490ee78913b2f2ac0c5d46ac6 100644 (file)
@@ -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
index 8895c508acc18f88e243ea8d0a650223808d249a..a594560290e5977030f7211eb023f0f300fbeffd 100644 (file)
@@ -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