ICU-13306 Can not get and set rounding attributes for RBNF with C API

X-SVN-Rev: 40343
This commit is contained in:
George Rhoten 2017-08-17 23:21:03 +00:00
parent 408afcee77
commit e3ac9c5561
4 changed files with 189 additions and 29 deletions

View File

@ -687,6 +687,7 @@ RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description,
, decimalFormatSymbols(NULL)
, defaultInfinityRule(NULL)
, defaultNaNRule(NULL)
, roundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)
, lenient(FALSE)
, lenientParseRules(NULL)
, localizations(NULL)
@ -711,6 +712,7 @@ RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description,
, decimalFormatSymbols(NULL)
, defaultInfinityRule(NULL)
, defaultNaNRule(NULL)
, roundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)
, lenient(FALSE)
, lenientParseRules(NULL)
, localizations(NULL)
@ -735,6 +737,7 @@ RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description,
, decimalFormatSymbols(NULL)
, defaultInfinityRule(NULL)
, defaultNaNRule(NULL)
, roundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)
, lenient(FALSE)
, lenientParseRules(NULL)
, localizations(NULL)
@ -758,6 +761,7 @@ RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description,
, decimalFormatSymbols(NULL)
, defaultInfinityRule(NULL)
, defaultNaNRule(NULL)
, roundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)
, lenient(FALSE)
, lenientParseRules(NULL)
, localizations(NULL)
@ -782,6 +786,7 @@ RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description,
, decimalFormatSymbols(NULL)
, defaultInfinityRule(NULL)
, defaultNaNRule(NULL)
, roundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)
, lenient(FALSE)
, lenientParseRules(NULL)
, localizations(NULL)
@ -803,6 +808,7 @@ RuleBasedNumberFormat::RuleBasedNumberFormat(URBNFRuleSetTag tag, const Locale&
, decimalFormatSymbols(NULL)
, defaultInfinityRule(NULL)
, defaultNaNRule(NULL)
, roundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)
, lenient(FALSE)
, lenientParseRules(NULL)
, localizations(NULL)
@ -869,6 +875,7 @@ RuleBasedNumberFormat::RuleBasedNumberFormat(const RuleBasedNumberFormat& rhs)
, decimalFormatSymbols(NULL)
, defaultInfinityRule(NULL)
, defaultNaNRule(NULL)
, roundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)
, lenient(FALSE)
, lenientParseRules(NULL)
, localizations(NULL)
@ -898,6 +905,7 @@ RuleBasedNumberFormat::operator=(const RuleBasedNumberFormat& rhs)
setDecimalFormatSymbols(*rhs.getDecimalFormatSymbols());
init(rhs.originalDescription, rhs.localizations ? rhs.localizations->ref() : NULL, perror, status);
setDefaultRuleSet(rhs.getDefaultRuleSetName(), status);
setRoundingMode(rhs.getRoundingMode());
capitalizationInfoSet = rhs.capitalizationInfoSet;
capitalizationForUIListMenu = rhs.capitalizationForUIListMenu;
@ -1195,7 +1203,7 @@ RuleBasedNumberFormat::format(double number,
int32_t startPos = toAppendTo.length();
UErrorCode status = U_ZERO_ERROR;
if (defaultRuleSet) {
defaultRuleSet->format(number, toAppendTo, toAppendTo.length(), 0, status);
format(number, *defaultRuleSet, toAppendTo, status);
}
return adjustForCapitalizationContext(startPos, toAppendTo, status);
}
@ -1248,15 +1256,31 @@ RuleBasedNumberFormat::format(double number,
} else {
NFRuleSet *rs = findRuleSet(ruleSetName, status);
if (rs) {
int32_t startPos = toAppendTo.length();
rs->format(number, toAppendTo, toAppendTo.length(), 0, status);
adjustForCapitalizationContext(startPos, toAppendTo, status);
format(number, *rs, toAppendTo, status);
}
}
}
return toAppendTo;
}
void
RuleBasedNumberFormat::format(double number,
NFRuleSet& rs,
UnicodeString& toAppendTo,
UErrorCode& status) const
{
int32_t startPos = toAppendTo.length();
if (getRoundingMode() != DecimalFormat::ERoundingMode::kRoundUnnecessary && !uprv_isNaN(number) && !uprv_isInfinite(number)) {
DigitList digitList;
digitList.set(number);
digitList.setRoundingMode(getRoundingMode());
digitList.roundFixedPoint(getMaximumFractionDigits());
number = digitList.getDouble();
}
rs.format(number, toAppendTo, toAppendTo.length(), 0, status);
adjustForCapitalizationContext(startPos, toAppendTo, status);
}
/**
* Bottleneck through which all the public format() methods
* that take a long pass. By the time we get here, we know
@ -1959,6 +1983,23 @@ RuleBasedNumberFormat::createPluralFormat(UPluralType pluralType,
return new PluralFormat(locale, pluralType, pattern, status);
}
/**
* Get the rounding mode.
* @return A rounding mode
*/
DecimalFormat::ERoundingMode RuleBasedNumberFormat::getRoundingMode() const {
return roundingMode;
}
/**
* Set the rounding mode. This has no effect unless the rounding
* increment is greater than zero.
* @param roundingMode A rounding mode
*/
void RuleBasedNumberFormat::setRoundingMode(DecimalFormat::ERoundingMode roundingMode) {
this->roundingMode = roundingMode;
}
U_NAMESPACE_END
/* U_HAVE_RBNF */

