ICU-12684 Add unum_formatDoubleForFields and test

X-SVN-Rev: 39565
This commit is contained in:
Peter Edberg 2017-01-16 19:48:33 +00:00
parent 26d15e5eca
commit 32ca386c27
3 changed files with 190 additions and 0 deletions

View File

@ -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.

View File

@ -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,

View File

@ -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 */