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


Quelle  formulacell.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
 * 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/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */


#include <config_feature_opencl.h>

#include <sal/config.h>
#include <sal/log.hxx>
#include <osl/diagnose.h>

#include <cassert>
#include <cstdlib>

#include <formulacell.hxx>
#include <grouptokenconverter.hxx>

#include <compiler.hxx>
#include <document.hxx>
#include <cellvalue.hxx>
#include <interpre.hxx>
#include <macromgr.hxx>
#include <refupdat.hxx>
#include <recursionhelper.hxx>
#include <docoptio.hxx>
#include <rangenam.hxx>
#include <rangelst.hxx>
#include <dbdata.hxx>
#include <progress.hxx>
#include <scmatrix.hxx>
#include <rechead.hxx>
#include <scitems.hxx>
#include <validat.hxx>
#include <editutil.hxx>
#include <chgtrack.hxx>
#include <tokenarray.hxx>

#include <comphelper/threadpool.hxx>
#include <editeng/editobj.hxx>
#include <formula/errorcodes.hxx>
#include <svl/intitem.hxx>
#include <svl/numformat.hxx>
#include <formulagroup.hxx>
#include <listenercontext.hxx>
#include <types.hxx>
#include <scopetools.hxx>
#include <refupdatecontext.hxx>
#include <tokenstringcontext.hxx>
#include <refhint.hxx>
#include <listenerquery.hxx>
#include <listenerqueryids.hxx>
#include <grouparealistener.hxx>
#include <formulalogger.hxx>
#include <com/sun/star/sheet/FormulaLanguage.hpp>

#if HAVE_FEATURE_OPENCL
#include <opencl/openclwrapper.hxx>
#endif

#include <memory>
#include <map>

using namespace formula;

#define DEBUG_CALCULATION 0
#if DEBUG_CALCULATION
static bool bDebugCalculationActive = false;                // Set to true for global active init,
static ScAddress aDebugCalculationTriggerAddress(1,2,0);    // or on cell Sheet1.B3, whatever you like

struct DebugCalculationEntry
{
          ScAddress     maPos;
          OUString      maResult;
    const ScDocument&   mrDoc;
          sal_uInt32    mnGroup;
          sal_uInt16    mnRecursion;

    DebugCalculationEntry( const ScAddress& rPos, ScDocument& rDoc, sal_uInt32 nGroup ) :
        maPos(rPos),
        mrDoc(rDoc),
        mnGroup(nGroup),
        mnRecursion(rDoc.GetRecursionHelper().GetRecursionCount())
    {
    }
};

/** Debug/dump formula cell calculation chain.
    Either, somewhere set aDC.mbActive=true, or
    aDC.maTrigger=ScAddress(col,row,tab) of interest from where to start.
    This does not work for deep recursion > MAXRECURSION, the results are
    somewhat... funny... ;)
 */

static struct DebugCalculation
{
    std::vector< DebugCalculationEntry >    mvPos;
    std::vector< DebugCalculationEntry >    mvResults;
    ScAddress                               maTrigger;
    sal_uInt32                              mnGroup;
    bool                                    mbActive;
    bool                                    mbSwitchOff;
    bool                                    mbPrint;
    bool                                    mbPrintResults;

    DebugCalculation() : mnGroup(0), mbActive(bDebugCalculationActive), mbSwitchOff(false),
    mbPrint(true), mbPrintResults(false) {}

    /** Print chain in encountered dependency order. */
    void print() const
    {
        for (auto const& it : mvPos)
        {
            OUString aStr( it.maPos.Format( ScRefFlags::VALID | ScRefFlags::TAB_3D, &it.mrDoc) +
                    " [" + OUString::number( it.mnRecursion) + "," + OUString::number( it.mnGroup) + "]");
            fprintf( stderr, "%s -> ", aStr.toUtf8().getStr());
        }
        fprintf( stderr, "%s""END\n");
    }

    /** Print chain results. */
    void printResults() const
    {
        for (auto const& it : mvResults)
        {
            OUString aStr( it.maPos.Format( ScRefFlags::VALID | ScRefFlags::TAB_3D, &it.mrDoc));
            aStr += " (" + it.maResult + ")";
            fprintf( stderr, "%s, ", aStr.toUtf8().getStr());
        }
        fprintf( stderr, "%s""END\n");
    }

    void storeResult( const svl::SharedString& rStr )
    {
        if (mbActive && !mvPos.empty())
            mvPos.back().maResult = "\"" + rStr.getString() + "\"";
    }

    void storeResult( const double& fVal )
    {
        if (mbActive && !mvPos.empty())
            mvPos.back().maResult = rtl::math::doubleToUString( fVal, rtl_math_StringFormat_G, 2, '.'true);
    }

    void storeResultError( FormulaError nErr )
    {
        if (mbActive && !mvPos.empty())
            mvPos.back().maResult = "Err:" + OUString::number( int( nErr ));
    }

    void enterGroup()
    {
        ++mnGroup;
    }

    void leaveGroup()
    {
        --mnGroup;
    }

} aDC;

struct DebugCalculationStacker
{
    DebugCalculationStacker( const ScAddress& rPos, ScDocument& rDoc )
    {
        if (!aDC.mbActive && rPos == aDC.maTrigger)
            aDC.mbActive = aDC.mbSwitchOff = true;
        if (aDC.mbActive)
        {
            aDC.mvPos.push_back( DebugCalculationEntry( rPos, rDoc, aDC.mnGroup));
            aDC.mbPrint = true;
        }
    }

    ~DebugCalculationStacker()
    {
        if (aDC.mbActive)
        {
            if (!aDC.mvPos.empty())
            {
                if (aDC.mbPrint)
                {
                    aDC.print();
                    aDC.mbPrint = false;
                }
                if (aDC.mbPrintResults)
                {
                    // Store results until final result is available, reversing order.
                    aDC.mvResults.push_back( aDC.mvPos.back());
                }
                aDC.mvPos.pop_back();
                if (aDC.mbPrintResults && aDC.mvPos.empty())
                {
                    aDC.printResults();
                    std::vector< DebugCalculationEntry >().swap( aDC.mvResults);
                }
                if (aDC.mbSwitchOff && aDC.mvPos.empty())
                    aDC.mbActive = false;
            }
        }
    }
};
#endif

namespace {

// More or less arbitrary, of course all recursions must fit into available
// stack space (which is what on all systems we don't know yet?). Choosing a
// lower value may be better than trying a much higher value that also isn't
// sufficient but temporarily leads to high memory consumption. On the other
// hand, if the value fits all recursions, execution is quicker as no resumes
// are necessary. Could be made a configurable option.
// Allow for a year's calendar (366).
const sal_uInt16 MAXRECURSION = 400;

typedef SCCOLROW(*DimensionSelector)(const ScDocument&, const ScAddress&, const ScSingleRefData&);

SCCOLROW lcl_GetCol(const ScDocument& rDoc, const ScAddress& rPos, const ScSingleRefData& rData)
{
    return rData.toAbs(rDoc, rPos).Col();
}

SCCOLROW lcl_GetRow(const ScDocument& rDoc, const ScAddress& rPos, const ScSingleRefData& rData)
{
    return rData.toAbs(rDoc, rPos).Row();
}

SCCOLROW lcl_GetTab(const ScDocument& rDoc, const ScAddress& rPos, const ScSingleRefData& rData)
{
    return rData.toAbs(rDoc, rPos).Tab();
}

/** Check if both references span the same range in selected dimension.
 */

bool
lcl_checkRangeDimension(
    const ScDocument& rDoc,
    const ScAddress& rPos, const SingleDoubleRefProvider& rRef1, const SingleDoubleRefProvider& rRef2,
    const DimensionSelector aWhich)
{
    return aWhich(rDoc, rPos, rRef1.Ref1) == aWhich(rDoc, rPos, rRef2.Ref1) &&
        aWhich(rDoc, rPos, rRef1.Ref2) == aWhich(rDoc, rPos, rRef2.Ref2);
}

bool
lcl_checkRangeDimensions(
    const ScDocument& rDoc,
    const ScAddress& rPos, const SingleDoubleRefProvider& rRef1, const SingleDoubleRefProvider& rRef2,
    bool& bCol, bool& bRow, bool& bTab)
{
    const bool bSameCols(lcl_checkRangeDimension(rDoc, rPos, rRef1, rRef2, lcl_GetCol));
    const bool bSameRows(lcl_checkRangeDimension(rDoc, rPos, rRef1, rRef2, lcl_GetRow));
    const bool bSameTabs(lcl_checkRangeDimension(rDoc, rPos, rRef1, rRef2, lcl_GetTab));

    // Test if exactly two dimensions are equal
    if (int(bSameCols) + int(bSameRows) + int(bSameTabs) == 2)
    {
        bCol = !bSameCols;
        bRow = !bSameRows;
        bTab = !bSameTabs;
        return true;
    }
    return false;
}

/** Check if references in given reference list can possibly
    form a range. To do that, two of their dimensions must be the same.
 */

bool
lcl_checkRangeDimensions(
    const ScDocument& rDoc, const ScAddress& rPos,
    const std::vector<formula::FormulaToken*>::const_iterator& rBegin,
    const std::vector<formula::FormulaToken*>::const_iterator& rEnd,
    bool& bCol, bool& bRow, bool& bTab)
{
    std::vector<formula::FormulaToken*>::const_iterator aCur(rBegin);
    ++aCur;
    const SingleDoubleRefProvider aRef(**rBegin);
    bool bOk(false);
    {
        const SingleDoubleRefProvider aRefCur(**aCur);
        bOk = lcl_checkRangeDimensions(rDoc, rPos, aRef, aRefCur, bCol, bRow, bTab);
    }
    while (bOk && aCur != rEnd)
    {
        const SingleDoubleRefProvider aRefCur(**aCur);
        bool bColTmp(false);
        bool bRowTmp(false);
        bool bTabTmp(false);
        bOk = lcl_checkRangeDimensions(rDoc, rPos, aRef, aRefCur, bColTmp, bRowTmp, bTabTmp);
        bOk = bOk && (bCol == bColTmp && bRow == bRowTmp && bTab == bTabTmp);
        ++aCur;
    }

    return bOk && aCur == rEnd;
}

class LessByReference
{
    const ScDocument& mrDoc;
    ScAddress         maPos;
    DimensionSelector maFunc;
public:
    LessByReference(const ScDocument& rDoc, const ScAddress& rPos, const DimensionSelector& rFunc) :
        mrDoc(rDoc), maPos(rPos), maFunc(rFunc) {}

    bool operator() (const formula::FormulaToken* pRef1, const formula::FormulaToken* pRef2)
    {
        const SingleDoubleRefProvider aRef1(*pRef1);
        const SingleDoubleRefProvider aRef2(*pRef2);
        return maFunc(mrDoc, maPos, aRef1.Ref1) < maFunc(mrDoc, maPos, aRef2.Ref1);
    }
};

/**
 * Returns true if range denoted by token p2 starts immediately after range
 * denoted by token p1. Dimension, in which the comparison takes place, is
 * given by maFunc.
 */

class AdjacentByReference
{
    const ScDocument& mrDoc;
    ScAddress         maPos;
    DimensionSelector maFunc;
public:
    AdjacentByReference(const ScDocument& rDoc, const ScAddress& rPos, DimensionSelector aFunc) :
        mrDoc(rDoc), maPos(rPos), maFunc(aFunc) {}

    bool operator() (const formula::FormulaToken* p1, const formula::FormulaToken* p2)
    {
        const SingleDoubleRefProvider aRef1(*p1);
        const SingleDoubleRefProvider aRef2(*p2);
        return maFunc(mrDoc, maPos, aRef2.Ref1) - maFunc(mrDoc, maPos, aRef1.Ref2) == 1;
    }
};

bool
lcl_checkIfAdjacent(
    const ScDocument& rDoc,
    const ScAddress& rPos, const std::vector<formula::FormulaToken*>& rReferences, const DimensionSelector aWhich)
{
    auto aBegin(rReferences.cbegin());
    auto aEnd(rReferences.cend());
    auto aBegin1(aBegin);
    ++aBegin1;
    --aEnd;
    return std::equal(aBegin, aEnd, aBegin1, AdjacentByReference(rDoc, rPos, aWhich));
}

void
lcl_fillRangeFromRefList(
    const ScDocument& rDoc,
    const ScAddress& aPos, const std::vector<formula::FormulaToken*>& rReferences, ScRange& rRange)
{
    const ScSingleRefData aStart(
            SingleDoubleRefProvider(*rReferences.front()).Ref1);
    rRange.aStart = aStart.toAbs(rDoc, aPos);
    const ScSingleRefData aEnd(
            SingleDoubleRefProvider(*rReferences.back()).Ref2);
    rRange.aEnd = aEnd.toAbs(rDoc, aPos);
}

bool
lcl_refListFormsOneRange(
        const ScDocument& rDoc,
        const ScAddress& rPos, std::vector<formula::FormulaToken*>& rReferences,
        ScRange& rRange)
{
    if (rReferences.size() == 1)
    {
        lcl_fillRangeFromRefList(rDoc, rPos, rReferences, rRange);
        return true;
    }

    bool bCell(false);
    bool bRow(false);
    bool bTab(false);
    if (lcl_checkRangeDimensions(rDoc, rPos, rReferences.begin(), rReferences.end(), bCell, bRow, bTab))
    {
        DimensionSelector aWhich;
        if (bCell)
        {
            aWhich = lcl_GetCol;
        }
        else if (bRow)
        {
            aWhich = lcl_GetRow;
        }
        else if (bTab)
        {
            aWhich = lcl_GetTab;
        }
        else
        {
            OSL_FAIL( "lcl_checkRangeDimensions shouldn't allow that!");
            aWhich = lcl_GetRow;    // initialize to avoid warning
        }

        // Sort the references by start of range
        std::sort(rReferences.begin(), rReferences.end(), LessByReference(rDoc, rPos, aWhich));
        if (lcl_checkIfAdjacent(rDoc, rPos, rReferences, aWhich))
        {
            lcl_fillRangeFromRefList(rDoc, rPos, rReferences, rRange);
            return true;
        }
    }
    return false;
}

bool lcl_isReference(const FormulaToken& rToken)
{
    return
        rToken.GetType() == svSingleRef ||
        rToken.GetType() == svDoubleRef;
}

void adjustRangeName(formula::FormulaToken* pToken, ScDocument& rNewDoc, const ScDocument& rOldDoc,
        const ScAddress& rNewPos, const ScAddress& rOldPos, bool bGlobalNamesToLocal)
{
    ScRangeData* pRangeData = nullptr;
    SCTAB nSheet = pToken->GetSheet();
    sal_uInt16 nIndex = pToken->GetIndex();
    if (!rOldDoc.CopyAdjustRangeName( nSheet, nIndex, pRangeData, rNewDoc, rNewPos, rOldPos, bGlobalNamesToLocal, true))
        return// nothing to do

    if (!pRangeData)
    {
        // If this happened we have a real problem.
        pToken->SetIndex(0);
        assert(!"inserting the range name should not fail");
        return;
    }

    pToken->SetIndex(nIndex);
    pToken->SetSheet(nSheet);
}

void adjustDBRange(formula::FormulaToken* pToken, ScDocument& rNewDoc, const ScDocument& rOldDoc)
{
    ScDBCollection* pOldDBCollection = rOldDoc.GetDBCollection();
    if (!pOldDBCollection)
        return;//strange error case, don't do anything
    ScDBCollection::NamedDBs& aOldNamedDBs = pOldDBCollection->getNamedDBs();
    ScDBData* pDBData = aOldNamedDBs.findByIndex(pToken->GetIndex());
    if (!pDBData)
        return//invalid index
    OUString aDBName = pDBData->GetUpperName();

    //search in new document
    ScDBCollection* pNewDBCollection = rNewDoc.GetDBCollection();
    if (!pNewDBCollection)
    {
        rNewDoc.SetDBCollection(std::unique_ptr<ScDBCollection>(new ScDBCollection(rNewDoc)));
        pNewDBCollection = rNewDoc.GetDBCollection();
    }
    ScDBCollection::NamedDBs& aNewNamedDBs = pNewDBCollection->getNamedDBs();
    ScDBData* pNewDBData = aNewNamedDBs.findByUpperName(aDBName);
    if (!pNewDBData)
    {
        pNewDBData = new ScDBData(*pDBData);
        bool ins = aNewNamedDBs.insert(std::unique_ptr<ScDBData>(pNewDBData));
        assert(ins); (void)ins;
    }
    pToken->SetIndex(pNewDBData->GetIndex());
}

}

