/* -*- 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>
#include <utility>
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( svl::SharedString aStr, bool bMultiLine ) : mfValue(0.0), maString(std::move(aStr)), mbMultiLine(bMultiLine), meType(String), mnError(FormulaError::NONE) {}
FormulaResultValue::FormulaResultValue( FormulaError nErr ) : mfValue(0.0), meType(Error), mnError(nErr) {}
}
ScFormulaResult::ScFormulaResult() :
mpToken(nullptr),
mbToken(true),
mbNoneRefCnt(false),
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;
mbNoneRefCnt = mbToken && mpToken && mpToken->GetRefCntPolicy() == formula::RefCntPolicy::None;
}
ScFormulaResult::ScFormulaResult( const formula::FormulaToken* p ) :
mbToken(false),
mbNoneRefCnt(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;
}
}
mbNoneRefCnt = mbToken && mpToken && mpToken->GetRefCntPolicy() == formula::RefCntPolicy::None;
}
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;
mbNoneRefCnt = 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;
mbNoneRefCnt = 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(), IsMultiline());
// 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();
mbNoneRefCnt = false;
}
}
else
{
mfValue = f;
mbToken = false;
mbNoneRefCnt = 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;
mbNoneRefCnt = false;
}
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;
mbNoneRefCnt = false;
}
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;
mbNoneRefCnt = false;
}
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;
mbNoneRefCnt = false;
}
const ScMatrixFormulaCellToken* ScFormulaResult::GetMatrixFormulaCellToken() const
{
return (GetType() == formula::svMatrixCell ?
static_cast<const ScMatrixFormulaCellToken*>(mpToken) : nullptr);
}
ScMatrixFormulaCellToken* ScFormulaResult::GetMatrixFormulaCellTokenNonConst()
{
return const_cast<ScMatrixFormulaCellToken*>( GetMatrixFormulaCellToken());
}
// If a token from the original tokens, supplied to a parallel group calculation
// while RefCounting was disabled for those tokens, ends up as a FormulaResult
// token, then fix up the ref count now
void ScFormulaResult::HandleStuffAfterParallelCalculation()
{
if (mbNoneRefCnt)
{
assert(mbToken && mpToken && mpToken->GetRefCntPolicy() != formula::RefCntPolicy::None);
mpToken->IncRef();
mbNoneRefCnt = false;
}
// If ScInterpreter::CreateFormulaDoubleToken tokens make it into a result
if (mbToken && mpToken)
{
// I don't see any evidence that this can happen, but assert if it arises
assert(mpToken->GetRefCntPolicy() == formula::RefCntPolicy::ThreadSafe);
const_cast<formula::FormulaToken*>(mpToken)->SetRefCntPolicy(formula::RefCntPolicy::ThreadSafe);
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V773 The 'mpToken' pointer was not released in destructor. A memory leak is possible.