From 867e55f0cb05edbb981565afabc1aa8a541415d1 Mon Sep 17 00:00:00 2001 From: Scott Russell Date: Sat, 30 Aug 2014 21:53:10 +0000 Subject: [PATCH] ICU-10970 Support required decimal point X-SVN-Rev: 36300 --- icu4c/source/i18n/decimfmt.cpp | 46 +++++++++++++++++++++ icu4c/source/i18n/unicode/decimfmt.h | 20 +++++++++ icu4c/source/i18n/unicode/unum.h | 10 +++++ icu4c/source/test/intltest/dcfmapts.cpp | 54 +++++++++++++++++++++++++ icu4c/source/test/intltest/dcfmapts.h | 1 + 5 files changed, 131 insertions(+) diff --git a/icu4c/source/i18n/decimfmt.cpp b/icu4c/source/i18n/decimfmt.cpp index ade906573d6..da1f8d29f37 100644 --- a/icu4c/source/i18n/decimfmt.cpp +++ b/icu4c/source/i18n/decimfmt.cpp @@ -2944,6 +2944,18 @@ UBool DecimalFormat::subparse(const UnicodeString& text, } } + // if we didn't see a decimal and it is required, check to see if the pattern had one + if(!sawDecimal && isDecimalPatternMatchRequired()) + { + if(fFormatPattern.indexOf(DecimalFormatSymbols::kDecimalSeparatorSymbol) != 0) + { + parsePosition.setIndex(oldStart); + parsePosition.setErrorIndex(position); + debug("decimal point match required fail!"); + return FALSE; + } + } + if (backup != -1) { position = backup; @@ -3057,6 +3069,20 @@ printf("PP -> %d, SLOW = [%s]! pp=%d, os=%d, err=%s\n", position, parsedNum.d parsePosition.setErrorIndex(position); return FALSE; } + + // check if we missed a required decimal point + if(fastParseOk && isDecimalPatternMatchRequired()) + { + if(fFormatPattern.indexOf(DecimalFormatSymbols::kDecimalSeparatorSymbol) != 0) + { + parsePosition.setIndex(oldStart); + parsePosition.setErrorIndex(position); + debug("decimal point match required fail!"); + return FALSE; + } + } + + return TRUE; } @@ -4168,6 +4194,24 @@ DecimalFormat::setDecimalSeparatorAlwaysShown(UBool newValue) #endif } +//------------------------------------------------------------------------------ +// Checks if decimal point pattern match is required +UBool +DecimalFormat::isDecimalPatternMatchRequired(void) const +{ + return fBoolFlags.contains(UNUM_PARSE_DECIMAL_MARK_REQUIRED); +} + +//------------------------------------------------------------------------------ +// Checks if decimal point pattern match is required + +void +DecimalFormat::setDecimalPatternMatchRequired(UBool newValue) +{ + fBoolFlags.set(UNUM_PARSE_DECIMAL_MARK_REQUIRED, newValue); +} + + //------------------------------------------------------------------------------ // Emits the pattern of this DecimalFormat instance. @@ -5480,6 +5524,7 @@ DecimalFormat& DecimalFormat::setAttribute( UNumberFormatAttribute attr, /* These are stored in fBoolFlags */ case UNUM_PARSE_NO_EXPONENT: case UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS: + case UNUM_PARSE_DECIMAL_MARK_REQUIRED: if(!fBoolFlags.isValidValue(newValue)) { status = U_ILLEGAL_ARGUMENT_ERROR; } else { @@ -5567,6 +5612,7 @@ int32_t DecimalFormat::getAttribute( UNumberFormatAttribute attr, /* These are stored in fBoolFlags */ case UNUM_PARSE_NO_EXPONENT: case UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS: + case UNUM_PARSE_DECIMAL_MARK_REQUIRED: return fBoolFlags.get(attr); case UNUM_SCALE: diff --git a/icu4c/source/i18n/unicode/decimfmt.h b/icu4c/source/i18n/unicode/decimfmt.h index 9c22dc6c0f8..116d65c93fe 100644 --- a/icu4c/source/i18n/unicode/decimfmt.h +++ b/icu4c/source/i18n/unicode/decimfmt.h @@ -1631,6 +1631,26 @@ public: */ virtual void setDecimalSeparatorAlwaysShown(UBool newValue); + /** + * Allows you to get the parse behavior of the pattern decimal mark. + * + * @return TRUE if input must contain a match to decimal mark in pattern + * @draft ICU 54 + */ + UBool isDecimalPatternMatchRequired(void) const; + + /** + * Allows you to set the behavior of the pattern decimal mark. + * + * if TRUE, the input must have a decimal mark if one was specified in the pattern. When + * FALSE the decimal mark may be omitted from the input. + * + * @param newValue set TRUE if input must contain a match to decimal mark in pattern + * @draft ICU 54 + */ + virtual void setDecimalPatternMatchRequired(UBool newValue); + + /** * Synthesizes a pattern string that represents the current state * of this Format object. diff --git a/icu4c/source/i18n/unicode/unum.h b/icu4c/source/i18n/unicode/unum.h index f248c17871a..27abadeb211 100644 --- a/icu4c/source/i18n/unicode/unum.h +++ b/icu4c/source/i18n/unicode/unum.h @@ -940,6 +940,16 @@ typedef enum UNumberFormatAttribute { */ UNUM_PARSE_NO_EXPONENT, + /** + * if this attribute is set to 1, specifies that, if the pattern contains a + * decimal mark the input is required to have one. If this attribute is set to 0, + * specifies that input does not have to contain a decimal mark. + * Has no effect on formatting. + * Default: 0 (unset) + * @draft ICU 54 + */ + UNUM_PARSE_DECIMAL_MARK_REQUIRED, + /* The following cannot be #ifndef U_HIDE_INTERNAL_API, needed in .h file variable declararions */ /** Limit of boolean attributes. * @internal */ diff --git a/icu4c/source/test/intltest/dcfmapts.cpp b/icu4c/source/test/intltest/dcfmapts.cpp index 0e651de5017..e98cdd8d749 100644 --- a/icu4c/source/test/intltest/dcfmapts.cpp +++ b/icu4c/source/test/intltest/dcfmapts.cpp @@ -79,6 +79,12 @@ void IntlTestDecimalFormatAPI::runIndexedTest( int32_t index, UBool exec, const TestBadFastpath(); } break; + case 7: name = "TestRequiredDecimalPoint"; + if(exec) { + logln((UnicodeString)"TestRequiredDecimalPoint ---"); + TestRequiredDecimalPoint(); + } + break; default: name = ""; break; } } @@ -825,4 +831,52 @@ void IntlTestDecimalFormatAPI::TestBadFastpath() { assertEquals("Format 1234 w/ grouping", "1,234", df->format(1234, fmt)); } +void IntlTestDecimalFormatAPI::TestRequiredDecimalPoint() { + UErrorCode status = U_ZERO_ERROR; + UnicodeString text("99"); + double expected = 99; + double whatIGot = 0.0; + Formattable result1; + UnicodeString pat1("##.0000"); + UnicodeString pat2("00.0"); + + LocalPointer df(new DecimalFormat(pat1, status)); + if (U_FAILURE(status)) { + dataerrln("Error creating new DecimalFormat - %s", u_errorName(status)); + return; + } + + status = U_ZERO_ERROR; + df->applyPattern(pat1, status); + if(U_FAILURE(status)) { + errln((UnicodeString)"ERROR: applyPattern() failed"); + } + df->parse(text, result1, status); + if(U_FAILURE(status)) { + errln((UnicodeString)"ERROR: parse() failed"); + } + df->setDecimalPatternMatchRequired(TRUE); + df->parse(text, result1, status); + if(U_SUCCESS(status)) { + errln((UnicodeString)"ERROR: unexpected parse()"); + } + + + status = U_ZERO_ERROR; + df->applyPattern(pat2, status); + df->setDecimalPatternMatchRequired(FALSE); + if(U_FAILURE(status)) { + errln((UnicodeString)"ERROR: applyPattern(2) failed"); + } + df->parse(text, result1, status); + if(U_FAILURE(status)) { + errln((UnicodeString)"ERROR: parse(2) failed - " + u_errorName(status)); + } + df->setDecimalPatternMatchRequired(TRUE); + df->parse(text, result1, status); + if(U_SUCCESS(status)) { + errln((UnicodeString)"ERROR: unexpected parse(2)"); + } +} + #endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/icu4c/source/test/intltest/dcfmapts.h b/icu4c/source/test/intltest/dcfmapts.h index 10be89fab25..32e208a36ad 100644 --- a/icu4c/source/test/intltest/dcfmapts.h +++ b/icu4c/source/test/intltest/dcfmapts.h @@ -30,6 +30,7 @@ public: void TestScale(); void TestFixedDecimal(); void TestBadFastpath(); + void TestRequiredDecimalPoint(); private: /*Helper functions */ void verify(const UnicodeString& message, const UnicodeString& got, double expected); -- 2.40.0