Kaydet (Commit) 0c8d1c04 authored tarafından Eike Rathke's avatar Eike Rathke Kaydeden (comit) Markus Mohrhard

Resolves: tdf#91453 use configuration of text to number conversion

... also in arithmetic matrix operations.

(combination of 4 commits):

move ConvertStringToValue() implementation from ScInterpreter to ScGlobal

In preparation of matrix calculations to use string conversion
configuration and UI markers for cells containing strings that could be
numeric values.

Change-Id: Ifa9e45853dded249fa741c050ae1f106365f99ea
(cherry picked from commit 329496c1)

add half decoupled ScInterpreter::ConvertStringToValue()

... for back calls of ScMatrix in preparation of tdf#91453

Change-Id: Ife94d1675c1bc7c5611586e3f352ff69264469d7
(cherry picked from commit 6516d5e2)

Resolves: tdf#91453 use configuration of text to number conversion

... also in arithmetic matrix operations.

Change-Id: Ia00054d0af383e225d9d40b59da2dc28a817b65a
(cherry picked from commit 466a20ef)

Resolves: tdf#91453 use configuration of text to number conversion

... also in arithmetic matrix operations if both operands are matrix.

Change-Id: I84609656b166b4e059d9496a5ed732a96e731164
(cherry picked from commit 778d03b5)
Reviewed-on: https://gerrit.libreoffice.org/19172Tested-by: 's avatarJenkins <ci@libreoffice.org>
Reviewed-by: 's avatarMarkus Mohrhard <markus.mohrhard@googlemail.com>
Tested-by: 's avatarMarkus Mohrhard <markus.mohrhard@googlemail.com>
üst 6b7ae3f8
...@@ -34,6 +34,7 @@ class ImageList; ...@@ -34,6 +34,7 @@ class ImageList;
class Bitmap; class Bitmap;
class SfxItemSet; class SfxItemSet;
class Color; class Color;
struct ScCalcConfig;
enum class SvtScriptType; enum class SvtScriptType;
#define SC_COLLATOR_IGNORES ( \ #define SC_COLLATOR_IGNORES ( \
...@@ -720,6 +721,117 @@ SC_DLLPUBLIC static const sal_Unicode* FindUnquoted( const sal_Unicode* pStri ...@@ -720,6 +721,117 @@ SC_DLLPUBLIC static const sal_Unicode* FindUnquoted( const sal_Unicode* pStri
SC_DLLPUBLIC static OUString ReplaceOrAppend( const OUString& rString, SC_DLLPUBLIC static OUString ReplaceOrAppend( const OUString& rString,
const OUString& rPlaceholder, const OUString& rPlaceholder,
const OUString& rReplacement ); const OUString& rReplacement );
/** Convert string content to numeric value.
In any case, if rError is set 0.0 is returned.
If nStringNoValueError is errCellNoValue, that is unconditionally
assigned to rError and 0.0 is returned. The caller is expected to
handle this situation. Used by the interpreter.
Usually errNoValue is passed as nStringNoValueError.
Otherwise, depending on the string conversion configuration different
approaches are taken:
For ScCalcConfig::StringConversion::ILLEGAL
The error value passed in nStringNoValueError is assigned to rError
(and 0.0 returned).
For ScCalcConfig::StringConversion::ZERO
A zero value is returned and no error assigned.
For ScCalcConfig::StringConversion::LOCALE
If the string is empty or consists only of spaces, if "treat empty
string as zero" is set 0.0 is returned, else nStringNoValueError
assigned to rError (and 0.0 returned).
Else a non-empty string is passed to the number formatter's scanner to
be parsed locale dependent. If that does not detect a numeric value
nStringNoValueError is assigned to rError (and 0.0 returned).
If no number formatter was passed, the conversion falls back to
UNAMBIGUOUS.
For ScCalcConfig::StringConversion::UNAMBIGUOUS
If the string is empty or consists only of spaces, if "treat empty
string as zero" is set 0.0 is returned, else nStringNoValueError
assigned to rError (and 0.0 returned).
If the string is not empty the following conversion rules are applied:
Converted are only integer numbers including exponent, and ISO 8601 dates
and times in their extended formats with separators. Anything else,
especially fractional numeric values with decimal separators or dates other
than ISO 8601 would be locale dependent and is a no-no. Leading and
trailing blanks are ignored.
The following ISO 8601 formats are converted:
CCYY-MM-DD
CCYY-MM-DDThh:mm
CCYY-MM-DDThh:mm:ss
CCYY-MM-DDThh:mm:ss,s
CCYY-MM-DDThh:mm:ss.s
hh:mm
hh:mm:ss
hh:mm:ss,s
hh:mm:ss.s
The century CC may not be omitted and the two-digit year setting is not
taken into account. Instead of the T date and time separator exactly one
blank may be used.
If a date is given, it must be a valid Gregorian calendar date. In this
case the optional time must be in the range 00:00 to 23:59:59.99999...
If only time is given, it may have any value for hours, taking elapsed time
into account; minutes and seconds are limited to the value 59 as well.
If the string can not be converted to a numeric value, the error value
passed in nStringNoValueError is assigned to rError.
@param rStr
The string to be converted.
@param rConfig
The calculation configuration.
@param rError
Contains the error on return, if any. If an error was set before
and the conversion did not result in an error, still 0.0 is
returned.
@param nStringNoValueError
The error value to be assigned to rError if string could not be
converted to number.
@param pFormatter
The number formatter to use in case of
ScCalcConfig::StringConversion::LOCALE. Can but should not be
nullptr in which case conversion falls back to
ScCalcConfig::StringConversion::UNAMBIGUOUS and if a date is
detected the null date is assumed to be the standard 1899-12-30
instead of the configured null date.
@param rCurFmtType
Can be assigned a format type in case a date or time or date+time
string was converted, e.g. css::util::NumberFormat::DATE or
css::util::NumberFormat::TIME or a combination thereof.
*/
static double ConvertStringToValue( const OUString& rStr, const ScCalcConfig& rConfig,
sal_uInt16 & rError, sal_uInt16 nStringNoValueError,
SvNumberFormatter* pFormatter, short & rCurFmtType );
}; };
#endif #endif
......
This diff is collapsed.
...@@ -203,6 +203,11 @@ void ReplaceCell( SCCOL& rCol, SCROW& rRow, SCTAB& rTab ); // for TableOp ...@@ -203,6 +203,11 @@ void ReplaceCell( SCCOL& rCol, SCROW& rRow, SCTAB& rTab ); // for TableOp
bool IsTableOpInRange( const ScRange& ); bool IsTableOpInRange( const ScRange& );
sal_uLong GetCellNumberFormat( const ScAddress& rPos, ScRefCellValue& rCell ); sal_uLong GetCellNumberFormat( const ScAddress& rPos, ScRefCellValue& rCell );
double ConvertStringToValue( const OUString& ); double ConvertStringToValue( const OUString& );
public:
/** For matrix back calls into the current interpreter.
Uses rError instead of nGlobalError and rCurFmtType instead of nCurFmtType. */
double ConvertStringToValue( const OUString&, sal_uInt16& rError, short& rCurFmtType );
private:
double GetCellValue( const ScAddress&, ScRefCellValue& rCell ); double GetCellValue( const ScAddress&, ScRefCellValue& rCell );
double GetCellValueOrZero( const ScAddress&, ScRefCellValue& rCell ); double GetCellValueOrZero( const ScAddress&, ScRefCellValue& rCell );
double GetValueCellValue( const ScAddress&, double fOrig ); double GetValueCellValue( const ScAddress&, double fOrig );
......
...@@ -1110,8 +1110,10 @@ static ScMatrixRef lcl_MatrixCalculation( ...@@ -1110,8 +1110,10 @@ static ScMatrixRef lcl_MatrixCalculation(
{ {
for (j = 0; j < nMinR; j++) for (j = 0; j < nMinR; j++)
{ {
bool bVal1 = rMat1.IsValueOrEmpty(i,j);
bool bVal2 = rMat2.IsValueOrEmpty(i,j);
sal_uInt16 nErr; sal_uInt16 nErr;
if (rMat1.IsValueOrEmpty(i,j) && rMat2.IsValueOrEmpty(i,j)) if (bVal1 && bVal2)
{ {
double d = Op(rMat1.GetDouble(i,j), rMat2.GetDouble(i,j)); double d = Op(rMat1.GetDouble(i,j), rMat2.GetDouble(i,j));
xResMat->PutDouble( d, i, j); xResMat->PutDouble( d, i, j);
...@@ -1121,6 +1123,28 @@ static ScMatrixRef lcl_MatrixCalculation( ...@@ -1121,6 +1123,28 @@ static ScMatrixRef lcl_MatrixCalculation(
{ {
xResMat->PutError( nErr, i, j); xResMat->PutError( nErr, i, j);
} }
else if ((!bVal1 && rMat1.IsString(i,j)) || (!bVal2 && rMat2.IsString(i,j)))
{
sal_uInt16 nError1 = 0;
short nFmt1 = 0;
double fVal1 = (bVal1 ? rMat1.GetDouble(i,j) :
pInterpreter->ConvertStringToValue( rMat1.GetString(i,j).getString(), nError1, nFmt1));
sal_uInt16 nError2 = 0;
short nFmt2 = 0;
double fVal2 = (bVal2 ? rMat2.GetDouble(i,j) :
pInterpreter->ConvertStringToValue( rMat2.GetString(i,j).getString(), nError2, nFmt2));
if (nError1)
xResMat->PutError( nError1, i, j);
else if (nError2)
xResMat->PutError( nError2, i, j);
else
{
double d = Op( fVal1, fVal2);
xResMat->PutDouble( d, i, j);
}
}
else else
xResMat->PutError( errNoValue, i, j); xResMat->PutError( errNoValue, i, j);
} }
......
...@@ -2560,7 +2560,10 @@ struct COp<T, double> ...@@ -2560,7 +2560,10 @@ struct COp<T, double>
}; };
/** A template for operations where operands are supposed to be numeric. /** A template for operations where operands are supposed to be numeric.
A non-numeric (string) operand leads to an errNoValue DoubleError. A non-numeric (string) operand leads to the configured conversion to number
method being called if in interpreter context and an errNoValue DoubleError
if conversion was not possible, else to an unconditional errNoValue
DoubleError.
An empty operand evaluates to 0. An empty operand evaluates to 0.
XXX: semantically TEmptyRes and types other than number_value_type are XXX: semantically TEmptyRes and types other than number_value_type are
unused, but this template could serve as a basis for future enhancements. unused, but this template could serve as a basis for future enhancements.
...@@ -2570,6 +2573,7 @@ struct MatOp ...@@ -2570,6 +2573,7 @@ struct MatOp
{ {
private: private:
TOp maOp; TOp maOp;
ScInterpreter* mpErrorInterpreter;
svl::SharedString maString; svl::SharedString maString;
double mfVal; double mfVal;
COp<TOp, TEmptyRes> maCOp; COp<TOp, TEmptyRes> maCOp;
...@@ -2579,8 +2583,10 @@ public: ...@@ -2579,8 +2583,10 @@ public:
typedef TRet number_value_type; typedef TRet number_value_type;
typedef svl::SharedString string_value_type; typedef svl::SharedString string_value_type;
MatOp( TOp aOp, double fVal = 0.0, const svl::SharedString& rString = svl::SharedString() ): MatOp( TOp aOp, ScInterpreter* pErrorInterpreter,
double fVal = 0.0, const svl::SharedString& rString = svl::SharedString() ):
maOp(aOp), maOp(aOp),
mpErrorInterpreter(pErrorInterpreter),
maString(rString), maString(rString),
mfVal(fVal) mfVal(fVal)
{ } { }
...@@ -2595,8 +2601,17 @@ public: ...@@ -2595,8 +2601,17 @@ public:
return maOp((double)bVal, mfVal); return maOp((double)bVal, mfVal);
} }
double operator()(const svl::SharedString&) const double operator()(const svl::SharedString& rStr) const
{ {
if (mpErrorInterpreter)
{
sal_uInt16 nError = 0;
short nCurFmtType = 0;
double fValue = mpErrorInterpreter->ConvertStringToValue( rStr.getString(), nError, nCurFmtType);
if (nError)
return CreateDoubleError( nError);
return fValue;
}
return CreateDoubleError( errNoValue); return CreateDoubleError( errNoValue);
} }
...@@ -2616,21 +2631,21 @@ public: ...@@ -2616,21 +2631,21 @@ public:
void ScMatrix::NotOp( ScMatrix& rMat) void ScMatrix::NotOp( ScMatrix& rMat)
{ {
auto not_ = [](double a, double){return double(a == 0.0);}; auto not_ = [](double a, double){return double(a == 0.0);};
matop::MatOp<decltype(not_), double> aOp(not_); matop::MatOp<decltype(not_), double> aOp(not_, pImpl->GetErrorInterpreter());
pImpl->ApplyOperation(aOp, *rMat.pImpl); pImpl->ApplyOperation(aOp, *rMat.pImpl);
} }
void ScMatrix::NegOp( ScMatrix& rMat) void ScMatrix::NegOp( ScMatrix& rMat)
{ {
auto neg_ = [](double a, double){return -a;}; auto neg_ = [](double a, double){return -a;};
matop::MatOp<decltype(neg_), double> aOp(neg_); matop::MatOp<decltype(neg_), double> aOp(neg_, pImpl->GetErrorInterpreter());
pImpl->ApplyOperation(aOp, *rMat.pImpl); pImpl->ApplyOperation(aOp, *rMat.pImpl);
} }
void ScMatrix::AddOp( double fVal, ScMatrix& rMat) void ScMatrix::AddOp( double fVal, ScMatrix& rMat)
{ {
auto add_ = [](double a, double b){return a + b;}; auto add_ = [](double a, double b){return a + b;};
matop::MatOp<decltype(add_)> aOp(add_, fVal); matop::MatOp<decltype(add_)> aOp(add_, pImpl->GetErrorInterpreter(), fVal);
pImpl->ApplyOperation(aOp, *rMat.pImpl); pImpl->ApplyOperation(aOp, *rMat.pImpl);
} }
...@@ -2639,13 +2654,13 @@ void ScMatrix::SubOp( bool bFlag, double fVal, ScMatrix& rMat) ...@@ -2639,13 +2654,13 @@ void ScMatrix::SubOp( bool bFlag, double fVal, ScMatrix& rMat)
if (bFlag) if (bFlag)
{ {
auto sub_ = [](double a, double b){return b - a;}; auto sub_ = [](double a, double b){return b - a;};
matop::MatOp<decltype(sub_)> aOp(sub_, fVal); matop::MatOp<decltype(sub_)> aOp(sub_, pImpl->GetErrorInterpreter(), fVal);
pImpl->ApplyOperation(aOp, *rMat.pImpl); pImpl->ApplyOperation(aOp, *rMat.pImpl);
} }
else else
{ {
auto sub_ = [](double a, double b){return a - b;}; auto sub_ = [](double a, double b){return a - b;};
matop::MatOp<decltype(sub_)> aOp(sub_, fVal); matop::MatOp<decltype(sub_)> aOp(sub_, pImpl->GetErrorInterpreter(), fVal);
pImpl->ApplyOperation(aOp, *rMat.pImpl); pImpl->ApplyOperation(aOp, *rMat.pImpl);
} }
} }
...@@ -2653,7 +2668,7 @@ void ScMatrix::SubOp( bool bFlag, double fVal, ScMatrix& rMat) ...@@ -2653,7 +2668,7 @@ void ScMatrix::SubOp( bool bFlag, double fVal, ScMatrix& rMat)
void ScMatrix::MulOp( double fVal, ScMatrix& rMat) void ScMatrix::MulOp( double fVal, ScMatrix& rMat)
{ {
auto mul_ = [](double a, double b){return a * b;}; auto mul_ = [](double a, double b){return a * b;};
matop::MatOp<decltype(mul_)> aOp(mul_, fVal); matop::MatOp<decltype(mul_)> aOp(mul_, pImpl->GetErrorInterpreter(), fVal);
pImpl->ApplyOperation(aOp, *rMat.pImpl); pImpl->ApplyOperation(aOp, *rMat.pImpl);
} }
...@@ -2662,13 +2677,13 @@ void ScMatrix::DivOp( bool bFlag, double fVal, ScMatrix& rMat) ...@@ -2662,13 +2677,13 @@ void ScMatrix::DivOp( bool bFlag, double fVal, ScMatrix& rMat)
if (bFlag) if (bFlag)
{ {
auto div_ = [](double a, double b){return sc::div(b, a);}; auto div_ = [](double a, double b){return sc::div(b, a);};
matop::MatOp<decltype(div_)> aOp(div_, fVal); matop::MatOp<decltype(div_)> aOp(div_, pImpl->GetErrorInterpreter(), fVal);
pImpl->ApplyOperation(aOp, *rMat.pImpl); pImpl->ApplyOperation(aOp, *rMat.pImpl);
} }
else else
{ {
auto div_ = [](double a, double b){return sc::div(a, b);}; auto div_ = [](double a, double b){return sc::div(a, b);};
matop::MatOp<decltype(div_)> aOp(div_, fVal); matop::MatOp<decltype(div_)> aOp(div_, pImpl->GetErrorInterpreter(), fVal);
pImpl->ApplyOperation(aOp, *rMat.pImpl); pImpl->ApplyOperation(aOp, *rMat.pImpl);
} }
} }
...@@ -2678,13 +2693,13 @@ void ScMatrix::PowOp( bool bFlag, double fVal, ScMatrix& rMat) ...@@ -2678,13 +2693,13 @@ void ScMatrix::PowOp( bool bFlag, double fVal, ScMatrix& rMat)
if (bFlag) if (bFlag)
{ {
auto pow_ = [](double a, double b){return pow(b, a);}; auto pow_ = [](double a, double b){return pow(b, a);};
matop::MatOp<decltype(pow_)> aOp(pow_, fVal); matop::MatOp<decltype(pow_)> aOp(pow_, pImpl->GetErrorInterpreter(), fVal);
pImpl->ApplyOperation(aOp, *rMat.pImpl); pImpl->ApplyOperation(aOp, *rMat.pImpl);
} }
else else
{ {
auto pow_ = [](double a, double b){return pow(a, b);}; auto pow_ = [](double a, double b){return pow(a, b);};
matop::MatOp<decltype(pow_)> aOp(pow_, fVal); matop::MatOp<decltype(pow_)> aOp(pow_, pImpl->GetErrorInterpreter(), fVal);
pImpl->ApplyOperation(aOp, *rMat.pImpl); pImpl->ApplyOperation(aOp, *rMat.pImpl);
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment