Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  formularesult.cxx   Sprache: C

 
/* -*- 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: */

Messung V0.5
C=91 H=100 G=95

¤ Dauer der Verarbeitung: 0.1 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge