diff options
author | Jonathan Clark <jon@nullptr.ca> | 2023-12-15 23:09:19 -0700 |
---|---|---|
committer | Andreas Heinisch <andreas.heinisch@yahoo.de> | 2023-12-23 09:12:24 +0100 |
commit | 9cc8457abcae57c7f9de6e0fbca1fbc2a0cc9892 (patch) | |
tree | b242300edd3c229c651b34bb0398526b54984ba1 | |
parent | 90b12c9bad55e8f50b75a6d7b68caa27d82cc2b9 (diff) |
tdf#128122 Updated BASIC CCur to reuse SvNumberFormatter
Previously, BASIC CCur used a custom, single-purpose currency string
parser which did not properly accommodate the user's locale setting.
This change replaces the custom parser with SvNumberFormatter, which
does correctly respect system locale.
Change-Id: I179915eb080e876e5e550dd350fdb86d7fa2bf4c
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/160848
Tested-by: Jenkins
Reviewed-by: Andreas Heinisch <andreas.heinisch@yahoo.de>
-rw-r--r-- | basic/qa/basic_coverage/da-DK/test_ccur_da_DK_locale.bas | 27 | ||||
-rw-r--r-- | basic/qa/basic_coverage/test_ccur_method.bas | 69 | ||||
-rw-r--r-- | basic/qa/basic_coverage/zh-CN/test_ccur_zh_CN_locale.bas | 27 | ||||
-rw-r--r-- | basic/source/sbx/sbxcurr.cxx | 100 |
4 files changed, 162 insertions, 61 deletions
diff --git a/basic/qa/basic_coverage/da-DK/test_ccur_da_DK_locale.bas b/basic/qa/basic_coverage/da-DK/test_ccur_da_DK_locale.bas new file mode 100644 index 000000000000..52b8d3b6f1aa --- /dev/null +++ b/basic/qa/basic_coverage/da-DK/test_ccur_da_DK_locale.bas @@ -0,0 +1,27 @@ +' +' This file is part of the LibreOffice project. +' +' This Source Code Form is subject to the terms of the Mozilla Public +' License, v. 2.0. If a copy of the MPL was not distributed with this +' file, You can obtain one at http://mozilla.org/MPL/2.0/. +' + +Option Explicit + +Function doUnitTest as String + TestUtil.TestInit + verify_testCCurDaDKLocale + doUnitTest = TestUtil.GetResult() +End Function + +Sub verify_testCCurDaDKLocale + On Error GoTo errorHandler + + ' tdf#141050 - characteristic test for CCur() with the da_DK locale + TestUtil.AssertEqual(CCur("75,50"), 75.5, "CCur(75,50)") + TestUtil.AssertEqual(CCur("75,50 kr."), 75.5, "CCur(75,50 kr.)") + + Exit Sub +errorHandler: + TestUtil.ReportErrorHandler("verify_testCCurDaDKLocale", Err, Error$, Erl) +End Sub diff --git a/basic/qa/basic_coverage/test_ccur_method.bas b/basic/qa/basic_coverage/test_ccur_method.bas index cd700cad3c8e..c42dcb938590 100644 --- a/basic/qa/basic_coverage/test_ccur_method.bas +++ b/basic/qa/basic_coverage/test_ccur_method.bas @@ -9,16 +9,73 @@ Option Explicit Function doUnitTest as String + TestUtil.TestInit + verify_testCCur + doUnitTest = TestUtil.GetResult() +End Function - doUnitTest = "FAIL" +Sub verify_testCCur + On Error GoTo errorHandler ' CCUR - if (CCur("100") <> 100) Then Exit Function + TestUtil.AssertEqual(CCur("100"), 100, "CCur(100)") + ' tdf#141050 - passing a number with + sign - if (CCur("+100") <> 100) Then Exit Function + TestUtil.AssertEqual(CCur("+100"), 100, "CCur(100)") ' tdf#141050 - passing a number with - sign - if (CCur("-100") <> -100) Then Exit Function + TestUtil.AssertEqual(CCur("-100"), -100, "CCur(-100)") - doUnitTest = "OK" + ' tdf#128122 - verify en_US locale currency format behavior + TestUtil.AssertEqual(CCur("$100"), 100, "CCur($100)") + TestUtil.AssertEqual(CCur("$1.50"), 1.5, "CCur($1.50)") -End Function + verify_testCCurUnderflow + verify_testCCurOverflow + verify_testCCurInvalidFormat + + Exit Sub +errorHandler: + TestUtil.ReportErrorHandler("verify_testCCur", Err, Error$, Erl) +End Sub + +sub verify_testCCurUnderflow + On Error GoTo underflowHandler + + ' tdf$128122 - test underflow condition + CCur("-9223372036854775809") + TestUtil.Assert(False, "verify_testCCur", "underflow error not raised") + + Exit Sub +underflowHandler: + If(Err <> 6) Then + TestUtil.Assert(False, "verify_testCCur", "underflow error incorrect type") + Endif +End Sub + +sub verify_testCCurOverflow + On Error GoTo overflowHandler + + ' tdf$128122 - test overflow condition + CCur("9223372036854775808") + TestUtil.Assert(False, "verify_testCCur", "overflow error not raised") + + Exit Sub +overflowHandler: + If(Err <> 6) Then + TestUtil.Assert(False, "verify_testCCur", "overflow error incorrect type") + Endif +End Sub + +sub verify_testCCurInvalidFormat + On Error GoTo invalidFormatHandler + + ' tdf$128122 - test invalid format in en_US locale + CCur("75,50 kr") + TestUtil.Assert(False, "verify_testCCur", "invalid format error not raised") + + Exit Sub +invalidFormatHandler: + If(Err <> 13) Then + TestUtil.Assert(False, "verify_testCCur", "invalid format error incorrect type") + Endif +End Sub diff --git a/basic/qa/basic_coverage/zh-CN/test_ccur_zh_CN_locale.bas b/basic/qa/basic_coverage/zh-CN/test_ccur_zh_CN_locale.bas new file mode 100644 index 000000000000..38a084e36c7f --- /dev/null +++ b/basic/qa/basic_coverage/zh-CN/test_ccur_zh_CN_locale.bas @@ -0,0 +1,27 @@ +' +' This file is part of the LibreOffice project. +' +' This Source Code Form is subject to the terms of the Mozilla Public +' License, v. 2.0. If a copy of the MPL was not distributed with this +' file, You can obtain one at http://mozilla.org/MPL/2.0/. +' + +Option Explicit + +Function doUnitTest as String + TestUtil.TestInit + verify_testCCurZhCNLocale + doUnitTest = TestUtil.GetResult() +End Function + +Sub verify_testCCurZhCNLocale + On Error GoTo errorHandler + + ' tdf#141050 - characteristic test for CCur() with the zh_CN locale + TestUtil.AssertEqual(CCur("75.50"), 75.5, "CCur(75.50)") + TestUtil.AssertEqual(CCur("¥75.50"), 75.5, "CCur(¥75.50)") + + Exit Sub +errorHandler: + TestUtil.ReportErrorHandler("verify_testCCurZhCNLocale", Err, Error$, Erl) +End Sub diff --git a/basic/source/sbx/sbxcurr.cxx b/basic/source/sbx/sbxcurr.cxx index 54b00102dd49..ca67977a3a56 100644 --- a/basic/source/sbx/sbxcurr.cxx +++ b/basic/source/sbx/sbxcurr.cxx @@ -22,6 +22,11 @@ #include <basic/sberrors.hxx> #include <basic/sbxvar.hxx> #include <o3tl/string_view.hxx> +#include <svl/numformat.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <sbintern.hxx> +#include <runtime.hxx> #include "sbxconv.hxx" @@ -85,76 +90,61 @@ static OUString ImpCurrencyToString( sal_Int64 rVal ) return aAbsStr; } - -static sal_Int64 ImpStringToCurrency( std::u16string_view rStr ) +static sal_Int64 ImpStringToCurrency(const rtl::OUString& rStr) { - - sal_Int32 nFractDigit = 4; - - sal_Unicode const cDeciPnt = '.'; - sal_Unicode const c1000Sep = ','; - - // lets use the existing string number conversions - // there is a performance impact here ( multiple string copies ) - // but better I think than a home brewed string parser, if we need a parser - // we should share some existing ( possibly from calc is there a currency - // conversion there ? #TODO check ) - - std::u16string_view sTmp = o3tl::trim( rStr ); - auto p = sTmp.begin(); - auto pEnd = sTmp.end(); - - // normalise string number by removing thousand & decimal point separators - OUStringBuffer sNormalisedNumString( static_cast<sal_Int32>(sTmp.size()) + nFractDigit ); - - if ( p != pEnd && (*p == '-' || *p == '+' ) ) - sNormalisedNumString.append( *p++ ); - - while ( p != pEnd && *p >= '0' && *p <= '9' ) + LanguageType eLangType = Application::GetSettings().GetLanguageTag().getLanguageType(); + std::shared_ptr<SvNumberFormatter> pFormatter; + if (GetSbData()->pInst) + { + pFormatter = GetSbData()->pInst->GetNumberFormatter(); + } + else { - sNormalisedNumString.append( *p++ ); - // #TODO in vba mode set runtime error when a space ( or other ) - // illegal character is found - if( p != pEnd && *p == c1000Sep ) - p++; + sal_uInt32 n; // Dummy + pFormatter = SbiInstance::PrepareNumberFormatter(/*date index*/ n, /*time index*/ n, + /*date time index*/ n); } - bool bRoundUp = false; + // Passing a locale index switches IsNumberFormat() to use that locale, + // in case the formatter wasn't default-created with it. + sal_uInt32 nIndex = pFormatter->GetStandardIndex(eLangType); - if( p != pEnd && *p == cDeciPnt ) + double fResult = 0.0; + bool bSuccess = pFormatter->IsNumberFormat(rStr, nIndex, fResult); + if (bSuccess) { - p++; - while( nFractDigit && p != pEnd && *p >= '0' && *p <= '9' ) + SvNumFormatType nType = pFormatter->GetType(nIndex); + if (!(nType & (SvNumFormatType::CURRENCY | SvNumFormatType::NUMBER))) { - sNormalisedNumString.append( *p++ ); - nFractDigit--; + bSuccess = false; } - // Consume trailing content - // Round up if necessary - if( p != pEnd && *p >= '5' && *p <= '9' ) - bRoundUp = true; - while( p != pEnd && *p >= '0' && *p <= '9' ) - p++; } - // can we raise error here ? ( previous behaviour was more forgiving ) - // so... not sure that could break existing code, let's see if anyone - // complains. - if ( p != pEnd ) - SbxBase::SetError( ERRCODE_BASIC_CONVERSION ); - while( nFractDigit ) + if (!bSuccess) { - sNormalisedNumString.append( '0' ); - nFractDigit--; + SbxBase::SetError(ERRCODE_BASIC_CONVERSION); } - sal_Int64 result = o3tl::toInt64(sNormalisedNumString); + sal_Int64 nRes = 0; + const auto fShiftedResult = fResult * CURRENCY_FACTOR; + if (fShiftedResult + 0.5 > static_cast<double>(SAL_MAX_INT64) + || fShiftedResult - 0.5 < static_cast<double>(SAL_MIN_INT64)) + { + nRes = SAL_MAX_INT64; + if (fShiftedResult - 0.5 < static_cast<double>(SAL_MIN_INT64)) + { + nRes = SAL_MIN_INT64; + } - if ( bRoundUp ) - ++result; - return result; -} + SbxBase::SetError(ERRCODE_BASIC_MATH_OVERFLOW); + } + else + { + nRes = ImpDoubleToCurrency(fResult); + } + return nRes; +} sal_Int64 ImpGetCurrency( const SbxValues* p ) { |