From e3ac9c5561984a70e149deb9d7ea04854f75cb98 Mon Sep 17 00:00:00 2001 From: George Rhoten Date: Thu, 17 Aug 2017 23:21:03 +0000 Subject: [PATCH] ICU-13306 Can not get and set rounding attributes for RBNF with C API X-SVN-Rev: 40343 --- icu4c/source/i18n/rbnf.cpp | 49 ++++++++++++- icu4c/source/i18n/unicode/rbnf.h | 20 ++++- icu4c/source/i18n/unum.cpp | 105 +++++++++++++++++++++------ icu4c/source/test/cintltst/cnumtst.c | 44 +++++++++++ 4 files changed, 189 insertions(+), 29 deletions(-) diff --git a/icu4c/source/i18n/rbnf.cpp b/icu4c/source/i18n/rbnf.cpp index b046887ab2e..81bc50f911f 100644 --- a/icu4c/source/i18n/rbnf.cpp +++ b/icu4c/source/i18n/rbnf.cpp @@ -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 */ diff --git a/icu4c/source/i18n/unicode/rbnf.h b/icu4c/source/i18n/unicode/rbnf.h index 3d9301bd71c..98e786008f3 100644 --- a/icu4c/source/i18n/unicode/rbnf.h +++ b/icu4c/source/i18n/unicode/rbnf.h @@ -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; diff --git a/icu4c/source/i18n/unum.cpp b/icu4c/source/i18n/unum.cpp index 11c2f8c8337..23861d375f7 100644 --- a/icu4c/source/i18n/unum.cpp +++ b/icu4c/source/i18n/unum.cpp @@ -507,20 +507,48 @@ U_CAPI int32_t U_EXPORT2 unum_getAttribute(const UNumberFormat* fmt, UNumberFormatAttribute attr) { - const NumberFormat* nf = reinterpret_cast(fmt); - if ( attr == UNUM_LENIENT_PARSE ) { - // Supported for all subclasses - return nf->isLenient(); - } + const NumberFormat* nf = reinterpret_cast(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(nf); - if (df != NULL) { - UErrorCode ignoredStatus = U_ZERO_ERROR; - return df->getAttribute( attr, ignoredStatus ); - } + // DecimalFormat specific attributes + const DecimalFormat* df = dynamic_cast(nf); + if (df != NULL) { + UErrorCode ignoredStatus = U_ZERO_ERROR; + return df->getAttribute(attr, ignoredStatus); + } - return -1; + // RuleBasedNumberFormat specific attributes + const RuleBasedNumberFormat* rbnf = dynamic_cast(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(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(nf); - if (df != NULL) { - UErrorCode ignoredStatus = U_ZERO_ERROR; - df->setAttribute(attr, newValue, ignoredStatus); - } + NumberFormat* nf = reinterpret_cast(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(nf); + if (df != NULL) { + UErrorCode ignoredStatus = U_ZERO_ERROR; + df->setAttribute(attr, newValue, ignoredStatus); + } + + // RuleBasedNumberFormat specific attributes + RuleBasedNumberFormat* rbnf = dynamic_cast(nf); + if (rbnf != NULL) { + if (attr == UNUM_ROUNDING_MODE) { + return rbnf->setRoundingMode((DecimalFormat::ERoundingMode)newValue); + } + } } U_CAPI double U_EXPORT2 diff --git a/icu4c/source/test/cintltst/cnumtst.c b/icu4c/source/test/cintltst/cnumtst.c index e419fc28921..b9a9bee4877 100644 --- a/icu4c/source/test/cintltst/cnumtst.c +++ b/icu4c/source/test/cintltst/cnumtst.c @@ -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 -- 2.40.0