From: Peter Edberg Date: Mon, 16 Jan 2017 19:48:33 +0000 (+0000) Subject: ICU-12684 Add unum_formatDoubleForFields and test X-Git-Tag: milestone-59-0-1~28 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=32ca386c27abc97ad7ca4cf9e677962a9e4a8e40;p=icu ICU-12684 Add unum_formatDoubleForFields and test X-SVN-Rev: 39565 --- diff --git a/icu4c/source/i18n/unicode/unum.h b/icu4c/source/i18n/unicode/unum.h index 34d54427f05..17a33f0853e 100644 --- a/icu4c/source/i18n/unicode/unum.h +++ b/icu4c/source/i18n/unicode/unum.h @@ -25,6 +25,7 @@ #include "unicode/parseerr.h" #include "unicode/uformattable.h" #include "unicode/udisplaycontext.h" +#include "unicode/ufieldpositer.h" /** * \file @@ -553,6 +554,59 @@ unum_formatDouble( const UNumberFormat* fmt, UFieldPosition *pos, /* 0 if ignore */ UErrorCode* status); +#ifndef U_HIDE_DRAFT_API +/** +* Format a double using a UNumberFormat according to the UNumberFormat's locale, +* and initialize a UFieldPositionIterator that enumerates the subcomponents of +* the resulting string. +* +* @param format +* The formatter to use. +* @param number +* The number to format. +* @param result +* A pointer to a buffer to receive the NULL-terminated formatted +* number. If the formatted number fits into dest but cannot be +* NULL-terminated (length == resultLength) then the error code is set +* to U_STRING_NOT_TERMINATED_WARNING. If the formatted number doesn't +* fit into result then the error code is set to +* U_BUFFER_OVERFLOW_ERROR. +* @param resultLength +* The maximum size of result. +* @param fpositer +* A pointer to a UFieldPositionIterator created by {@link #ufieldpositer_open} +* (may be NULL if field position information is not needed, but in this +* case it's preferable to use {@link #unum_formatDouble}). Iteration +* information already present in the UFieldPositionIterator is deleted, +* and the iterator is reset to apply to the fields in the formatted +* string created by this function call. The field values and indexes +* returned by {@link #ufieldpositer_next} represent fields denoted by +* the UNumberFormatFields enum. Fields are not returned in a guaranteed +* order. Fields cannot overlap, but they may nest. For example, 1234 +* could format as "1,234" which might consist of a grouping separator +* field for ',' and an integer field encompassing the entire string. +* @param status +* A pointer to an UErrorCode to receive any errors +* @return +* The total buffer size needed; if greater than resultLength, the +* output was truncated. +* @see unum_formatDouble +* @see unum_parse +* @see unum_parseDouble +* @see UFieldPositionIterator +* @see UNumberFormatFields +* @draft ICU 59 +*/ +U_DRAFT int32_t U_EXPORT2 +unum_formatDoubleForFields(const UNumberFormat* format, + double number, + UChar* result, + int32_t resultLength, + UFieldPositionIterator* fpositer, + UErrorCode* status); + +#endif /* U_HIDE_DRAFT_API */ + /** * Format a decimal number using a UNumberFormat. * The number will be formatted according to the UNumberFormat's locale. diff --git a/icu4c/source/i18n/unum.cpp b/icu4c/source/i18n/unum.cpp index 01170b4c1d0..d02ba0229ee 100644 --- a/icu4c/source/i18n/unum.cpp +++ b/icu4c/source/i18n/unum.cpp @@ -247,6 +247,33 @@ unum_formatDouble( const UNumberFormat* fmt, return res.extract(result, resultLength, *status); } +U_CAPI int32_t U_EXPORT2 +unum_formatDoubleForFields(const UNumberFormat* format, + double number, + UChar* result, + int32_t resultLength, + UFieldPositionIterator* fpositer, + UErrorCode* status) +{ + if (U_FAILURE(*status)) + return -1; + + if (result == NULL ? resultLength != 0 : resultLength < 0) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return -1; + } + + UnicodeString res; + if (result != NULL) { + // NULL destination for pure preflighting: empty dummy string + // otherwise, alias the destination buffer + res.setTo(result, 0, resultLength); + } + + ((const NumberFormat*)format)->format(number, res, (FieldPositionIterator*)fpositer, *status); + + return res.extract(result, resultLength, *status); +} U_CAPI int32_t U_EXPORT2 unum_formatDecimal(const UNumberFormat* fmt, diff --git a/icu4c/source/test/cintltst/cnumtst.c b/icu4c/source/test/cintltst/cnumtst.c index 15fec24313d..64e60c385d9 100644 --- a/icu4c/source/test/cintltst/cnumtst.c +++ b/icu4c/source/test/cintltst/cnumtst.c @@ -63,6 +63,7 @@ static void TestCurrencyUsage(void); static void TestCurrFmtNegSameAsPositive(void); static void TestVariousStylesAndAttributes(void); static void TestParseCurrPatternWithDecStyle(void); +static void TestFormatForFields(void); #define TESTCASE(x) addTest(root, &x, "tsformat/cnumtst/" #x) @@ -93,6 +94,7 @@ void addNumForTest(TestNode** root) TESTCASE(TestCurrFmtNegSameAsPositive); TESTCASE(TestVariousStylesAndAttributes); TESTCASE(TestParseCurrPatternWithDecStyle); + TESTCASE(TestFormatForFields); } /* test Parse int 64 */ @@ -2887,4 +2889,111 @@ static void TestParseCurrPatternWithDecStyle() { } } +/* + * Ticket #12684 + * Test unum_formatDoubleForFields (and UFieldPositionIterator) + */ + +typedef struct { + int32_t field; + int32_t beginPos; + int32_t endPos; +} FieldsData; + +typedef struct { + const char * locale; + UNumberFormatStyle style; + double value; + const FieldsData * expectedFields; +} FormatForFieldsItem; + +static const UChar patNoFields[] = { 0x0027, 0x0078, 0x0027, 0 }; /* "'x'", for UNUM_PATTERN_DECIMAL */ + + +/* "en_US", UNUM_CURRENCY, 123456.0 : "¤#,##0.00" => "$123,456.00" */ +static const FieldsData fields_en_CURR[] = { + { UNUM_CURRENCY_FIELD /*7*/, 0, 1 }, + { UNUM_GROUPING_SEPARATOR_FIELD /*6*/, 4, 5 }, + { UNUM_INTEGER_FIELD /*0*/, 1, 8 }, + { UNUM_DECIMAL_SEPARATOR_FIELD /*2*/, 8, 9 }, + { UNUM_FRACTION_FIELD /*1*/, 9, 11 }, + { -1, -1, -1 }, +}; +/* "en_US", UNUM_PERCENT, -34 : "#,##0%" => "-34%" */ +static const FieldsData fields_en_PRCT[] = { + { UNUM_SIGN_FIELD /*10*/, 0, 1 }, + { UNUM_INTEGER_FIELD /*0*/, 1, 3 }, + { UNUM_PERCENT_FIELD /*8*/, 3, 4 }, + { -1, -1, -1 }, +}; +/* "fr_FR", UNUM_CURRENCY, 123456.0 : "#,##0.00 ¤" => "123,456.00 €" */ +static const FieldsData fields_fr_CURR[] = { + { UNUM_GROUPING_SEPARATOR_FIELD /*6*/, 3, 4 }, + { UNUM_INTEGER_FIELD /*0*/, 0, 7 }, + { UNUM_DECIMAL_SEPARATOR_FIELD /*2*/, 7, 8 }, + { UNUM_FRACTION_FIELD /*1*/, 8, 10 }, + { UNUM_CURRENCY_FIELD /*7*/, 11, 12 }, + { -1, -1, -1 }, +}; +/* "en_US", UNUM_PATTERN_DECIMAL, 12.0 : "'x'" => "x12" */ +static const FieldsData fields_en_PATN[] = { + { UNUM_INTEGER_FIELD /*0*/, 1, 3 }, + { -1, -1, -1 }, +}; + +static const FormatForFieldsItem fffItems[] = { + { "en_US", UNUM_CURRENCY_STANDARD, 123456.0, fields_en_CURR }, + { "en_US", UNUM_PERCENT, -0.34, fields_en_PRCT }, + { "fr_FR", UNUM_CURRENCY_STANDARD, 123456.0, fields_fr_CURR }, + { "en_US", UNUM_PATTERN_DECIMAL, 12.0, fields_en_PATN }, + { NULL, (UNumberFormatStyle)0, 0, NULL }, +}; + +static void TestFormatForFields(void) { + UErrorCode status = U_ZERO_ERROR; + UFieldPositionIterator* fpositer = ufieldpositer_open(&status); + if ( U_FAILURE(status) ) { + log_err("ufieldpositer_open fails, status %s\n", u_errorName(status)); + } else { + const FormatForFieldsItem * itemPtr; + for (itemPtr = fffItems; itemPtr->locale != NULL; itemPtr++) { + UNumberFormat* unum; + status = U_ZERO_ERROR; + unum = (itemPtr->style == UNUM_PATTERN_DECIMAL)? + unum_open(itemPtr->style, patNoFields, -1, itemPtr->locale, NULL, &status): + unum_open(itemPtr->style, NULL, 0, itemPtr->locale, NULL, &status); + if ( U_FAILURE(status) ) { + log_data_err("unum_open fails for locale %s, style %d: status %s (Are you missing data?)\n", itemPtr->locale, itemPtr->style, u_errorName(status)); + } else { + UChar ubuf[kUBufSize]; + int32_t ulen = unum_formatDoubleForFields(unum, itemPtr->value, ubuf, kUBufSize, fpositer, &status); + if ( U_FAILURE(status) ) { + log_err("unum_formatDoubleForFields fails for locale %s, style %d: status %s\n", itemPtr->locale, itemPtr->style, u_errorName(status)); + } else { + const FieldsData * fptr; + int32_t field, beginPos, endPos; + for (fptr = itemPtr->expectedFields; TRUE; fptr++) { + field = ufieldpositer_next(fpositer, &beginPos, &endPos); + if (field != fptr->field || (field >= 0 && (beginPos != fptr->beginPos || endPos != fptr->endPos))) { + if (fptr->field >= 0) { + log_err("unum_formatDoubleForFields for locale %s as \"%s\"; expect field %d range %d-%d, get field %d range %d-%d\n", + itemPtr->locale, aescstrdup(ubuf, ulen), fptr->field, fptr->beginPos, fptr->endPos, field, beginPos, endPos); + } else { + log_err("unum_formatDoubleForFields for locale %s as \"%s\"; expect field < 0, get field %d range %d-%d\n", + itemPtr->locale, aescstrdup(ubuf, ulen), field, beginPos, endPos); + } + break; + } + if (field < 0) { + break; + } + } + } + unum_close(unum); + } + } + ufieldpositer_close(fpositer); + } +} + #endif /* #if !UCONFIG_NO_FORMATTING */