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>
This commit is contained in:
committed by
Andreas Heinisch
parent
90b12c9bad
commit
9cc8457abc
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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,77 +90,62 @@ 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)
|
||||
{
|
||||
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++;
|
||||
pFormatter = GetSbData()->pInst->GetNumberFormatter();
|
||||
}
|
||||
else
|
||||
{
|
||||
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 )
|
||||
{
|
||||
SbxValues aTmp;
|
||||
|
||||
Reference in New Issue
Block a user