View File

@ -30,6 +30,7 @@
#define U_HAVE_RBNF 1
#include "unicode/dcfmtsym.h"
#include "unicode/decimfmt.h"
#include "unicode/fmtable.h"
#include "unicode/locid.h"
#include "unicode/numfmt.h"
@ -1010,6 +1011,22 @@ public:
*/
virtual void setContext(UDisplayContext value, UErrorCode& status);
#ifndef U_HIDE_DRAFT_API
/**
* Get the rounding mode.
* @return A rounding mode
* @draft ICU 60
*/
virtual DecimalFormat::ERoundingMode getRoundingMode(void) const;
/**
* Set the rounding mode.
* @param roundingMode A rounding mode
* @draft ICU 60
*/
virtual void setRoundingMode(DecimalFormat::ERoundingMode roundingMode);
#endif /* U_HIDE_DRAFT_API */
public:
/**
* ICU "poor man's RTTI", returns a UClassID for this class.
@ -1059,7 +1076,6 @@ private:
void dispose();
void stripWhitespace(UnicodeString& src);
void initDefaultRuleSet();
void format(double number, NFRuleSet& ruleSet);
NFRuleSet* findRuleSet(const UnicodeString& name, UErrorCode& status) const;
/* friend access */
@ -1079,6 +1095,7 @@ private:
PluralFormat *createPluralFormat(UPluralType pluralType, const UnicodeString &pattern, UErrorCode& status) const;
UnicodeString& adjustForCapitalizationContext(int32_t startPos, UnicodeString& currentResult, UErrorCode& status) const;
UnicodeString& format(int64_t number, NFRuleSet *ruleSet, UnicodeString& toAppendTo, UErrorCode& status) const;
void format(double number, NFRuleSet& rs, UnicodeString& toAppendTo, UErrorCode& status) const;
private:
NFRuleSet **ruleSets;
@ -1090,6 +1107,7 @@ private:
DecimalFormatSymbols* decimalFormatSymbols;
NFRule *defaultInfinityRule;
NFRule *defaultNaNRule;
DecimalFormat::ERoundingMode roundingMode;
UBool lenient;
UnicodeString* lenientParseRules;
LocalizationInfo* localizations;

View File

@ -507,20 +507,48 @@ U_CAPI int32_t U_EXPORT2
unum_getAttribute(const UNumberFormat* fmt,
UNumberFormatAttribute attr)
{
const NumberFormat* nf = reinterpret_cast<const NumberFormat*>(fmt);
if ( attr == UNUM_LENIENT_PARSE ) {
// Supported for all subclasses
return nf->isLenient();
}
const NumberFormat* nf = reinterpret_cast<const NumberFormat*>(fmt);
if (attr == UNUM_LENIENT_PARSE) {
// Supported for all subclasses
return nf->isLenient();
}
else if (attr == UNUM_MAX_INTEGER_DIGITS) {
return nf->getMaximumIntegerDigits();
}
else if (attr == UNUM_MIN_INTEGER_DIGITS) {
return nf->getMinimumIntegerDigits();
}
else if (attr == UNUM_INTEGER_DIGITS) {
// TODO: what should this return?
return nf->getMinimumIntegerDigits();
}
else if (attr == UNUM_MAX_FRACTION_DIGITS) {
return nf->getMaximumFractionDigits();
}
else if (attr == UNUM_MIN_FRACTION_DIGITS) {
return nf->getMinimumFractionDigits();
}
else if (attr == UNUM_FRACTION_DIGITS) {
// TODO: what should this return?
return nf->getMinimumFractionDigits();
}
// The remaining attributea are only supported for DecimalFormat
const DecimalFormat* df = dynamic_cast<const DecimalFormat*>(nf);
if (df != NULL) {
UErrorCode ignoredStatus = U_ZERO_ERROR;
return df->getAttribute( attr, ignoredStatus );
}
// DecimalFormat specific attributes
const DecimalFormat* df = dynamic_cast<const DecimalFormat*>(nf);
if (df != NULL) {
UErrorCode ignoredStatus = U_ZERO_ERROR;
return df->getAttribute(attr, ignoredStatus);
}
return -1;
// RuleBasedNumberFormat specific attributes
const RuleBasedNumberFormat* rbnf = dynamic_cast<const RuleBasedNumberFormat*>(nf);
if (rbnf != NULL) {
if (attr == UNUM_ROUNDING_MODE) {
return rbnf->getRoundingMode();
}
}
return -1;
}
U_CAPI void U_EXPORT2
@ -528,18 +556,47 @@ unum_setAttribute( UNumberFormat* fmt,
UNumberFormatAttribute attr,
int32_t newValue)
{
NumberFormat* nf = reinterpret_cast<NumberFormat*>(fmt);
if ( attr == UNUM_LENIENT_PARSE ) {
// Supported for all subclasses
// keep this here as the class may not be a DecimalFormat
return nf->setLenient(newValue != 0);
}
// The remaining attributea are only supported for DecimalFormat
DecimalFormat* df = dynamic_cast<DecimalFormat*>(nf);
if (df != NULL) {
UErrorCode ignoredStatus = U_ZERO_ERROR;
df->setAttribute(attr, newValue, ignoredStatus);
}
NumberFormat* nf = reinterpret_cast<NumberFormat*>(fmt);
if (attr == UNUM_LENIENT_PARSE) {
// Supported for all subclasses
// keep this here as the class may not be a DecimalFormat
return nf->setLenient(newValue != 0);
}
else if (attr == UNUM_MAX_INTEGER_DIGITS) {
return nf->setMaximumIntegerDigits(newValue);
}
else if (attr == UNUM_MIN_INTEGER_DIGITS) {
return nf->setMinimumIntegerDigits(newValue);
}
else if (attr == UNUM_INTEGER_DIGITS) {
nf->setMinimumIntegerDigits(newValue);
return nf->setMaximumIntegerDigits(newValue);
}
else if (attr == UNUM_MAX_FRACTION_DIGITS) {
return nf->setMaximumFractionDigits(newValue);
}
else if (attr == UNUM_MIN_FRACTION_DIGITS) {
return nf->setMinimumFractionDigits(newValue);
}
else if (attr == UNUM_FRACTION_DIGITS) {
nf->setMinimumFractionDigits(newValue);
return nf->setMaximumFractionDigits(newValue);
}
// DecimalFormat specific attributes
DecimalFormat* df = dynamic_cast<DecimalFormat*>(nf);
if (df != NULL) {
UErrorCode ignoredStatus = U_ZERO_ERROR;
df->setAttribute(attr, newValue, ignoredStatus);
}
// RuleBasedNumberFormat specific attributes
RuleBasedNumberFormat* rbnf = dynamic_cast<RuleBasedNumberFormat*>(nf);
if (rbnf != NULL) {
if (attr == UNUM_ROUNDING_MODE) {
return rbnf->setRoundingMode((DecimalFormat::ERoundingMode)newValue);
}
}
}
U_CAPI double U_EXPORT2

View File

@ -64,6 +64,7 @@ static void TestCurrFmtNegSameAsPositive(void);
static void TestVariousStylesAndAttributes(void);
static void TestParseCurrPatternWithDecStyle(void);
static void TestFormatForFields(void);
static void TestRBNFRounding(void);
#define TESTCASE(x) addTest(root, &x, "tsformat/cnumtst/" #x)
@ -79,6 +80,7 @@ void addNumForTest(TestNode** root)
TESTCASE(TestCurrencyRegression);
TESTCASE(TestTextAttributeCrash);
TESTCASE(TestRBNFFormat);
TESTCASE(TestRBNFRounding);
TESTCASE(TestNBSPInPattern);
TESTCASE(TestInt64Parse);
TESTCASE(TestParseZero);
@ -1791,6 +1793,48 @@ static void TestRBNFFormat() {
}
}
static void TestRBNFRounding() {
UChar fmtbuf[FORMAT_BUF_CAPACITY];
UChar expectedBuf[FORMAT_BUF_CAPACITY];
int32_t len;
UErrorCode status = U_ZERO_ERROR;
UNumberFormat* fmt = unum_open(UNUM_SPELLOUT, NULL, 0, "en_US", NULL, &status);
if (U_FAILURE(status)) {
log_err_status(status, "unable to open spellout -> %s\n", u_errorName(status));
return;
}
len = unum_formatDouble(fmt, 10.123456789, fmtbuf, FORMAT_BUF_CAPACITY, NULL, &status);
if (U_FAILURE(status)) {
log_err_status(status, "unum_formatDouble 10.123456789 failed with %s\n", u_errorName(status));
}
u_uastrcpy(expectedBuf, "ten point one two three four five six seven eight nine");
if (u_strcmp(expectedBuf, fmtbuf) != 0) {
log_err("Wrong result for unrounded value\n");
}
unum_setAttribute(fmt, UNUM_MAX_FRACTION_DIGITS, 3);
if (unum_getAttribute(fmt, UNUM_MAX_FRACTION_DIGITS) != 3) {
log_err("UNUM_MAX_FRACTION_DIGITS was incorrectly ignored -> %d\n", unum_getAttribute(fmt, UNUM_MAX_FRACTION_DIGITS));
}
if (unum_getAttribute(fmt, UNUM_ROUNDING_MODE) != UNUM_ROUND_UNNECESSARY) {
log_err("UNUM_ROUNDING_MODE was set -> %d\n", unum_getAttribute(fmt, UNUM_ROUNDING_MODE));
}
unum_setAttribute(fmt, UNUM_ROUNDING_MODE, UNUM_ROUND_HALFUP);
if (unum_getAttribute(fmt, UNUM_ROUNDING_MODE) != UNUM_ROUND_HALFUP) {
log_err("UNUM_ROUNDING_MODE was not set -> %d\n", unum_getAttribute(fmt, UNUM_ROUNDING_MODE));
}
len = unum_formatDouble(fmt, 10.123456789, fmtbuf, FORMAT_BUF_CAPACITY, NULL, &status);
if (U_FAILURE(status)) {
log_err_status(status, "unum_formatDouble 10.123456789 failed with %s\n", u_errorName(status));
}
u_uastrcpy(expectedBuf, "ten point one two three");
if (u_strcmp(expectedBuf, fmtbuf) != 0) {
char temp[512];
u_austrcpy(temp, fmtbuf);
log_err("Wrong result for rounded value. Got: %s\n", temp);
}
unum_close(fmt);
}
static void TestCurrencyRegression(void) {
/*
I've found a case where unum_parseDoubleCurrency is not doing what I