bool AreaListenerKey::operator < ( const AreaListenerKey& r ) const
{
    if (maRange.aStart.Tab() != r.maRange.aStart.Tab())
        return maRange.aStart.Tab() < r.maRange.aStart.Tab();
    if (maRange.aStart.Col() != r.maRange.aStart.Col())
        return maRange.aStart.Col() < r.maRange.aStart.Col();
    if (maRange.aStart.Row() != r.maRange.aStart.Row())
        return maRange.aStart.Row() < r.maRange.aStart.Row();
    if (maRange.aEnd.Tab() != r.maRange.aEnd.Tab())
        return maRange.aEnd.Tab() < r.maRange.aEnd.Tab();
    if (maRange.aEnd.Col() != r.maRange.aEnd.Col())
        return maRange.aEnd.Col() < r.maRange.aEnd.Col();
    if (maRange.aEnd.Row() != r.maRange.aEnd.Row())
        return maRange.aEnd.Row() < r.maRange.aEnd.Row();
    if (mbStartFixed != r.mbStartFixed)
        return r.mbStartFixed;
    if (mbEndFixed != r.mbEndFixed)
        return r.mbEndFixed;

    return false;
}

ScFormulaCellGroup::ScFormulaCellGroup() :
    mnRefCount(0),
    mpTopCell(nullptr),
    mnLength(0),
    mnWeight(0),
    mnFormatType(SvNumFormatType::NUMBER),
    mbInvariant(false),
    mbSubTotal(false),
    mbPartOfCycle(false),
    meCalcState(sc::GroupCalcEnabled)
{
}

ScFormulaCellGroup::~ScFormulaCellGroup()
{
}

void ScFormulaCellGroup::setCode( const ScTokenArray& rCode )
{
    mpCode = rCode.CloneValue();
    mbInvariant = mpCode->IsInvariant();
    mpCode->GenHash();
}

void ScFormulaCellGroup::compileCode(
    ScDocument& rDoc, const ScAddress& rPos, FormulaGrammar::Grammar eGram )
{
    if (!mpCode)
        return;

    if (mpCode->GetLen() && mpCode->GetCodeError() == FormulaError::NONE && !mpCode->GetCodeLen())
    {
        bool bMatrixFormula = mpTopCell->GetMatrixFlag() != ScMatrixMode::NONE;
        ScCompiler aComp(rDoc, rPos, *mpCode, eGram, true, bMatrixFormula);
        mbSubTotal = aComp.CompileTokenArray();
        mnFormatType = aComp.GetNumFormatType();
    }
    else
    {
        mbSubTotal = mpCode->HasOpCodeRPN( ocSubTotal ) || mpCode->HasOpCodeRPN( ocAggregate );
    }
}

sc::FormulaGroupAreaListener* ScFormulaCellGroup::getAreaListener(
    ScFormulaCell** ppTopCell, const ScRange& rRange, bool bStartFixed, bool bEndFixed )
{
    AreaListenerKey aKey(rRange, bStartFixed, bEndFixed);

    AreaListenersType::iterator it = m_AreaListeners.lower_bound(aKey);
    if (it == m_AreaListeners.end() || m_AreaListeners.key_comp()(aKey, it->first))
    {
        // Insert a new one.
        it = m_AreaListeners.emplace_hint(
             it, std::piecewise_construct,
             std::forward_as_tuple(aKey),
             std::forward_as_tuple(
                rRange, (*ppTopCell)->GetDocument(), (*ppTopCell)->aPos, mnLength, bStartFixed, bEndFixed));
    }

    return &it->second;
}

void ScFormulaCellGroup::endAllGroupListening( ScDocument& rDoc )
{
    for (auto& rEntry : m_AreaListeners)
    {
        sc::FormulaGroupAreaListener& rListener = rEntry.second;
        ScRange aListenRange = rListener.getListeningRange();
        // This "always listen" special range is never grouped.
        bool bGroupListening = (aListenRange != BCA_LISTEN_ALWAYS);
        rDoc.EndListeningArea(aListenRange, bGroupListening, &rListener);
    }

    m_AreaListeners.clear();
}

ScFormulaCell::ScFormulaCell( ScDocument& rDoc, const ScAddress& rPos ) :
    bDirty(false),
    bTableOpDirty(false),
    bChanged(false),
    bRunning(false),
    bCompile(false),
    bSubTotal(false),
    bIsIterCell(false),
    bInChangeTrack(false),
    bNeedListening(false),
    mbNeedsNumberFormat(false),
    mbAllowNumberFormatChange(false),
    mbPostponedDirty(false),
    mbIsExtRef(false),
    mbSeenInPath(false),
    mbFreeFlying(false),
    cMatrixFlag(ScMatrixMode::NONE),
    nSeenInIteration(0),
    nFormatType(SvNumFormatType::NUMBER),
    eTempGrammar(formula::FormulaGrammar::GRAM_DEFAULT),
    pCode(new ScTokenArray(rDoc)),
    rDocument(rDoc),
    pPrevious(nullptr),
    pNext(nullptr),
    pPreviousTrack(nullptr),
    pNextTrack(nullptr),
    aPos(rPos)
{
}

ScFormulaCell::ScFormulaCell( ScDocument& rDoc, const ScAddress& rPos,
                              const OUString& rFormula,
                              const FormulaGrammar::Grammar eGrammar,
                              ScMatrixMode cMatInd ) :
    bDirty( true ), // -> Because of the use of the Auto Pilot Function was: cMatInd != 0
    bTableOpDirty( false ),
    bChanged( false ),
    bRunning( false ),
    bCompile( false ),
    bSubTotal( false ),
    bIsIterCell( false ),
    bInChangeTrack( false ),
    bNeedListening( false ),
    mbNeedsNumberFormat( false ),
    mbAllowNumberFormatChange(false),
    mbPostponedDirty(false),
    mbIsExtRef(false),
    mbSeenInPath(false),
    mbFreeFlying(false),
    cMatrixFlag ( cMatInd ),
    nSeenInIteration(0),
    nFormatType ( SvNumFormatType::NUMBER ),
    eTempGrammar( eGrammar),
    pCode( nullptr ),
    rDocument( rDoc ),
    pPrevious(nullptr),
    pNext(nullptr),
    pPreviousTrack(nullptr),
    pNextTrack(nullptr),
    aPos(rPos)
{
    Compile( rFormula, true, eGrammar );    // bNoListening, Insert does that
    if (!pCode)
        // We need to have a non-NULL token array instance at all times.
        pCode = new ScTokenArray(rDoc);
}

ScFormulaCell::ScFormulaCell(
    ScDocument& rDoc, const ScAddress& rPos, std::unique_ptr<ScTokenArray> pArray,
    const FormulaGrammar::Grammar eGrammar, ScMatrixMode cMatInd ) :
    bDirty( true ),
    bTableOpDirty( false ),
    bChanged( false ),
    bRunning( false ),
    bCompile( false ),
    bSubTotal( false ),
    bIsIterCell( false ),
    bInChangeTrack( false ),
    bNeedListening( false ),
    mbNeedsNumberFormat( false ),
    mbAllowNumberFormatChange(false),
    mbPostponedDirty(false),
    mbIsExtRef(false),
    mbSeenInPath(false),
    mbFreeFlying(false),
    cMatrixFlag ( cMatInd ),
    nSeenInIteration(0),
    nFormatType ( SvNumFormatType::NUMBER ),
    eTempGrammar( eGrammar),
    pCode(pArray.release()),
    rDocument( rDoc ),
    pPrevious(nullptr),
    pNext(nullptr),
    pPreviousTrack(nullptr),
    pNextTrack(nullptr),
    aPos(rPos)
{
    assert(pCode); // Never pass a NULL pointer here.

    pCode->Finalize(); // Reduce memory usage if needed.

    // Generate RPN token array.
    if (pCode->GetLen() && pCode->GetCodeError() == FormulaError::NONE && !pCode->GetCodeLen())
    {
        ScCompiler aComp(rDocument, aPos, *pCode, eTempGrammar, true, cMatrixFlag != ScMatrixMode::NONE);
        bSubTotal = aComp.CompileTokenArray();
        nFormatType = aComp.GetNumFormatType();
    }
    else
    {
        if ( pCode->HasOpCodeRPN( ocSubTotal ) || pCode->HasOpCodeRPN( ocAggregate ) )
            bSubTotal = true;
    }

    if (bSubTotal)
        rDocument.AddSubTotalCell(this);

    pCode->GenHash();
}

ScFormulaCell::ScFormulaCell(
    ScDocument& rDoc, const ScAddress& rPos, const ScTokenArray& rArray,
    const FormulaGrammar::Grammar eGrammar, ScMatrixMode cMatInd ) :
    bDirty( true ),
    bTableOpDirty( false ),
    bChanged( false ),
    bRunning( false ),
    bCompile( false ),
    bSubTotal( false ),
    bIsIterCell( false ),
    bInChangeTrack( false ),
    bNeedListening( false ),
    mbNeedsNumberFormat( false ),
    mbAllowNumberFormatChange(false),
    mbPostponedDirty(false),
    mbIsExtRef(false),
    mbSeenInPath(false),
    mbFreeFlying(false),
    cMatrixFlag ( cMatInd ),
    nSeenInIteration(0),
    nFormatType ( SvNumFormatType::NUMBER ),
    eTempGrammar( eGrammar),
    pCode(new ScTokenArray(rArray)), // also implicitly does Finalize() on the array
    rDocument( rDoc ),
    pPrevious(nullptr),
    pNext(nullptr),
    pPreviousTrack(nullptr),
    pNextTrack(nullptr),
    aPos(rPos)
{
    // RPN array generation
    if( pCode->GetLen() && pCode->GetCodeError() == FormulaError::NONE && !pCode->GetCodeLen() )
    {
        ScCompiler aComp( rDocument, aPos, *pCode, eTempGrammar, true, cMatrixFlag != ScMatrixMode::NONE );
        bSubTotal = aComp.CompileTokenArray();
        nFormatType = aComp.GetNumFormatType();
    }
    else
    {
        if ( pCode->HasOpCodeRPN( ocSubTotal ) || pCode->HasOpCodeRPN( ocAggregate ) )
            bSubTotal = true;
    }

    if (bSubTotal)
        rDocument.AddSubTotalCell(this);

    pCode->GenHash();
}

ScFormulaCell::ScFormulaCell(
    ScDocument& rDoc, const ScAddress& rPos, const ScFormulaCellGroupRef& xGroup,
    const FormulaGrammar::Grammar eGrammar, ScMatrixMode cInd ) :
    mxGroup(xGroup),
    bDirty(true),
    bTableOpDirty( false ),
    bChanged( false ),
    bRunning( false ),
    bCompile( false ),
    bSubTotal(xGroup->mbSubTotal),
    bIsIterCell( false ),
    bInChangeTrack( false ),
    bNeedListening( false ),
    mbNeedsNumberFormat( false ),
    mbAllowNumberFormatChange(false),
    mbPostponedDirty(false),
    mbIsExtRef(false),
    mbSeenInPath(false),
    mbFreeFlying(false),
    cMatrixFlag ( cInd ),
    nSeenInIteration(0),
    nFormatType(xGroup->mnFormatType),
    eTempGrammar( eGrammar),
    pCode(xGroup->mpCode ? &*xGroup->mpCode : new ScTokenArray(rDoc)),
    rDocument( rDoc ),
    pPrevious(nullptr),
    pNext(nullptr),
    pPreviousTrack(nullptr),
    pNextTrack(nullptr),
    aPos(rPos)
{
    if (bSubTotal)
        rDocument.AddSubTotalCell(this);
}

ScFormulaCell::ScFormulaCell(const ScFormulaCell& rCell, ScDocument& rDoc, const ScAddress& rPos, ScCloneFlags nCloneFlags) :
    bDirty( rCell.bDirty ),
    bTableOpDirty( false ),
    bChanged( rCell.bChanged ),
    bRunning( false ),
    bCompile( rCell.bCompile ),
    bSubTotal( rCell.bSubTotal ),
    bIsIterCell( false ),
    bInChangeTrack( false ),
    bNeedListening( false ),
    mbNeedsNumberFormat( rCell.mbNeedsNumberFormat ),
    mbAllowNumberFormatChange(false),
    mbPostponedDirty(false),
    mbIsExtRef(false),
    mbSeenInPath(false),
    mbFreeFlying(false),
    cMatrixFlag ( rCell.cMatrixFlag ),
    nSeenInIteration(0),
    nFormatType( rCell.nFormatType ),
    aResult( rCell.aResult ),
    eTempGrammar( rCell.eTempGrammar),
    rDocument( rDoc ),
    pPrevious(nullptr),
    pNext(nullptr),
    pPreviousTrack(nullptr),
    pNextTrack(nullptr),
    aPos(rPos)
{
    pCode = rCell.pCode->Clone().release();

    //  set back any errors and recompile
    //  not in the Clipboard - it must keep the received error flag
    //  Special Length=0: as bad cells are generated, then they are also retained
    if ( pCode->GetCodeError() != FormulaError::NONE && !rDocument.IsClipboard() && pCode->GetLen() )
    {
        pCode->SetCodeError( FormulaError::NONE );
        bCompile = true;
    }
    // Compile ColRowNames on URM_MOVE/URM_COPY _after_ UpdateReference !
    bool bCompileLater = false;
    bool bClipMode = rCell.rDocument.IsClipboard();

    //update ScNameTokens
    if (!rDocument.IsClipOrUndo() || rDoc.IsUndo())
    {
        if (!rDocument.IsClipboardSource() || aPos.Tab() != rCell.aPos.Tab())
        {
            bool bGlobalNamesToLocal = ((nCloneFlags & ScCloneFlags::NamesToLocal) != ScCloneFlags::Default);
            formula::FormulaToken* pToken = nullptr;
            formula::FormulaTokenArrayPlainIterator aIter(*pCode);
            while((pToken = aIter.GetNextName())!= nullptr)
            {
                OpCode eOpCode = pToken->GetOpCode();
                if (eOpCode == ocName)
                    adjustRangeName(pToken, rDoc, rCell.rDocument, aPos, rCell.aPos, bGlobalNamesToLocal);
                else if (eOpCode == ocDBArea || eOpCode == ocTableRef)
                    adjustDBRange(pToken, rDoc, rCell.rDocument);
            }
        }

        bool bCopyBetweenDocs = rDocument.GetPool() != rCell.rDocument.GetPool();
        if (bCopyBetweenDocs && !(nCloneFlags & ScCloneFlags::NoMakeAbsExternal))
        {
            pCode->ReadjustAbsolute3DReferences(rCell.rDocument, rDoc, rCell.aPos);
        }

        pCode->AdjustAbsoluteRefs( rCell.rDocument, rCell.aPos, aPos, bCopyBetweenDocs );
    }

    if (!rDocument.IsClipOrUndo())
    {
        if (&rDocument.GetSharedStringPool() != &rCell.rDocument.GetSharedStringPool())
            pCode->ReinternStrings( rDocument.GetSharedStringPool());
        pCode->AdjustReferenceOnCopy( aPos);
    }

    if( !bCompile )
    {   // Name references with references and ColRowNames
        formula::FormulaTokenArrayPlainIterator aIter(*pCode);
        for (;;)
        {
            formula::FormulaToken* t = aIter.GetNextReferenceOrName();
            if (!t || bCompile)
                break;
            if ( t->IsExternalRef() )
            {
                // External name, cell, and area references.
                bCompile = true;
            }
            else if ( t->GetType() == svIndex )
            {
                const ScRangeData* pRangeData = rDoc.FindRangeNameBySheetAndIndex( t->GetSheet(), t->GetIndex());
                if( pRangeData )
                {
                    if( pRangeData->HasReferences() )
                        bCompile = true;
                }
                else
                    bCompile = true;    // invalid reference!
            }
            else if ( t->GetOpCode() == ocColRowName )
            {
                bCompile = true;        // new lookup needed
                bCompileLater = bClipMode;
            }
        }
    }
    if( bCompile )
    {
        if ( !bCompileLater && bClipMode )
        {
            // Merging ranges needs the actual positions after UpdateReference.
            // ColRowNames and TableRefs need new lookup after positions are
            // adjusted.
            bCompileLater = pCode->HasOpCode( ocRange) || pCode->HasOpCode( ocColRowName) ||
                pCode->HasOpCode( ocTableRef);
        }
        if ( !bCompileLater )
        {
            // bNoListening, not at all if in Clipboard/Undo,
            // and not from Clipboard either, instead after Insert(Clone) and UpdateReference.
            CompileTokenArray( true );
        }
    }

    if( nCloneFlags & ScCloneFlags::StartListening )
        StartListeningTo( rDoc );

    if (bSubTotal)
        rDocument.AddSubTotalCell(this);
}

ScFormulaCell::~ScFormulaCell()
{
    rDocument.RemoveFromFormulaTrack( this );
    rDocument.RemoveFromFormulaTree( this );
    rDocument.RemoveSubTotalCell(this);
    if (pCode->HasOpCode(ocMacro))
        rDocument.GetMacroManager()->RemoveDependentCell(this);

    if (rDocument.HasExternalRefManager())
        rDocument.GetExternalRefManager()->removeRefCell(this);

    if (!mxGroup || !mxGroup->mpCode)
        // Formula token is not shared.
        delete pCode;

    if (mxGroup && mxGroup->mpTopCell == this)
        mxGroup->mpTopCell = nullptr;
}

