diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /sc/source/core/tool/formularesult.cxx | |
parent | Initial commit. (diff) | |
download | libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sc/source/core/tool/formularesult.cxx')
-rw-r--r-- | sc/source/core/tool/formularesult.cxx | 640 |
1 files changed, 640 insertions, 0 deletions
diff --git a/sc/source/core/tool/formularesult.cxx b/sc/source/core/tool/formularesult.cxx new file mode 100644 index 000000000..c14f5a9a7 --- /dev/null +++ b/sc/source/core/tool/formularesult.cxx @@ -0,0 +1,640 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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/. + */ + +#include <formularesult.hxx> +#include <scmatrix.hxx> +#include <token.hxx> + +#include <sal/log.hxx> + +namespace sc { + +FormulaResultValue::FormulaResultValue() : mfValue(0.0), meType(Invalid), mnError(FormulaError::NONE) {} +FormulaResultValue::FormulaResultValue( double fValue ) : mfValue(fValue), meType(Value), mnError(FormulaError::NONE) {} +FormulaResultValue::FormulaResultValue( const svl::SharedString& rStr ) : mfValue(0.0), maString(rStr), meType(String), mnError(FormulaError::NONE) {} +FormulaResultValue::FormulaResultValue( FormulaError nErr ) : mfValue(0.0), meType(Error), mnError(nErr) {} + +} + +ScFormulaResult::ScFormulaResult() : + mpToken(nullptr), + mbToken(true), + mbEmpty(false), + mbEmptyDisplayedAsString(false), + mbValueCached(false), + meMultiline(MULTILINE_UNKNOWN), + mnError(FormulaError::NONE) {} + +ScFormulaResult::ScFormulaResult( const ScFormulaResult & r ) : + mbToken( r.mbToken), + mbEmpty( r.mbEmpty), + mbEmptyDisplayedAsString( r.mbEmptyDisplayedAsString), + mbValueCached( r.mbValueCached), + meMultiline( r.meMultiline), + mnError( r.mnError) +{ + if (mbToken) + { + mpToken = r.mpToken; + if (mpToken) + { + // Since matrix dimension and + // results are assigned to a matrix + // cell formula token we have to + // clone that instead of sharing it. + const ScMatrixFormulaCellToken* pMatFormula = + r.GetMatrixFormulaCellToken(); + if (pMatFormula) + mpToken = new ScMatrixFormulaCellToken( *pMatFormula); + mpToken->IncRef(); + } + } + else + mfValue = r.mfValue; +} + +ScFormulaResult::ScFormulaResult( const formula::FormulaToken* p ) : + mbToken(false), + mbEmpty(false), + mbEmptyDisplayedAsString(false), + mbValueCached(false), + meMultiline(MULTILINE_UNKNOWN), + mnError(FormulaError::NONE) +{ + SetToken( p); +} + +ScFormulaResult::~ScFormulaResult() +{ + if (mbToken && mpToken) + mpToken->DecRef(); +} + +void ScFormulaResult::ResetToDefaults() +{ + mnError = FormulaError::NONE; + mbEmpty = false; + mbEmptyDisplayedAsString = false; + meMultiline = MULTILINE_UNKNOWN; + mbValueCached = false; +} + +void ScFormulaResult::ResolveToken( const formula::FormulaToken * p ) +{ + ResetToDefaults(); + if (!p) + { + mpToken = p; + mbToken = true; + } + else + { + switch (p->GetType()) + { + case formula::svError: + mnError = p->GetError(); + p->DecRef(); + mbToken = false; + // set in case mnError is 0 now, which shouldn't happen but ... + mfValue = 0.0; + meMultiline = MULTILINE_FALSE; + break; + case formula::svEmptyCell: + mbEmpty = true; + mbEmptyDisplayedAsString = static_cast<const ScEmptyCellToken*>(p)->IsDisplayedAsString(); + p->DecRef(); + mbToken = false; + meMultiline = MULTILINE_FALSE; + // Take advantage of fast double result return for empty result token. + // by setting mfValue to 0 and turning on mbValueCached flag. + mfValue = 0.0; + mbValueCached = true; + break; + case formula::svDouble: + mfValue = p->GetDouble(); + p->DecRef(); + mbToken = false; + meMultiline = MULTILINE_FALSE; + mbValueCached = true; + break; + default: + mpToken = p; + mbToken = true; + } + } +} + +ScFormulaResult & ScFormulaResult::operator=( const ScFormulaResult & r ) +{ + Assign( r); + return *this; +} + +void ScFormulaResult::Assign( const ScFormulaResult & r ) +{ + if (this == &r) + return; + + // It is important to reset the value-cache flag to that of the source + // unconditionally. + mbValueCached = r.mbValueCached; + + if (r.mbEmpty) + { + if (mbToken && mpToken) + mpToken->DecRef(); + mbToken = false; + mbEmpty = true; + mbEmptyDisplayedAsString = r.mbEmptyDisplayedAsString; + meMultiline = r.meMultiline; + // here r.mfValue will be 0.0 which is ensured in ResolveToken(). + mfValue = 0.0; + } + else if (r.mbToken) + { + // Matrix formula cell token must be cloned, see copy-ctor. + const ScMatrixFormulaCellToken* pMatFormula = + r.GetMatrixFormulaCellToken(); + if (pMatFormula) + SetToken( new ScMatrixFormulaCellToken( *pMatFormula)); + else + SetToken( r.mpToken); + } + else + SetDouble( r.mfValue); + // If there was an error there will be an error, no matter what Set...() + // methods did. + SetResultError(r.mnError); +} + +void ScFormulaResult::SetToken( const formula::FormulaToken* p ) +{ + ResetToDefaults(); + if (p) + p->IncRef(); + // Handle a result obtained from the interpreter to be assigned to a matrix + // formula cell's ScMatrixFormulaCellToken. + ScMatrixFormulaCellToken* pMatFormula = GetMatrixFormulaCellTokenNonConst(); + if (pMatFormula) + { + const ScMatrixCellResultToken* pMatResult = + (p && p->GetType() == formula::svMatrixCell ? + dynamic_cast<const ScMatrixCellResultToken*>(p) : nullptr); + if (pMatResult) + { + const ScMatrixFormulaCellToken* pNewMatFormula = + dynamic_cast<const ScMatrixFormulaCellToken*>(pMatResult); + if (pNewMatFormula && (pMatFormula->GetMatCols() <= 0 || pMatFormula->GetMatRows() <= 0)) + { + SAL_WARN( "sc", "ScFormulaResult::SetToken: pNewMatFormula and pMatFormula, overriding matrix formula dimension; intended?"); + pMatFormula->SetMatColsRows( pNewMatFormula->GetMatCols(), + pNewMatFormula->GetMatRows()); + } + pMatFormula->Assign( *pMatResult); + p->DecRef(); + } + else if (p) + { + // This may be the result of some constant expression like + // {="string"} that doesn't result in a matrix but still would + // display the result in all cells of this matrix formula. + pMatFormula->Assign( *p); + p->DecRef(); + } + else + { + // NULL result? Well, if you say so ... + pMatFormula->ResetResult(); + } + } + else + { + if (mbToken && mpToken) + mpToken->DecRef(); + ResolveToken( p); + } +} + +void ScFormulaResult::SetDouble( double f ) +{ + ResetToDefaults(); + // Handle a result obtained from the interpreter to be assigned to a matrix + // formula cell's ScMatrixFormulaCellToken. + ScMatrixFormulaCellToken* pMatFormula = GetMatrixFormulaCellTokenNonConst(); + if (pMatFormula) + pMatFormula->SetUpperLeftDouble( f); + else + { + if (mbToken && mpToken) + mpToken->DecRef(); + mfValue = f; + mbToken = false; + meMultiline = MULTILINE_FALSE; + mbValueCached = true; + } +} + +formula::StackVar ScFormulaResult::GetType() const +{ + // Order is significant. + if (mnError != FormulaError::NONE) + return formula::svError; + if (mbEmpty) + return formula::svEmptyCell; + if (!mbToken) + return formula::svDouble; + if (mpToken) + return mpToken->GetType(); + return formula::svUnknown; +} + +formula::StackVar ScFormulaResult::GetCellResultType() const +{ + formula::StackVar sv = GetType(); + if (sv == formula::svMatrixCell) + // don't need to test for mpToken here, GetType() already did it + sv = static_cast<const ScMatrixCellResultToken*>(mpToken)->GetUpperLeftType(); + return sv; +} + +bool ScFormulaResult::IsEmptyDisplayedAsString() const +{ + if (mbEmpty) + return mbEmptyDisplayedAsString; + switch (GetType()) + { + case formula::svMatrixCell: + { + // don't need to test for mpToken here, GetType() already did it + const ScEmptyCellToken* p = dynamic_cast<const ScEmptyCellToken*>( + static_cast<const ScMatrixCellResultToken*>( + mpToken)->GetUpperLeftToken().get()); + if (p) + return p->IsDisplayedAsString(); + } + break; + case formula::svHybridCell: + { + const ScHybridCellToken* p = static_cast<const ScHybridCellToken*>(mpToken); + return p->IsEmptyDisplayedAsString(); + } + break; + default: + break; + } + return false; +} + +namespace { + +bool isValue( formula::StackVar sv ) +{ + return sv == formula::svDouble || sv == formula::svError + || sv == formula::svEmptyCell + // The initial uninitialized result value is double 0.0, even if the type + // is unknown, so the interpreter asking for it gets that double + // instead of having to convert a string which may result in #VALUE! + // (otherwise the unknown would be neither error nor double nor string) + || sv == formula::svUnknown; +} + +bool isString( formula::StackVar sv ) +{ + switch (sv) + { + case formula::svString: + case formula::svHybridCell: + return true; + default: + break; + } + + return false; +} + +} + +bool ScFormulaResult::IsValue() const +{ + if (IsEmptyDisplayedAsString()) + return true; + + return isValue(GetCellResultType()); +} + +bool ScFormulaResult::IsValueNoError() const +{ + switch (GetCellResultType()) + { + case formula::svDouble: + case formula::svEmptyCell: + return true; + default: + return false; + } +} + +bool ScFormulaResult::IsMultiline() const +{ + if (meMultiline == MULTILINE_UNKNOWN) + { + svl::SharedString aStr = GetString(); + if (!aStr.isEmpty() && aStr.getString().indexOf('\n') != -1) + const_cast<ScFormulaResult*>(this)->meMultiline = MULTILINE_TRUE; + else + const_cast<ScFormulaResult*>(this)->meMultiline = MULTILINE_FALSE; + } + return meMultiline == MULTILINE_TRUE; +} + +bool ScFormulaResult::GetErrorOrDouble( FormulaError& rErr, double& rVal ) const +{ + if (mbValueCached) + { + rVal = mfValue; + return true; + } + + if (mnError != FormulaError::NONE) + { + rErr = mnError; + return true; + } + + formula::StackVar sv = GetCellResultType(); + if (sv == formula::svError) + { + if (GetType() == formula::svMatrixCell) + { + // don't need to test for mpToken here, GetType() already did it + rErr = static_cast<const ScMatrixCellResultToken*>(mpToken)-> + GetUpperLeftToken()->GetError(); + } + else if (mpToken) + { + rErr = mpToken->GetError(); + } + } + + if (rErr != FormulaError::NONE) + return true; + + if (!isValue(sv)) + return false; + + rVal = GetDouble(); + return true; +} + +sc::FormulaResultValue ScFormulaResult::GetResult() const +{ + if (mbValueCached) + return sc::FormulaResultValue(mfValue); + + if (mnError != FormulaError::NONE) + return sc::FormulaResultValue(mnError); + + formula::StackVar sv = GetCellResultType(); + FormulaError nErr = FormulaError::NONE; + if (sv == formula::svError) + { + if (GetType() == formula::svMatrixCell) + { + // don't need to test for mpToken here, GetType() already did it + nErr = static_cast<const ScMatrixCellResultToken*>(mpToken)-> + GetUpperLeftToken()->GetError(); + } + else if (mpToken) + { + nErr = mpToken->GetError(); + } + } + + if (nErr != FormulaError::NONE) + return sc::FormulaResultValue(nErr); + + if (isValue(sv)) + return sc::FormulaResultValue(GetDouble()); + + if (!mbToken) + // String result type needs token. + return sc::FormulaResultValue(); + + if (isString(sv)) + return sc::FormulaResultValue(GetString()); + + // Invalid + return sc::FormulaResultValue(); +} + +FormulaError ScFormulaResult::GetResultError() const +{ + if (mnError != FormulaError::NONE) + return mnError; + formula::StackVar sv = GetCellResultType(); + if (sv == formula::svError) + { + if (GetType() == formula::svMatrixCell) + // don't need to test for mpToken here, GetType() already did it + return static_cast<const ScMatrixCellResultToken*>(mpToken)-> + GetUpperLeftToken()->GetError(); + if (mpToken) + return mpToken->GetError(); + } + return FormulaError::NONE; +} + +void ScFormulaResult::SetResultError( FormulaError nErr ) +{ + mnError = nErr; + if (mnError != FormulaError::NONE) + mbValueCached = false; +} + +formula::FormulaConstTokenRef ScFormulaResult::GetToken() const +{ + if (mbToken) + return mpToken; + return nullptr; +} + +formula::FormulaConstTokenRef ScFormulaResult::GetCellResultToken() const +{ + if (GetType() == formula::svMatrixCell) + // don't need to test for mpToken here, GetType() already did it + return static_cast<const ScMatrixCellResultToken*>(mpToken)->GetUpperLeftToken(); + return GetToken(); +} + +double ScFormulaResult::GetDouble() const +{ + if (mbValueCached) + return mfValue; + + if (mbToken) + { + // Should really not be of type formula::svDouble here. + if (mpToken) + { + switch (mpToken->GetType()) + { + case formula::svHybridCell: + return mpToken->GetDouble(); + case formula::svMatrixCell: + { + const ScMatrixCellResultToken* p = + static_cast<const ScMatrixCellResultToken*>(mpToken); + if (p->GetUpperLeftType() == formula::svDouble) + return p->GetUpperLeftToken()->GetDouble(); + } + break; + default: + ; // nothing + } + } + // Note that we reach here also for the default ctor and + // formula::svUnknown from GetType(). + return 0.0; + } + if (mbEmpty) + return 0.0; + return mfValue; +} + +const svl::SharedString & ScFormulaResult::GetString() const +{ + if (mbToken && mpToken) + { + switch (mpToken->GetType()) + { + case formula::svString: + case formula::svHybridCell: + return mpToken->GetString(); + case formula::svMatrixCell: + { + const ScMatrixCellResultToken* p = + static_cast<const ScMatrixCellResultToken*>(mpToken); + if (p->GetUpperLeftType() == formula::svString) + return p->GetUpperLeftToken()->GetString(); + } + break; + default: + ; // nothing + } + } + return svl::SharedString::getEmptyString(); +} + +ScConstMatrixRef ScFormulaResult::GetMatrix() const +{ + if (GetType() == formula::svMatrixCell) + return mpToken->GetMatrix(); + return nullptr; +} + +OUString ScFormulaResult::GetHybridFormula() const +{ + if (GetType() == formula::svHybridCell) + { + const ScHybridCellToken* p = static_cast<const ScHybridCellToken*>(mpToken); + return p->GetFormula(); + } + return OUString(); +} + +void ScFormulaResult::SetHybridDouble( double f ) +{ + ResetToDefaults(); + if (mbToken && mpToken) + { + if(GetType() == formula::svMatrixCell) + SetDouble(f); + else + { + svl::SharedString aString = GetString(); + OUString aFormula( GetHybridFormula()); + mpToken->DecRef(); + mpToken = new ScHybridCellToken( f, aString, aFormula, false); + mpToken->IncRef(); + } + } + else + { + mfValue = f; + mbToken = false; + meMultiline = MULTILINE_FALSE; + mbValueCached = true; + } +} + +void ScFormulaResult::SetHybridString( const svl::SharedString& rStr ) +{ + // Obtain values before changing anything. + double f = GetDouble(); + OUString aFormula( GetHybridFormula()); + ResetToDefaults(); + if (mbToken && mpToken) + mpToken->DecRef(); + mpToken = new ScHybridCellToken( f, rStr, aFormula, false); + mpToken->IncRef(); + mbToken = true; +} + +void ScFormulaResult::SetHybridEmptyDisplayedAsString() +{ + // Obtain values before changing anything. + double f = GetDouble(); + OUString aFormula( GetHybridFormula()); + svl::SharedString aStr = GetString(); + ResetToDefaults(); + if (mbToken && mpToken) + mpToken->DecRef(); + // XXX NOTE: we can't use mbEmpty and mbEmptyDisplayedAsString here because + // GetType() intentionally returns svEmptyCell if mbEmpty==true. So stick + // it into the ScHybridCellToken. + mpToken = new ScHybridCellToken( f, aStr, aFormula, true); + mpToken->IncRef(); + mbToken = true; +} + +void ScFormulaResult::SetHybridFormula( const OUString & rFormula ) +{ + // Obtain values before changing anything. + double f = GetDouble(); + svl::SharedString aStr = GetString(); + ResetToDefaults(); + if (mbToken && mpToken) + mpToken->DecRef(); + mpToken = new ScHybridCellToken( f, aStr, rFormula, false); + mpToken->IncRef(); + mbToken = true; +} + +void ScFormulaResult::SetMatrix( SCCOL nCols, SCROW nRows, const ScConstMatrixRef& pMat, const formula::FormulaToken* pUL ) +{ + ResetToDefaults(); + if (mbToken && mpToken) + mpToken->DecRef(); + mpToken = new ScMatrixFormulaCellToken(nCols, nRows, pMat, pUL); + mpToken->IncRef(); + mbToken = true; +} + +const ScMatrixFormulaCellToken* ScFormulaResult::GetMatrixFormulaCellToken() const +{ + return (GetType() == formula::svMatrixCell ? + static_cast<const ScMatrixFormulaCellToken*>(mpToken) : nullptr); +} + +ScMatrixFormulaCellToken* ScFormulaResult::GetMatrixFormulaCellTokenNonConst() +{ + return const_cast<ScMatrixFormulaCellToken*>( GetMatrixFormulaCellToken()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |