summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Clark <jon@nullptr.ca>2023-12-15 23:09:19 -0700
committerAndreas Heinisch <andreas.heinisch@yahoo.de>2023-12-23 09:12:24 +0100
commit9cc8457abcae57c7f9de6e0fbca1fbc2a0cc9892 (patch)
treeb242300edd3c229c651b34bb0398526b54984ba1
parent90b12c9bad55e8f50b75a6d7b68caa27d82cc2b9 (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.bas27
-rw-r--r--basic/qa/basic_coverage/test_ccur_method.bas69
-rw-r--r--basic/qa/basic_coverage/zh-CN/test_ccur_zh_CN_locale.bas27
-rw-r--r--basic/source/sbx/sbxcurr.cxx100
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 )
{