ScFormulaCell* ScFormulaCell::Clone() const
{
    return new ScFormulaCell(*this, rDocument, aPos);
}

ScFormulaCell* ScFormulaCell::Clone( const ScAddress& rPos ) const
{
    return new ScFormulaCell(*this, rDocument, rPos, ScCloneFlags::Default);
}

size_t ScFormulaCell::GetHash() const
{
    return pCode->GetHash();
}

OUString ScFormulaCell::GetFormula( const FormulaGrammar::Grammar eGrammar, ScInterpreterContext* pContext ) const
{
    if( pCode->GetCodeError() != FormulaError::NONE && !pCode->GetLen() )
    {
        return ScGlobal::GetErrorString(pCode->GetCodeError());
    }
    OUStringBuffer buffer;
    if( cMatrixFlag == ScMatrixMode::Reference )
    {
        // Reference to another cell that contains a matrix formula.
        formula::FormulaTokenArrayPlainIterator aIter(*pCode);
        formula::FormulaToken* p = aIter.GetNextReferenceRPN();
        if( p )
        {
            /* FIXME: original GetFormula() code obtained
             * pCell only if (!IsInChangeTrack()),
             * GetEnglishFormula() omitted that test.
             * Can we live without in all cases? */

            ScFormulaCell* pCell = nullptr;
            ScSingleRefData& rRef = *p->GetSingleRef();
            ScAddress aAbs = rRef.toAbs(rDocument, aPos);
            if (rDocument.ValidAddress(aAbs))
                pCell = rDocument.GetFormulaCell(aAbs);

            if (pCell)
            {
                return pCell->GetFormula( eGrammar, pContext );
            }
            else
            {
                ScCompiler aComp( rDocument, aPos, *pCode, eGrammar, falsefalse, pContext );
                aComp.CreateStringFromTokenArray( buffer );
            }
        }
        else
        {
            OSL_FAIL("ScFormulaCell::GetFormula: not a matrix");
        }
    }
    else
    {
        ScCompiler aComp( rDocument, aPos, *pCode, eGrammar, falsefalse, pContext );
        aComp.CreateStringFromTokenArray( buffer );
    }

    buffer.insert( 0, '=');
    if( cMatrixFlag != ScMatrixMode::NONE )
    {
        buffer.insert( 0, '{');
        buffer.append( '}');
    }
    return buffer.makeStringAndClear();
}

OUString ScFormulaCell::GetFormula( sc::CompileFormulaContext& rCxt, ScInterpreterContext* pContext ) const
{
    OUStringBuffer aBuf;
    if (pCode->GetCodeError() != FormulaError::NONE && !pCode->GetLen())
    {
        ScTokenArray aCode(rCxt.getDoc());
        aCode.AddToken( FormulaErrorToken( pCode->GetCodeError()));
        ScCompiler aComp(rCxt, aPos, aCode, falsefalse, pContext);
        aComp.CreateStringFromTokenArray(aBuf);
        return aBuf.makeStringAndClear();
    }
    else if( cMatrixFlag == ScMatrixMode::Reference )
    {
        // Reference to another cell that contains a matrix formula.
        formula::FormulaTokenArrayPlainIterator aIter(*pCode);
        formula::FormulaToken* p = aIter.GetNextReferenceRPN();
        if( p )
        {
            /* FIXME: original GetFormula() code obtained
             * pCell only if (!IsInChangeTrack()),
             * GetEnglishFormula() omitted that test.
             * Can we live without in all cases? */

            ScFormulaCell* pCell = nullptr;
            ScSingleRefData& rRef = *p->GetSingleRef();
            ScAddress aAbs = rRef.toAbs(rDocument, aPos);
            if (rDocument.ValidAddress(aAbs))
                pCell = rDocument.GetFormulaCell(aAbs);

            if (pCell)
            {
                return pCell->GetFormula(rCxt);
            }
            else
            {
                ScCompiler aComp(rCxt, aPos, *pCode, falsefalse, pContext);
                aComp.CreateStringFromTokenArray(aBuf);
            }
        }
        else
        {
            OSL_FAIL("ScFormulaCell::GetFormula: not a matrix");
        }
    }
    else
    {
        ScCompiler aComp(rCxt, aPos, *pCode, falsefalse, pContext);
        aComp.CreateStringFromTokenArray(aBuf);
    }

    aBuf.insert( 0, '=');
    if( cMatrixFlag != ScMatrixMode::NONE )
    {
        aBuf.insert( 0, '{');
        aBuf.append( '}');
    }

    return aBuf.makeStringAndClear();
}

void ScFormulaCell::GetResultDimensions( SCSIZE& rCols, SCSIZE& rRows )
{
    MaybeInterpret();

    if (pCode->GetCodeError() == FormulaError::NONE && aResult.GetType() == svMatrixCell)
    {
        const ScMatrix* pMat = aResult.GetToken()->GetMatrix();
        if (pMat)
        {
            pMat->GetDimensions( rCols, rRows );
            if (pCode->IsHyperLink())
            {
                // Row 2 element is the URL that is not to be displayed and the
                // result dimension not to be extended.
                assert(rRows == 2);
                rRows = 1;
            }
            return;
        }
    }
    rCols = 0;
    rRows = 0;
}

void ScFormulaCell::ResetDirty() { bDirty = bTableOpDirty = mbPostponedDirty = false; }
void ScFormulaCell::SetNeedsListening( bool bVar ) { bNeedListening = bVar; }

void ScFormulaCell::SetNeedsDirty( bool bVar )
{
    mbPostponedDirty = bVar;
}

void ScFormulaCell::SetNeedNumberFormat( bool bVal )
{
    mbNeedsNumberFormat = mbAllowNumberFormatChange = bVal;
}

void ScFormulaCell::Compile( const OUString& rFormula, bool bNoListening,
                            const FormulaGrammar::Grammar eGrammar )
{
    if ( rDocument.IsClipOrUndo() )
        return;
    bool bWasInFormulaTree = rDocument.IsInFormulaTree( this );
    if ( bWasInFormulaTree )
        rDocument.RemoveFromFormulaTree( this );
    // pCode may not deleted for queries, but must be empty
    if ( pCode )
        pCode->Clear();
    ScTokenArray* pCodeOld = pCode;
    ScCompiler aComp( rDocument, aPos, eGrammar);
    pCode = aComp.CompileString( rFormula ).release();
    assert(!mxGroup);
    delete pCodeOld;
    if( pCode->GetCodeError() == FormulaError::NONE )
    {
        if ( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() && rFormula == aResult.GetHybridFormula() )
        {   // not recursive CompileTokenArray/Compile/CompileTokenArray
            if ( rFormula[0] == '=' )
                pCode->AddBad( rFormula.copy(1) );
            else
                pCode->AddBad( rFormula );
        }
        bCompile = true;
        CompileTokenArray( bNoListening );
    }
    else
        bChanged = true;

    if ( bWasInFormulaTree )
        rDocument.PutInFormulaTree( this );
}

void ScFormulaCell::Compile(
    sc::CompileFormulaContext& rCxt, const OUString& rFormula, bool bNoListening )
{
    if ( rDocument.IsClipOrUndo() )
        return;
    bool bWasInFormulaTree = rDocument.IsInFormulaTree( this );
    if ( bWasInFormulaTree )
        rDocument.RemoveFromFormulaTree( this );
    // pCode may not deleted for queries, but must be empty
    if ( pCode )
        pCode->Clear();
    ScTokenArray* pCodeOld = pCode;
    ScCompiler aComp(rCxt, aPos);
    pCode = aComp.CompileString( rFormula ).release();
    assert(!mxGroup);
    delete pCodeOld;
    if( pCode->GetCodeError() == FormulaError::NONE )
    {
        if ( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() && rFormula == aResult.GetHybridFormula() )
        {   // not recursive CompileTokenArray/Compile/CompileTokenArray
            if ( rFormula[0] == '=' )
                pCode->AddBad( rFormula.copy(1) );
            else
                pCode->AddBad( rFormula );
        }
        bCompile = true;
        CompileTokenArray(rCxt, bNoListening);
    }
    else
        bChanged = true;

    if ( bWasInFormulaTree )
        rDocument.PutInFormulaTree( this );
}

