From 05b2ae1381eddb72213fc07e1ce613863b164bd6 Mon Sep 17 00:00:00 2001 From: Peter Edberg Date: Sun, 19 Feb 2012 06:45:47 +0000 Subject: [PATCH] ICU-8697 C, change parseCurrency methods to return CurrencyAmount*, make draft ICU 49 X-SVN-Rev: 31440 --- icu4c/source/i18n/currfmt.cpp | 8 ++- icu4c/source/i18n/decimfmt.cpp | 47 +++++++------ icu4c/source/i18n/numfmt.cpp | 26 ++++---- icu4c/source/i18n/unicode/decimfmt.h | 32 ++++----- icu4c/source/i18n/unicode/numfmt.h | 26 ++++---- icu4c/source/i18n/unum.cpp | 54 ++++++++++----- icu4c/source/test/intltest/numfmtst.cpp | 88 +++++++++++++------------ 7 files changed, 159 insertions(+), 122 deletions(-) diff --git a/icu4c/source/i18n/currfmt.cpp b/icu4c/source/i18n/currfmt.cpp index 133497154b5..d3de2ab633d 100644 --- a/icu4c/source/i18n/currfmt.cpp +++ b/icu4c/source/i18n/currfmt.cpp @@ -1,6 +1,6 @@ /* ********************************************************************** -* Copyright (c) 2004-2010, International Business Machines +* Copyright (c) 2004-2012 International Business Machines * Corporation and others. All Rights Reserved. ********************************************************************** * Author: Alan Liu @@ -16,6 +16,7 @@ #include "currfmt.h" #include "unicode/numfmt.h" +#include "unicode/curramt.h" U_NAMESPACE_BEGIN @@ -62,7 +63,10 @@ void CurrencyFormat::parseObject(const UnicodeString& source, Formattable& result, ParsePosition& pos) const { - fmt->parseCurrency(source, result, pos); + CurrencyAmount* currAmt = fmt->parseCurrency(source, pos); + if (currAmt != NULL) { + result.adoptObject(currAmt); + } } UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CurrencyFormat) diff --git a/icu4c/source/i18n/decimfmt.cpp b/icu4c/source/i18n/decimfmt.cpp index 06b2043d7da..1b4b311b5db 100644 --- a/icu4c/source/i18n/decimfmt.cpp +++ b/icu4c/source/i18n/decimfmt.cpp @@ -1591,32 +1591,48 @@ void DecimalFormat::parse(const UnicodeString& text, Formattable& result, ParsePosition& parsePosition) const { - parse(text, result, parsePosition, FALSE); + parse(text, result, parsePosition, NULL); } -Formattable& DecimalFormat::parseCurrency(const UnicodeString& text, - Formattable& result, - ParsePosition& pos) const { - parse(text, result, pos, TRUE); - return result; +CurrencyAmount* DecimalFormat::parseCurrency(const UnicodeString& text, + ParsePosition& pos) const { + CurrencyAmount* currAmt = NULL; + Formattable parseResult; + int32_t start = pos.getIndex(); + UChar curbuf[4]; + parse(text, parseResult, pos, curbuf); + if (pos.getIndex() != start && pos.getErrorIndex() == -1) { + UErrorCode ec = U_ZERO_ERROR; + currAmt = new CurrencyAmount(parseResult, curbuf, ec); + if (U_FAILURE(ec) || currAmt == NULL) { + pos.setIndex(start); // indicate failure + if ( currAmt != NULL ) { + delete currAmt; + currAmt = NULL; + } + } + } + return currAmt; } /** - * Parses the given text as either a number or a currency amount. + * Parses the given text as a number, optionally providing a currency amount. * @param text the string to parse - * @param result output parameter for the result + * @param result output parameter for the numeric result. * @param parsePosition input-output position; on input, the * position within text to match; must have 0 <= pos.getIndex() < * text.length(); on output, the position after the last matched * character. If the parse fails, the position in unchanged upon * output. - * @param parseCurrency if true, a currency amount is parsed; - * otherwise a Number is parsed + * @param currency if non-NULL, it should point to a 4-UChar buffer. + * In this case the text is parsed as a currency format, and the + * ISO 4217 code for the parsed currency is put into the buffer. + * Otherwise the text is parsed as a non-currency format. */ void DecimalFormat::parse(const UnicodeString& text, Formattable& result, ParsePosition& parsePosition, - UBool parseCurrency) const { + UChar* currency) const { int32_t startIdx, backup; int32_t i = startIdx = backup = parsePosition.getIndex(); @@ -1658,8 +1674,6 @@ void DecimalFormat::parse(const UnicodeString& text, // status is used to record whether a number is infinite. UBool status[fgStatusLength]; - UChar curbuf[4]; - UChar* currency = parseCurrency ? curbuf : NULL; DigitList *digits = new DigitList; if (digits == NULL) { return; // no way to report error from here. @@ -1705,13 +1719,6 @@ void DecimalFormat::parse(const UnicodeString& text, } result.adoptDigitList(digits); } - - if (parseCurrency) { - UErrorCode ec = U_ZERO_ERROR; - Formattable n(result); - result.adoptObject(new CurrencyAmount(n, curbuf, ec)); - U_ASSERT(U_SUCCESS(ec)); // should always succeed - } } diff --git a/icu4c/source/i18n/numfmt.cpp b/icu4c/source/i18n/numfmt.cpp index d8540ae48b1..57ca29df5d0 100644 --- a/icu4c/source/i18n/numfmt.cpp +++ b/icu4c/source/i18n/numfmt.cpp @@ -1,6 +1,6 @@ /* ******************************************************************************* -* Copyright (C) 1997-2011, International Business Machines Corporation and * +* Copyright (C) 1997-2012, International Business Machines Corporation and * * others. All Rights Reserved. * ******************************************************************************* * @@ -623,27 +623,29 @@ NumberFormat::parse(const UnicodeString& text, } } -Formattable& NumberFormat::parseCurrency(const UnicodeString& text, - Formattable& result, - ParsePosition& pos) const { +CurrencyAmount* NumberFormat::parseCurrency(const UnicodeString& text, + ParsePosition& pos) const { // Default implementation only -- subclasses should override + CurrencyAmount* currAmt = NULL; + Formattable parseResult; int32_t start = pos.getIndex(); - parse(text, result, pos); - if (pos.getIndex() != start) { + parse(text, parseResult, pos); + if (pos.getIndex() != start && pos.getErrorIndex() == -1) { UChar curr[4]; UErrorCode ec = U_ZERO_ERROR; getEffectiveCurrency(curr, ec); if (U_SUCCESS(ec)) { - Formattable n(result); - CurrencyAmount *tempCurAmnt = new CurrencyAmount(n, curr, ec); // Use for null testing. - if (U_FAILURE(ec) || tempCurAmnt == NULL) { + currAmt = new CurrencyAmount(parseResult, curr, ec); + if (U_FAILURE(ec) || currAmt == NULL) { pos.setIndex(start); // indicate failure - } else { - result.adoptObject(tempCurAmnt); + if ( currAmt != NULL ) { + delete currAmt; + currAmt = NULL; + } } } } - return result; + return currAmt; } // ------------------------------------- diff --git a/icu4c/source/i18n/unicode/decimfmt.h b/icu4c/source/i18n/unicode/decimfmt.h index ab81b2ce353..afb5572121f 100644 --- a/icu4c/source/i18n/unicode/decimfmt.h +++ b/icu4c/source/i18n/unicode/decimfmt.h @@ -1,6 +1,6 @@ /* ******************************************************************************** -* Copyright (C) 1997-2011, International Business Machines +* Copyright (C) 1997-2012, International Business Machines * Corporation and others. All Rights Reserved. ******************************************************************************** * @@ -37,6 +37,7 @@ #include "unicode/locid.h" #include "unicode/fpositer.h" #include "unicode/stringpiece.h" +#include "unicode/curramt.h" U_NAMESPACE_BEGIN @@ -1108,6 +1109,7 @@ public: Formattable& result, UErrorCode& status) const; +#ifndef U_HIDE_DRAFT_API /** * Parses text from the given string as a currency amount. Unlike * the parse() method, this method will attempt to parse a generic @@ -1118,18 +1120,18 @@ public: * (U+00A4) in its prefix or suffix. * * @param text the string to parse - * @param result output parameter to receive result. This will have - * its currency set to the parsed ISO currency code. - * @param pos input-output position; on input, the position within - * text to match; must have 0 <= pos.getIndex() < text.length(); - * on output, the position after the last matched character. If - * the parse fails, the position in unchanged upon output. - * @return a reference to result - * @internal + * @param pos input-output position; on input, the position within text + * to match; must have 0 <= pos.getIndex() < text.length(); + * on output, the position after the last matched character. + * If the parse fails, the position in unchanged upon output. + * @return if parse succeeds, a pointer to a newly-created CurrencyAmount + * object (owned by the caller) containing information about + * the parsed currency; if parse fails, this is NULL. + * @draft ICU 49 */ - virtual Formattable& parseCurrency(const UnicodeString& text, - Formattable& result, - ParsePosition& pos) const; + virtual CurrencyAmount* parseCurrency(const UnicodeString& text, + ParsePosition& pos) const; +#endif /* U_HIDE_DRAFT_API */ /** * Returns the decimal format symbols, which is generally not changed @@ -1944,7 +1946,7 @@ private: void parse(const UnicodeString& text, Formattable& result, ParsePosition& pos, - UBool parseCurrency) const; + UChar* currency) const; enum { fgStatusInfinite, @@ -2179,7 +2181,7 @@ private: * and plural currency style. And the patterns are set through applyPattern. */ // TODO: innerclass? - /* This is not needed in the class declaration, so it is moved into decimfmp.cpp + /* This is not needed in the class declaration, so it is moved into decimfmp.cpp struct AffixPatternsForCurrency : public UMemory { // negative prefix pattern UnicodeString negPrefixPatternForCurrency; @@ -2209,7 +2211,7 @@ private: * equals to 3, such as the pattern contains 3 currency sign or * the formatter style is currency plural format style. */ - /* This is not needed in the class declaration, so it is moved into decimfmp.cpp + /* This is not needed in the class declaration, so it is moved into decimfmp.cpp struct AffixesForCurrency : public UMemory { // negative prefix UnicodeString negPrefixForCurrency; diff --git a/icu4c/source/i18n/unicode/numfmt.h b/icu4c/source/i18n/unicode/numfmt.h index f2f2d6e5630..1961c6b18e3 100644 --- a/icu4c/source/i18n/unicode/numfmt.h +++ b/icu4c/source/i18n/unicode/numfmt.h @@ -36,6 +36,7 @@ #include "unicode/unum.h" // UNumberFormatStyle #include "unicode/locid.h" #include "unicode/stringpiece.h" +#include "unicode/curramt.h" class NumberFormatTest; @@ -569,6 +570,7 @@ public: Formattable& result, UErrorCode& status) const; +#ifndef U_HIDE_DRAFT_API /** * Parses text from the given string as a currency amount. Unlike * the parse() method, this method will attempt to parse a generic @@ -579,18 +581,18 @@ public: * (U+00A4) in its prefix or suffix. * * @param text the string to parse - * @param result output parameter to receive result. This will have - * its currency set to the parsed ISO currency code. - * @param pos input-output position; on input, the position within - * text to match; must have 0 <= pos.getIndex() < text.length(); - * on output, the position after the last matched character. If - * the parse fails, the position in unchanged upon output. - * @return a reference to result - * @internal + * @param pos input-output position; on input, the position within text + * to match; must have 0 <= pos.getIndex() < text.length(); + * on output, the position after the last matched character. + * If the parse fails, the position in unchanged upon output. + * @return if parse succeeds, a pointer to a newly-created CurrencyAmount + * object (owned by the caller) containing information about + * the parsed currency; if parse fails, this is NULL. + * @draft ICU 49 */ - virtual Formattable& parseCurrency(const UnicodeString& text, - Formattable& result, - ParsePosition& pos) const; + virtual CurrencyAmount* parseCurrency(const UnicodeString& text, + ParsePosition& pos) const; +#endif /* U_HIDE_DRAFT_API */ /** * Return true if this format will parse numbers as integers @@ -1060,7 +1062,7 @@ NumberFormat::isParseIntegerOnly() const inline UBool NumberFormat::isLenient() const { - return fLenient; + return fLenient; } inline UnicodeString& diff --git a/icu4c/source/i18n/unum.cpp b/icu4c/source/i18n/unum.cpp index 7d7fe9cd171..a16d3890e97 100644 --- a/icu4c/source/i18n/unum.cpp +++ b/icu4c/source/i18n/unum.cpp @@ -1,6 +1,6 @@ /* ******************************************************************************* -* Copyright (C) 1996-2011, International Business Machines +* Copyright (C) 1996-2012, International Business Machines * Corporation and others. All Rights Reserved. ******************************************************************************* * Modification History: @@ -318,7 +318,6 @@ parseRes(Formattable& res, const UChar* text, int32_t textLength, int32_t *parsePos /* 0 = start */, - UBool parseCurrency, UErrorCode *status) { if(U_FAILURE(*status)) @@ -331,11 +330,7 @@ parseRes(Formattable& res, if(parsePos != 0) pp.setIndex(*parsePos); - if (parseCurrency) { - ((const NumberFormat*)fmt)->parseCurrency(src, res, pp); - } else { - ((const NumberFormat*)fmt)->parse(src, res, pp); - } + ((const NumberFormat*)fmt)->parse(src, res, pp); if(pp.getErrorIndex() != -1) { *status = U_PARSE_ERROR; @@ -355,7 +350,7 @@ unum_parse( const UNumberFormat* fmt, UErrorCode *status) { Formattable res; - parseRes(res, fmt, text, textLength, parsePos, FALSE, status); + parseRes(res, fmt, text, textLength, parsePos, status); return res.getLong(*status); } @@ -367,7 +362,7 @@ unum_parseInt64( const UNumberFormat* fmt, UErrorCode *status) { Formattable res; - parseRes(res, fmt, text, textLength, parsePos, FALSE, status); + parseRes(res, fmt, text, textLength, parsePos, status); return res.getInt64(*status); } @@ -379,7 +374,7 @@ unum_parseDouble( const UNumberFormat* fmt, UErrorCode *status) { Formattable res; - parseRes(res, fmt, text, textLength, parsePos, FALSE, status); + parseRes(res, fmt, text, textLength, parsePos, status); return res.getDouble(*status); } @@ -400,7 +395,7 @@ unum_parseDecimal(const UNumberFormat* fmt, return -1; } Formattable res; - parseRes(res, fmt, text, textLength, parsePos, FALSE, status); + parseRes(res, fmt, text, textLength, parsePos, status); StringPiece sp = res.getDecimalNumber(*status); if (U_FAILURE(*status)) { return -1; @@ -423,15 +418,38 @@ unum_parseDoubleCurrency(const UNumberFormat* fmt, int32_t* parsePos, /* 0 = start */ UChar* currency, UErrorCode* status) { - Formattable res; - parseRes(res, fmt, text, textLength, parsePos, TRUE, status); + double doubleVal = 0.0; currency[0] = 0; - const CurrencyAmount* c; - if (res.getType() == Formattable::kObject && - (c = dynamic_cast(res.getObject())) != NULL) { - u_strcpy(currency, c->getISOCurrency()); + if (U_FAILURE(*status)) { + return doubleVal; } - return res.getDouble(*status); + int32_t len = (textLength == -1 ? u_strlen(text) : textLength); + const UnicodeString src((UChar*)text, len, len); + ParsePosition pp; + if (parsePos != NULL) { + pp.setIndex(*parsePos); + } + *status = U_PARSE_ERROR; // assume failure, reset if succeed + CurrencyAmount* currAmt = ((const NumberFormat*)fmt)->parseCurrency(src, pp); + if (pp.getErrorIndex() != -1) { + if (parsePos != NULL) { + *parsePos = pp.getErrorIndex(); + } + if (currAmt != NULL) { + delete currAmt; + } + } else { + if (parsePos != NULL) { + *parsePos = pp.getIndex(); + } + if (currAmt != NULL) { + *status = U_ZERO_ERROR; + u_strcpy(currency, currAmt->getISOCurrency()); + doubleVal = currAmt->getNumber().getDouble(*status); + delete currAmt; + } + } + return doubleVal; } U_CAPI const char* U_EXPORT2 diff --git a/icu4c/source/test/intltest/numfmtst.cpp b/icu4c/source/test/intltest/numfmtst.cpp index bcafb8d1366..1e9c705abb4 100644 --- a/icu4c/source/test/intltest/numfmtst.cpp +++ b/icu4c/source/test/intltest/numfmtst.cpp @@ -159,9 +159,11 @@ NumberFormatTest::TestAPI(void) } ParsePosition ppos; - test->parseCurrency("",bla,ppos); - if(U_FAILURE(status)) { - errln("Problems accessing the parseCurrency function for NumberFormat"); + CurrencyAmount* currAmt = test->parseCurrency("",ppos); + // old test for (U_FAILURE(status)) was bogus here, method does not set status! + if (currAmt != NULL) { + errln("Parsed empty string as currency"); + delete currAmt; } delete test; @@ -854,10 +856,10 @@ NumberFormatTest::TestParse(void) // ------------------------------------- static const char *lenientAffixTestCases[] = { - "(1)", - "( 1)", - "(1 )", - "( 1 )" + "(1)", + "( 1)", + "(1 )", + "( 1 )" }; static const char *lenientMinusTestCases[] = { @@ -867,33 +869,33 @@ static const char *lenientMinusTestCases[] = { }; static const char *lenientCurrencyTestCases[] = { - "$1,000", - "$ 1,000", - "$1000", - "$ 1000", - "$1 000.00", - "$ 1 000.00", - "$ 1\\u00A0000.00", - "1000.00" + "$1,000", + "$ 1,000", + "$1000", + "$ 1000", + "$1 000.00", + "$ 1 000.00", + "$ 1\\u00A0000.00", + "1000.00" }; static const char *lenientNegativeCurrencyTestCases[] = { - "($1,000)", - "($ 1,000)", - "($1000)", - "($ 1000)", - "($1 000.00)", - "($ 1 000.00)", - "( $ 1,000.00 )", - "($ 1\\u00A0000.00)", - "(1000.00)" + "($1,000)", + "($ 1,000)", + "($1000)", + "($ 1000)", + "($1 000.00)", + "($ 1 000.00)", + "( $ 1,000.00 )", + "($ 1\\u00A0000.00)", + "(1000.00)" }; static const char *lenientPercentTestCases[] = { - "25%", - " 25%", - " 25 %", - "25 %", + "25%", + " 25%", + " 25 %", + "25 %", "25\\u00A0%", "25" }; @@ -4064,7 +4066,7 @@ NumberFormatTest::TestParseCurrencyInUCurr() { "Latvian Lats1.00", "Latvian Ruble1.00", "Latvian lats1.00", - "Latvian lati.00", + "Latvian lati1.00", "Latvian ruble1.00", "Latvian rubles1.00", "Lebanese Pound1.00", @@ -6015,14 +6017,16 @@ NumberFormatTest::TestParseCurrencyInUCurr() { UErrorCode status = U_ZERO_ERROR; NumberFormat* numFmt = NumberFormat::createInstance(locale, UNUM_CURRENCY, status); if (numFmt != NULL && U_SUCCESS(status)) { - Formattable parseResult; ParsePosition parsePos; - numFmt->parseCurrency(formatted, parseResult, parsePos); - if (parsePos.getIndex() == 0 || - (parseResult.getType() == Formattable::kDouble && - parseResult.getDouble() != 1.0)) { - errln("wrong parsing, " + formatted); - errln("data: " + formatted); + CurrencyAmount* currAmt = numFmt->parseCurrency(formatted, parsePos); + if (currAmt != NULL) { + double doubleVal = currAmt->getNumber().getDouble(status); + if ( doubleVal != 1.0 ) { + errln("Parsed as currency value other than 1.0: " + formatted + " -> " + doubleVal); + } + delete currAmt; + } else { + errln("Failed to parse as currency: " + formatted); } } else { dataerrln("Unable to create NumberFormat. - %s", u_errorName(status)); @@ -6037,14 +6041,12 @@ NumberFormatTest::TestParseCurrencyInUCurr() { UErrorCode status = U_ZERO_ERROR; NumberFormat* numFmt = NumberFormat::createInstance(locale, UNUM_CURRENCY, status); if (numFmt != NULL && U_SUCCESS(status)) { - Formattable parseResult; ParsePosition parsePos; - numFmt->parseCurrency(formatted, parseResult, parsePos); - if (parsePos.getIndex() > 0 || - (parseResult.getType() == Formattable::kDouble && - parseResult.getDouble() == 1.0)) { - errln("parsed but should not be: " + formatted); - errln("data: " + formatted); + CurrencyAmount* currAmt = numFmt->parseCurrency(formatted, parsePos); + if (currAmt != NULL) { + double doubleVal = currAmt->getNumber().getDouble(status); + errln("Parsed as currency, should not have: " + formatted + " -> " + doubleVal); + delete currAmt; } } else { dataerrln("Unable to create NumberFormat. - %s", u_errorName(status));