]> granicus.if.org Git - icu/commitdiff
ICU-13306 Can not get and set rounding attributes for RBNF with C API
authorGeorge Rhoten <grhoten@users.noreply.github.com>
Thu, 17 Aug 2017 23:21:03 +0000 (23:21 +0000)
committerGeorge Rhoten <grhoten@users.noreply.github.com>
Thu, 17 Aug 2017 23:21:03 +0000 (23:21 +0000)
X-SVN-Rev: 40343

icu4c/source/i18n/rbnf.cpp
icu4c/source/i18n/unicode/rbnf.h
icu4c/source/i18n/unum.cpp
icu4c/source/test/cintltst/cnumtst.c

index b046887ab2eca094e27fcd22f232dade69dfa935..81bc50f911fb768f5a5e7ec8f88113d4eff7aa46 100644 (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 */
index 3d9301bd71cac7139e9c22a0dc385b85c6491ebb..98e786008f336a97291e3d538677bf6f2fd448d2 100644 (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;
index 11c2f8c8337eddbb0c41551d831568120c5d6a83..23861d375f78081e8eb6b020039b1d6bd3515323 100644 (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
index e419fc28921b0be4a9677bd3f5de3772353d779e..b9a9bee48776225b41e0aef3eaf972dde350392a 100644 (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