void ScFormulaCell::CompileTokenArray( bool bNoListening )
{
    // Not already compiled?
    if( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
    {
        Compile( aResult.GetHybridFormula(), bNoListening, eTempGrammar);
    }
    else if( bCompile && !rDocument.IsClipOrUndo() && pCode->GetCodeError() == FormulaError::NONE )
    {
        // RPN length may get changed
        bool bWasInFormulaTree = rDocument.IsInFormulaTree( this );
        if ( bWasInFormulaTree )
            rDocument.RemoveFromFormulaTree( this );

        // Loading from within filter? No listening yet!
        if( rDocument.IsInsertingFromOtherDoc() )
            bNoListening = true;

        if( !bNoListening && pCode->GetCodeLen() )
            EndListeningTo( rDocument );
        ScCompiler aComp(rDocument, aPos, *pCode, rDocument.GetGrammar(), true, cMatrixFlag != ScMatrixMode::NONE);
        bSubTotal = aComp.CompileTokenArray();
        if( pCode->GetCodeError() == FormulaError::NONE )
        {
            nFormatType = aComp.GetNumFormatType();
            bChanged = true;
            aResult.SetToken( nullptr);
            bCompile = false;
            if ( !bNoListening )
                StartListeningTo( rDocument );
        }
        if ( bWasInFormulaTree )
            rDocument.PutInFormulaTree( this );

        if (bSubTotal)
            rDocument.AddSubTotalCell(this);
    }
}

void ScFormulaCell::CompileTokenArray( sc::CompileFormulaContext& rCxt, bool bNoListening )
{
    // Not already compiled?
    if( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
    {
        rCxt.setGrammar(eTempGrammar);
        Compile(rCxt, aResult.GetHybridFormula(), bNoListening);
    }
    else if( bCompile && !rDocument.IsClipOrUndo() && pCode->GetCodeError() == FormulaError::NONE)
    {
        // RPN length may get changed
        bool bWasInFormulaTree = rDocument.IsInFormulaTree( this );
        if ( bWasInFormulaTree )
            rDocument.RemoveFromFormulaTree( this );

        // Loading from within filter? No listening yet!
        if( rDocument.IsInsertingFromOtherDoc() )
            bNoListening = true;

        if( !bNoListening && pCode->GetCodeLen() )
            EndListeningTo( rDocument );
        ScCompiler aComp(rCxt, aPos, *pCode, true, cMatrixFlag != ScMatrixMode::NONE);
        bSubTotal = aComp.CompileTokenArray();
        if( pCode->GetCodeError() == FormulaError::NONE )
        {
            nFormatType = aComp.GetNumFormatType();
            bChanged = true;
            aResult.SetToken( nullptr);
            bCompile = false;
            if ( !bNoListening )
                StartListeningTo( rDocument );
        }
        if ( bWasInFormulaTree )
            rDocument.PutInFormulaTree( this );

        if (bSubTotal)
            rDocument.AddSubTotalCell(this);
    }
}

void ScFormulaCell::CompileXML( sc::CompileFormulaContext& rCxt, ScProgress& rProgress )
{
    if ( cMatrixFlag == ScMatrixMode::Reference )
    {   // is already token code via ScDocFunc::EnterMatrix, ScDocument::InsertMatrixFormula
        // just establish listeners
        StartListeningTo( rDocument );
        return ;
    }

    // Error constant formula cell stays as is.
    if (!pCode->GetLen() && pCode->GetCodeError() != FormulaError::NONE)
        return;

    // Compilation changes RPN count, remove and reinsert to FormulaTree if it
    // was in to update its count.
    bool bWasInFormulaTree = rDocument.IsInFormulaTree( this);
    if (bWasInFormulaTree)
        rDocument.RemoveFromFormulaTree( this);
    rCxt.setGrammar(eTempGrammar);
    ScCompiler aComp(rCxt, aPos, *pCode, true, cMatrixFlag != ScMatrixMode::NONE);
    OUString aFormula, aFormulaNmsp;
    aComp.CreateStringFromXMLTokenArray( aFormula, aFormulaNmsp );
    rDocument.DecXMLImportedFormulaCount( aFormula.getLength() );
    rProgress.SetStateCountDownOnPercent( rDocument.GetXMLImportedFormulaCount() );
    // pCode may not deleted for queries, but must be empty
    pCode->Clear();

    bool bDoCompile = true;

    if ( !mxGroup && aFormulaNmsp.isEmpty() ) // optimization
    {
        ScAddress aPreviousCell( aPos );
        aPreviousCell.IncRow( -1 );
        ScFormulaCell *pPreviousCell = rDocument.GetFormulaCell( aPreviousCell );
        if (pPreviousCell && pPreviousCell->GetCode()->IsShareable())
        {
            // Build formula string using the tokens from the previous cell,
            // but use the current cell position.
            ScCompiler aBackComp( rCxt, aPos, *(pPreviousCell->pCode) );
            OUStringBuffer aShouldBeBuf;
            aBackComp.CreateStringFromTokenArray( aShouldBeBuf );

            // The initial '=' is optional in ODFF.
            const sal_Int32 nLeadingEqual = (aFormula.getLength() > 0 && aFormula[0] == '=') ? 1 : 0;
            if (aFormula.getLength() == aShouldBeBuf.getLength() + nLeadingEqual &&
                    aFormula.match( aShouldBeBuf, nLeadingEqual))
            {
                // Put them in the same formula group.
                ScFormulaCellGroupRef xGroup = pPreviousCell->GetCellGroup();
                if (!xGroup) // Last cell is not grouped yet. Start a new group.
                    xGroup = pPreviousCell->CreateCellGroup(1, false);
                ++xGroup->mnLength;
                SetCellGroup( xGroup );

                // Do setup here based on previous cell.

                nFormatType = pPreviousCell->nFormatType;
                bSubTotal = pPreviousCell->bSubTotal;
                bChanged = true;
                bCompile = false;

                if (bSubTotal)
                    rDocument.AddSubTotalCell(this);

                bDoCompile = false;
                pCode = pPreviousCell->pCode;
                if (pPreviousCell->mbIsExtRef)
                    rDocument.GetExternalRefManager()->insertRefCellFromTemplate( pPreviousCell, this );
            }
        }
    }

    if (bDoCompile)
    {
        ScTokenArray* pCodeOld = pCode;
        pCode = aComp.CompileString( aFormula, aFormulaNmsp ).release();
        assert(!mxGroup);
        delete pCodeOld;

        if( pCode->GetCodeError() == FormulaError::NONE )
        {
            if ( !pCode->GetLen() )
            {
                if ( !aFormula.isEmpty() && aFormula[0] == '=' )
                    pCode->AddBad( aFormula.copy( 1 ) );
                else
                    pCode->AddBad( aFormula );
            }
            bSubTotal = aComp.CompileTokenArray();
            if( pCode->GetCodeError() == FormulaError::NONE )
            {
                nFormatType = aComp.GetNumFormatType();
                bChanged = true;
                bCompile = false;
            }

            if (bSubTotal)
                rDocument.AddSubTotalCell(this);
        }
        else
            bChanged = true;
    }

    //  After loading, it must be known if ocDde/ocWebservice is in any formula
    //  (for external links warning, CompileXML is called at the end of loading XML file)
    rDocument.CheckLinkFormulaNeedingCheck(*pCode);

    //volatile cells must be added here for import
    if( !pCode->IsRecalcModeNormal() || pCode->IsRecalcModeForced())
    {
        // During load, only those cells that are marked explicitly dirty get
        // recalculated.  So we need to set it dirty here.
        SetDirtyVar();
        rDocument.AppendToFormulaTrack(this);
        // Do not call TrackFormulas() here, not all listeners may have been
        // established, postponed until ScDocument::CompileXML() finishes.
    }
    else if (bWasInFormulaTree)
        rDocument.PutInFormulaTree(this);
}

void ScFormulaCell::CalcAfterLoad( sc::CompileFormulaContext& rCxt, bool bStartListening )
{
    bool bNewCompiled = false;
    // If a Calc 1.0-doc is read, we have a result, but no token array
    if( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
    {
        rCxt.setGrammar(eTempGrammar);
        Compile(rCxt, aResult.GetHybridFormula(), true);
        aResult.SetToken( nullptr);
        bDirty = true;
        bNewCompiled = true;
    }
    // The RPN array is not created when a Calc 3.0-Doc has been read as the Range Names exist until now.
    if( pCode->GetLen() && !pCode->GetCodeLen() && pCode->GetCodeError() == FormulaError::NONE )
    {
        ScCompiler aComp(rCxt, aPos, *pCode, true, cMatrixFlag != ScMatrixMode::NONE);
        bSubTotal = aComp.CompileTokenArray();
        nFormatType = aComp.GetNumFormatType();
        bDirty = true;
        bCompile = false;
        bNewCompiled = true;

        if (bSubTotal)
            rDocument.AddSubTotalCell(this);
    }

    // On OS/2 with broken FPU exception, we can somehow store /0 without Err503. Later on in
    // the BLC Lib NumberFormatter crashes when doing a fabs (NAN) (# 32739 #).
    // We iron this out here for all systems, such that we also have an Err503 here.
    if ( aResult.IsValue() && !std::isfinite( aResult.GetDouble() ) )
    {
        OSL_FAIL("Formula cell INFINITY!!! Where does this document come from?");
        aResult.SetResultError( FormulaError::IllegalFPOperation );
        bDirty = true;
    }

    // DoubleRefs for binary operators were always a Matrix before version v5.0.
    // Now this is only the case when in an array formula, otherwise it's an implicit intersection
    if ( ScDocument::GetSrcVersion() < SC_MATRIX_DOUBLEREF &&
            GetMatrixFlag() == ScMatrixMode::NONE && pCode->HasMatrixDoubleRefOps() )
    {
        cMatrixFlag = ScMatrixMode::Formula;
        SetMatColsRows( 1, 1);
    }

    // Do the cells need to be calculated? After Load cells can contain an error code, and then start
    // the listener and Recalculate (if needed) if not ScRecalcMode::NORMAL
    if( !bNewCompiled || pCode->GetCodeError() == FormulaError::NONE )
    {
        if (bStartListening)
            StartListeningTo(rDocument);

        if( !pCode->IsRecalcModeNormal() )
            bDirty = true;
    }
    if ( pCode->IsRecalcModeAlways() )
    {   // random(), today(), now() always stay in the FormulaTree, so that they are calculated
        // for each F9
        bDirty = true;
    }
    // No SetDirty yet, as no all Listeners are known yet (only in SetDirtyAfterLoad)
}

bool ScFormulaCell::MarkUsedExternalReferences()
{
    return pCode && rDocument.MarkUsedExternalReferences(*pCode, aPos);
}

namespace {
class RecursionCounter
{
    ScRecursionHelper&  rRec;
    bool                bStackedInIteration;
#if defined DBG_UTIL && !defined NDEBUG
    const ScFormulaCell* cell;
#endif
public:
    RecursionCounter( ScRecursionHelper& r, ScFormulaCell* p )
        : rRec(r)
#if defined DBG_UTIL && !defined NDEBUG
        , cell(p)
#endif
    {
        bStackedInIteration = rRec.IsDoingIteration();
        if (bStackedInIteration)
            rRec.GetRecursionInIterationStack().push( p);
        rRec.IncRecursionCount();
    }
    ~RecursionCounter()
    {
        rRec.DecRecursionCount();
        if (bStackedInIteration)
        {
#if defined DBG_UTIL && !defined NDEBUG
            assert(rRec.GetRecursionInIterationStack().top() == cell);
#endif
            rRec.GetRecursionInIterationStack().pop();
        }
    }
};

// Forced calculation: OpenCL and threads require formula groups, so force even single cells to be a "group".
// Remove the group again at the end, since there are some places throughout the code
// that do not handle well groups with just 1 cell. Remove the groups only when the recursion level
// reaches 0 again (groups contain some info such as disabling threading because of cycles, so removing
// a group immediately would remove the info), for this reason affected cells are stored in the recursion
// helper.
struct TemporaryCellGroupMaker
{
    TemporaryCellGroupMaker( ScFormulaCell* cell, bool enable )
        : mCell( cell )
        , mEnabled( enable )
    {
        if( mEnabled && mCell->GetCellGroup() == nullptr )
        {
            mCell->CreateCellGroup( 1, false );
            mCell->GetDocument().GetRecursionHelper().AddTemporaryGroupCell( mCell );
        }
    }
    ~TemporaryCellGroupMaker() COVERITY_NOEXCEPT_FALSE
    {
        if( mEnabled )
            mCell->GetDocument().GetRecursionHelper().CleanTemporaryGroupCells();
    }
    ScFormulaCell* mCell;
    const bool mEnabled;
};

// namespace

bool ScFormulaCell::Interpret(SCROW nStartOffset, SCROW nEndOffset)
{
    ScRecursionHelper& rRecursionHelper = rDocument.GetRecursionHelper();
    bool bGroupInterpreted = false;

    // The result would possibly depend on a cell without a valid value, bail out
    // the entire dependency computation.
    if (rRecursionHelper.IsAbortingDependencyComputation())
        return false;

    if ((mxGroup && !rRecursionHelper.CheckFGIndependence(mxGroup.get())) || !rRecursionHelper.AreGroupsIndependent())
        return bGroupInterpreted;

    static ForceCalculationType forceType = ScCalcConfig::getForceCalculationType();
    TemporaryCellGroupMaker cellGroupMaker( this, forceType != ForceCalculationNone && forceType != ForceCalculationCore );

    ScFormulaCell* pTopCell = mxGroup ? mxGroup->mpTopCell : this;

    if (pTopCell->mbSeenInPath && rRecursionHelper.GetDepComputeLevel() &&
        rRecursionHelper.AnyCycleMemberInDependencyEvalMode(pTopCell))
    {
        // This call arose from a dependency calculation and we just found a cycle.
        // This will mark all elements in the cycle as parts-of-cycle.
        ScFormulaGroupCycleCheckGuard aCycleCheckGuard(rRecursionHelper, pTopCell);
        // Reaching here does not necessarily mean a circular reference, so don't set Err:522 here yet.
        // If there is a genuine circular reference, it will be marked so when all groups
        // in the cycle get out of dependency evaluation mode.
        // But returning without calculation a new value means other cells depending
        // on this one would use a possibly invalid value, so ensure the dependency
        // computation is aborted without resetting the dirty flag of any cell.
        rRecursionHelper.AbortDependencyComputation();
        return bGroupInterpreted;
    }

#if DEBUG_CALCULATION
    static bool bDebugCalculationInit = true;
    if (bDebugCalculationInit)
    {
        aDC.maTrigger = aDebugCalculationTriggerAddress;
        aDC.mbPrintResults = true;
        bDebugCalculationInit = false;
    }
    DebugCalculationStacker aDebugEntry(aPos, rDocument);
#endif

    if (!IsDirtyOrInTableOpDirty() || rRecursionHelper.IsInReturn())
        return bGroupInterpreted;     // no double/triple processing

    //FIXME:
    //  If the call originates from a Reschedule in DdeLink update, leave dirty
    //  Better: Do a Dde Link Update without Reschedule or do it completely asynchronously!
    if ( rDocument.IsInDdeLinkUpdate() )
        return bGroupInterpreted;

    if (bRunning)
    {
        if (!rDocument.GetDocOptions().IsIter())
        {
            aResult.SetResultError( FormulaError::CircularReference );
            return bGroupInterpreted;
        }

        if (aResult.GetResultError() == FormulaError::CircularReference)
            aResult.SetResultError( FormulaError::NONE );

        // Start or add to iteration list.
        if (!rRecursionHelper.IsDoingIteration() ||
                !rRecursionHelper.GetRecursionInIterationStack().top()->bIsIterCell)
            rRecursionHelper.SetInIterationReturn( true);

        return bGroupInterpreted;
    }
    // no multiple interprets for GetErrCode, IsValue, GetValue and
    // different entry point recursions. Would also lead to premature
    // convergence in iterations.
    if (rRecursionHelper.GetIteration() && nSeenInIteration ==
            rRecursionHelper.GetIteration())
        return bGroupInterpreted;

    bool bOldRunning = bRunning;
    if (rRecursionHelper.GetRecursionCount() > MAXRECURSION)
    {
        bRunning = true;
        rRecursionHelper.SetInRecursionReturn( true);
    }
    else
    {
        rDocument.IncInterpretLevel();

#if DEBUG_CALCULATION
        aDC.enterGroup();
#endif
        bool bPartOfCycleBefore = mxGroup && mxGroup->mbPartOfCycle;
        bGroupInterpreted = InterpretFormulaGroup(nStartOffset, nEndOffset);
        bool bPartOfCycleAfter = mxGroup && mxGroup->mbPartOfCycle;

#if DEBUG_CALCULATION
        aDC.leaveGroup();
#endif
        if (!bGroupInterpreted)
        {
            // This call resulted from a dependency calculation for a multigroup-threading attempt,
            // but found dependency among the groups.
            if (!rRecursionHelper.AreGroupsIndependent())
            {
                rDocument.DecInterpretLevel();
                return bGroupInterpreted;
            }
            // Dependency calc inside InterpretFormulaGroup() failed due to
            // detection of a cycle and there are parent FG's in the cycle.
            // Skip InterpretTail() in such cases, only run InterpretTail for the "cycle-starting" FG
            if (!bPartOfCycleBefore && bPartOfCycleAfter && rRecursionHelper.AnyParentFGInCycle())
            {
                rDocument.DecInterpretLevel();
                return bGroupInterpreted;
            }

            ScFormulaGroupCycleCheckGuard aCycleCheckGuard(rRecursionHelper, this);
            ScInterpreterContextGetterGuard aContextGetterGuard(rDocument, rDocument.GetFormatTable());
            InterpretTail( *aContextGetterGuard.GetInterpreterContext(), SCITP_NORMAL);
        }

        rDocument.DecInterpretLevel();
    }

    // While leaving a recursion or iteration stack, insert its cells to the
    // recursion list in reverse order.
    if (rRecursionHelper.IsInReturn())
    {
        bool bFreeFlyingInserted = false;
        if (rRecursionHelper.GetRecursionCount() > 0 || !rRecursionHelper.IsDoingRecursion())
        {
            rRecursionHelper.Insert( this, bOldRunning, aResult);
            bFreeFlyingInserted = mbFreeFlying;
        }
        bool bIterationFromRecursion = false;
        bool bResumeIteration = false;
        do
        {
            if ((rRecursionHelper.IsInIterationReturn() &&
                        rRecursionHelper.GetRecursionCount() == 0 &&
                        !rRecursionHelper.IsDoingIteration()) ||
                    bIterationFromRecursion || bResumeIteration)
            {
                bool & rDone = rRecursionHelper.GetConvergingReference();
                rDone = false;
                if (!bIterationFromRecursion && bResumeIteration)
                {
                    bResumeIteration = false;
                    // Resuming iteration expands the range.
                    ScFormulaRecursionList::const_iterator aOldStart(
                            rRecursionHelper.GetLastIterationStart());
                    rRecursionHelper.ResumeIteration();
                    // Mark new cells being in iteration.
                    for (ScFormulaRecursionList::const_iterator aIter(
                                rRecursionHelper.GetIterationStart()); aIter !=
                            aOldStart; ++aIter)
                    {
                        ScFormulaCell* pIterCell = (*aIter).pCell;
                        pIterCell->bIsIterCell = true;
                    }
                    // Mark older cells dirty again, in case they converted
                    // without accounting for all remaining cells in the circle
                    // that weren't touched so far, e.g. conditional. Restore
                    // backupped result.
                    sal_uInt16 nIteration = rRecursionHelper.GetIteration();
                    for (ScFormulaRecursionList::const_iterator aIter(
                                aOldStart); aIter !=
                            rRecursionHelper.GetIterationEnd(); ++aIter)
                    {
                        ScFormulaCell* pIterCell = (*aIter).pCell;
                        if (pIterCell->nSeenInIteration == nIteration)
                        {
                            if (!pIterCell->bDirty || aIter == aOldStart)
                            {
                                pIterCell->aResult = (*aIter).aPreviousResult;
                            }
                            --pIterCell->nSeenInIteration;
                        }
                        pIterCell->bDirty = true;
                    }
                }
                else
                {
                    bResumeIteration = false;
                    // Close circle once. If 'this' is self-referencing only
                    // (e.g. counter or self-adder) then it is already
                    // implicitly closed.
                    /* TODO: does this even make sense anymore? The last cell
                     * added above with rRecursionHelper.Insert() should always
                     * be 'this', shouldn't it? */

                    if (rRecursionHelper.GetList().size() > 1)
                    {
                        ScFormulaCell* pLastCell = rRecursionHelper.GetList().back().pCell;
                        if (pLastCell != this)
                        {
                            rDocument.IncInterpretLevel();
                            ScInterpreterContextGetterGuard aContextGetterGuard(rDocument, rDocument.GetFormatTable());
                            pLastCell->InterpretTail(
                                *aContextGetterGuard.GetInterpreterContext(), SCITP_CLOSE_ITERATION_CIRCLE);
                            rDocument.DecInterpretLevel();
                        }
                    }
                    // Start at 1, init things.
                    rRecursionHelper.StartIteration();
                    // Mark all cells being in iteration. Reset results to
                    // original values, formula cells have been interpreted
                    // already, discard that step.
                    for (ScFormulaRecursionList::const_iterator aIter(
                                rRecursionHelper.GetIterationStart()); aIter !=
                            rRecursionHelper.GetIterationEnd(); ++aIter)
                    {
                        ScFormulaCell* pIterCell = (*aIter).pCell;
                        pIterCell->aResult = (*aIter).aPreviousResult;
                        pIterCell->bIsIterCell = true;
                    }
                }
                bIterationFromRecursion = false;
                sal_uInt16 nIterMax = rDocument.GetDocOptions().GetIterCount();
                for ( ; rRecursionHelper.GetIteration() <= nIterMax && !rDone;
                        rRecursionHelper.IncIteration())
                {
                    rDone = false;
                    bool bFirst = true;
                    for ( ScFormulaRecursionList::iterator aIter(
                                rRecursionHelper.GetIterationStart()); aIter !=
                            rRecursionHelper.GetIterationEnd() &&
                            !rRecursionHelper.IsInReturn(); ++aIter)
                    {
                        ScFormulaCell* pIterCell = (*aIter).pCell;
                        if (pIterCell->IsDirtyOrInTableOpDirty() &&
                                rRecursionHelper.GetIteration() !=
                                pIterCell->GetSeenInIteration())
                        {
                            (*aIter).aPreviousResult = pIterCell->aResult;
                            rDocument.IncInterpretLevel();
                            ScInterpreterContextGetterGuard aContextGetterGuard(rDocument, rDocument.GetFormatTable());
                            pIterCell->InterpretTail( *aContextGetterGuard.GetInterpreterContext(), SCITP_FROM_ITERATION);
                            rDocument.DecInterpretLevel();
                        }
                        if (bFirst)
                        {
                            rDone = !pIterCell->IsDirtyOrInTableOpDirty();
                            bFirst = false;
                        }
                        else if (rDone)
                        {
                            rDone = !pIterCell->IsDirtyOrInTableOpDirty();
                        }
                    }
                    if (rRecursionHelper.IsInReturn())
                    {
                        bResumeIteration = true;
                        break;  // for
                        // Don't increment iteration.
                    }
                }
                if (!bResumeIteration)
                {
                    if (rDone)
                    {
                        for (ScFormulaRecursionList::const_iterator aIter(
                                    rRecursionHelper.GetIterationStart());
                                aIter != rRecursionHelper.GetIterationEnd();
                                ++aIter)
                        {
                            ScFormulaCell* pIterCell = (*aIter).pCell;
                            pIterCell->bIsIterCell = false;
                            pIterCell->nSeenInIteration = 0;
                            pIterCell->bRunning = (*aIter).bOldRunning;
                        }
                    }
                    else
                    {
                        for (ScFormulaRecursionList::const_iterator aIter(
                                    rRecursionHelper.GetIterationStart());
                                aIter != rRecursionHelper.GetIterationEnd();
                                ++aIter)
                        {
                            ScFormulaCell* pIterCell = (*aIter).pCell;
                            pIterCell->bIsIterCell = false;
                            pIterCell->nSeenInIteration = 0;
                            pIterCell->bRunning = (*aIter).bOldRunning;
                            pIterCell->ResetDirty();
                            // The difference to Excel is that Excel does not
                            // produce an error for non-convergence thus a
                            // delta of 0.001 still works to execute the
                            // maximum number of iterations and display the
                            // results no matter if the result anywhere reached
                            // near delta, but also never indicates whether the
                            // result actually makes sense in case of
                            // non-counter context. Calc does check the delta
                            // in every case. If we wanted to support what
                            // Excel does then add another option "indicate
                            // non-convergence error" (default on) and execute
                            // the following block only if set.
#if 1
                            // If one cell didn't converge, all cells of this
                            // circular dependency don't, no matter whether
                            // single cells did.
                            pIterCell->aResult.SetResultError( FormulaError::NoConvergence);
                            pIterCell->bChanged = true;
#endif
                        }
                    }
                    // End this iteration and remove entries.
                    rRecursionHelper.EndIteration();
                    bResumeIteration = rRecursionHelper.IsDoingIteration();
                }
            }
            if (rRecursionHelper.IsInRecursionReturn() &&
                    rRecursionHelper.GetRecursionCount() == 0 &&
                    !rRecursionHelper.IsDoingRecursion())
            {
                bIterationFromRecursion = false;
                // Iterate over cells known so far, start with the last cell
                // encountered, inserting new cells if another recursion limit
                // is reached. Repeat until solved.
                rRecursionHelper.SetDoingRecursion( true);
                do
                {
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=92 H=97 G=94

¤ Dauer der Verarbeitung: 0.47 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