ICU-12740 getFieldDisplayName C/C++ initial implementation & C tests; TODO: C++ tests and Java imp

X-SVN-Rev: 40922
This commit is contained in:
Peter Edberg 2018-02-15 08:38:20 +00:00
parent e8bb1bb9c2
commit 7cf0adb29a
5 changed files with 229 additions and 28 deletions

View 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;
}
}

View 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) {

View 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);

View 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

View 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