Map shared formula from xls to formula groups, and share the tokens as well.

No more mapping to range names.

Change-Id: Ic43b6ef35a91fe4d6fff748ebc22969ba4e036db
diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx
index 045b76a..22bb9bd 100644
--- a/sc/inc/column.hxx
+++ b/sc/inc/column.hxx
@@ -275,6 +275,7 @@ public:
     */
    ScFormulaCell* SetFormulaCell( SCROW nRow, ScFormulaCell* pCell );
    ScFormulaCell* SetFormulaCell( sc::ColumnBlockPosition& rBlockPos, SCROW nRow, ScFormulaCell* pCell );
    bool SetGroupFormulaCell( SCROW nRow, ScFormulaCell* pCell );

    void SetRawString( SCROW nRow, const OUString& rStr, bool bBroadcast = true );
    void SetRawString( sc::ColumnBlockPosition& rBlockPos, SCROW nRow, const OUString& rStr, bool bBroadcast = true );
@@ -507,8 +508,8 @@ private:

    sc::CellStoreType::iterator GetPositionToInsert( SCROW nRow );
    sc::CellStoreType::iterator GetPositionToInsert( const sc::CellStoreType::iterator& it, SCROW nRow );
    void ActivateNewFormulaCell( const sc::CellStoreType::iterator& itPos, SCROW nRow, ScFormulaCell& rCell );
    void ActivateNewFormulaCell( const sc::CellStoreType::position_type& aPos, ScFormulaCell& rCell );
    void ActivateNewFormulaCell( const sc::CellStoreType::iterator& itPos, SCROW nRow, ScFormulaCell& rCell, bool bJoin = true );
    void ActivateNewFormulaCell( const sc::CellStoreType::position_type& aPos, ScFormulaCell& rCell, bool bJoin = true );
    void BroadcastNewCell( SCROW nRow );
    bool UpdateScriptType( sc::CellTextAttr& rAttr, SCROW nRow );

diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx
index 90e0148..8b82fb7 100644
--- a/sc/inc/document.hxx
+++ b/sc/inc/document.hxx
@@ -804,6 +804,7 @@ public:
     *         is deleted automatically on failure to insert.
     */
    SC_DLLPUBLIC ScFormulaCell* SetFormulaCell( const ScAddress& rPos, ScFormulaCell* pCell );
    SC_DLLPUBLIC bool SetGroupFormulaCell( const ScAddress& rPos, ScFormulaCell* pCell );

    SC_DLLPUBLIC void InsertMatrixFormula(SCCOL nCol1, SCROW nRow1,
                                        SCCOL nCol2, SCROW nRow2,
diff --git a/sc/inc/formulacell.hxx b/sc/inc/formulacell.hxx
index 6480056..f857aff 100644
--- a/sc/inc/formulacell.hxx
+++ b/sc/inc/formulacell.hxx
@@ -27,6 +27,7 @@
#include "types.hxx"

#include <set>
#include <boost/noncopyable.hpp>

namespace sc {

@@ -43,7 +44,7 @@ class ScProgress;
class ScTokenArray;
struct ScSimilarFormulaDelta;

struct SC_DLLPUBLIC ScFormulaCellGroup
struct SC_DLLPUBLIC ScFormulaCellGroup : boost::noncopyable
{
    mutable size_t mnRefCount;

@@ -55,6 +56,8 @@ struct SC_DLLPUBLIC ScFormulaCellGroup

    ScFormulaCellGroup();
    ~ScFormulaCellGroup();

    void setCode( const ScTokenArray& rCode );
};

inline void intrusive_ptr_add_ref(const ScFormulaCellGroup *p)
diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx
index 37c1430..a95f2bc 100644
--- a/sc/inc/table.hxx
+++ b/sc/inc/table.hxx
@@ -340,6 +340,7 @@ public:
     *         is deleted automatically on failure to insert.
     */
    ScFormulaCell* SetFormulaCell( SCCOL nCol, SCROW nRow, ScFormulaCell* pCell );
    bool SetGroupFormulaCell( SCCOL nCol, SCROW nRow, ScFormulaCell* pCell );

    void        SetValue( SCCOL nCol, SCROW nRow, const double& rVal );
    void        SetError( SCCOL nCol, SCROW nRow, sal_uInt16 nError);
diff --git a/sc/inc/tokenarray.hxx b/sc/inc/tokenarray.hxx
index f9a4ff1..09a9798 100644
--- a/sc/inc/tokenarray.hxx
+++ b/sc/inc/tokenarray.hxx
@@ -62,6 +62,12 @@ public:

    ScFormulaVectorState GetVectorState() const;

    /**
     * If the array contains at least one relative row reference or named
     * expression, it's variant. Otherwise invariant.
     */
    bool IsInvariant() const;

    /// Exactly and only one range (valid or deleted)
    bool IsReference( ScRange& rRange, const ScAddress& rPos ) const;
    /// Exactly and only one valid range (no #REF!s)
diff --git a/sc/qa/unit/data/xls/shared-formula.xls b/sc/qa/unit/data/xls/shared-formula.xls
index a9be6b7..f27acb4 100644
--- a/sc/qa/unit/data/xls/shared-formula.xls
+++ b/sc/qa/unit/data/xls/shared-formula.xls
Binary files differ
diff --git a/sc/qa/unit/filters-test.cxx b/sc/qa/unit/filters-test.cxx
index 1d7eeca..957e0e0 100644
--- a/sc/qa/unit/filters-test.cxx
+++ b/sc/qa/unit/filters-test.cxx
@@ -32,6 +32,8 @@
#include "cellform.hxx"
#include "drwlayer.hxx"
#include "userdat.hxx"
#include "formulacell.hxx"

#include <svx/svdpage.hxx>

using namespace ::com::sun::star;
@@ -344,6 +346,13 @@ void ScFiltersTest::testSharedFormulaXLS()
        double fCheck = i*10.0;
        CPPUNIT_ASSERT_EQUAL(fCheck, fVal);
    }

    ScFormulaCell* pCell = pDoc->GetFormulaCell(ScAddress(1,18,0));
    CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pCell);
    ScFormulaCellGroupRef xGroup = pCell->GetCellGroup();
    CPPUNIT_ASSERT_MESSAGE("This cell should be a part of a cell group.", xGroup);
    CPPUNIT_ASSERT_MESSAGE("Incorrect group geometry.", xGroup->mnStart == 2 && xGroup->mnLength == 17);

    xDocSh->DoClose();
}

diff --git a/sc/source/core/data/column3.cxx b/sc/source/core/data/column3.cxx
index 4474aec..461193c 100644
--- a/sc/source/core/data/column3.cxx
+++ b/sc/source/core/data/column3.cxx
@@ -381,16 +381,17 @@ sc::CellStoreType::iterator ScColumn::GetPositionToInsert( const sc::CellStoreTy
}

void ScColumn::ActivateNewFormulaCell(
    const sc::CellStoreType::iterator& itPos, SCROW nRow, ScFormulaCell& rCell )
    const sc::CellStoreType::iterator& itPos, SCROW nRow, ScFormulaCell& rCell, bool bJoin )
{
    ActivateNewFormulaCell(maCells.position(itPos, nRow), rCell);
    ActivateNewFormulaCell(maCells.position(itPos, nRow), rCell, bJoin);
}

void ScColumn::ActivateNewFormulaCell(
    const sc::CellStoreType::position_type& aPos, ScFormulaCell& rCell )
    const sc::CellStoreType::position_type& aPos, ScFormulaCell& rCell, bool bJoin )
{
    // See if this new formula cell can join an existing shared formula group.
    JoinNewFormulaCell(aPos, rCell);
    if (bJoin)
        // See if this new formula cell can join an existing shared formula group.
        JoinNewFormulaCell(aPos, rCell);

    // When we insert from the Clipboard we still have wrong (old) References!
    // First they are rewired in CopyBlockFromClip via UpdateReference and the
@@ -1729,6 +1730,20 @@ ScFormulaCell* ScColumn::SetFormulaCell( sc::ColumnBlockPosition& rBlockPos, SCR
    return pCell;
}

bool ScColumn::SetGroupFormulaCell( SCROW nRow, ScFormulaCell* pCell )
{
    sc::CellStoreType::iterator it = GetPositionToInsert(nRow);
    sal_uInt32 nCellFormat = GetNumberFormat(nRow);
    if( (nCellFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0)
        pCell->SetNeedNumberFormat(true);
    it = maCells.set(it, nRow, pCell);
    maCellTextAttrs.set(nRow, sc::CellTextAttr());
    CellStorageModified();

    ActivateNewFormulaCell(it, nRow, *pCell, false);
    return true;
}

namespace {

class FilterEntriesHandler
diff --git a/sc/source/core/data/documen2.cxx b/sc/source/core/data/documen2.cxx
index 7a9e9d6..720b504 100644
--- a/sc/source/core/data/documen2.cxx
+++ b/sc/source/core/data/documen2.cxx
@@ -1075,6 +1075,14 @@ ScFormulaCell* ScDocument::SetFormulaCell( const ScAddress& rPos, ScFormulaCell*
    return maTabs[rPos.Tab()]->SetFormulaCell(rPos.Col(), rPos.Row(), pCell);
}

bool ScDocument::SetGroupFormulaCell( const ScAddress& rPos, ScFormulaCell* pCell )
{
    if (!TableExists(rPos.Tab()))
        return false;

    return maTabs[rPos.Tab()]->SetGroupFormulaCell(rPos.Col(), rPos.Row(), pCell);
}

void ScDocument::SetConsolidateDlgData( const ScConsolidateParam* pData )
{
    delete pConsolidateDlgData;
diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx
index cdedb66..b9db55a 100644
--- a/sc/source/core/data/formulacell.cxx
+++ b/sc/source/core/data/formulacell.cxx
@@ -397,6 +397,14 @@ ScFormulaCellGroup::~ScFormulaCellGroup()
    delete mpCode;
}

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

// ============================================================================

ScFormulaCell::ScFormulaCell( ScDocument* pDoc, const ScAddress& rPos,
@@ -522,8 +530,6 @@ ScFormulaCell::ScFormulaCell(

    if (bSubTotal)
        pDocument->AddSubTotalCell(this);

    pCode->GenHash();
}

ScFormulaCell::ScFormulaCell( const ScFormulaCell& rCell, ScDocument& rDoc, const ScAddress& rPos, int nCloneFlags ) :
@@ -3358,7 +3364,7 @@ bool ScFormulaCell::InterpretFormulaGroup()

    // Re-build formulae groups if necessary - ideally this is done at
    // import / insert / delete etc. and is integral to the data structures
    pDocument->RebuildFormulaGroups();
//  pDocument->RebuildFormulaGroups();

    if (!mxGroup || !pCode)
        return false;
diff --git a/sc/source/core/data/table2.cxx b/sc/source/core/data/table2.cxx
index 278096e..537f856 100644
--- a/sc/source/core/data/table2.cxx
+++ b/sc/source/core/data/table2.cxx
@@ -1453,6 +1453,14 @@ ScFormulaCell* ScTable::SetFormulaCell( SCCOL nCol, SCROW nRow, ScFormulaCell* p
    return aCol[nCol].SetFormulaCell(nRow, pCell);
}

bool ScTable::SetGroupFormulaCell( SCCOL nCol, SCROW nRow, ScFormulaCell* pCell )
{
    if (!ValidColRow(nCol, nRow))
        return false;

    return aCol[nCol].SetGroupFormulaCell(nRow, pCell);
}

void ScTable::SetValue( SCCOL nCol, SCROW nRow, const double& rVal )
{
    if (ValidColRow(nCol, nRow))
diff --git a/sc/source/core/tool/token.cxx b/sc/source/core/tool/token.cxx
index 4703cde..d74182d 100644
--- a/sc/source/core/tool/token.cxx
+++ b/sc/source/core/tool/token.cxx
@@ -1474,6 +1474,42 @@ ScFormulaVectorState ScTokenArray::GetVectorState() const
    return meVectorState;
}

bool ScTokenArray::IsInvariant() const
{
    FormulaToken** p = pCode;
    FormulaToken** pEnd = p + static_cast<size_t>(nLen);
    for (; p != pEnd; ++p)
    {
        switch ((*p)->GetType())
        {
            case svSingleRef:
            case svExternalSingleRef:
            {
                const ScToken* pT = static_cast<const ScToken*>(*p);
                const ScSingleRefData& rRef = pT->GetSingleRef();
                if (rRef.IsRowRel())
                    return false;
            }
            break;
            case svDoubleRef:
            case svExternalDoubleRef:
            {
                const ScToken* pT = static_cast<const ScToken*>(*p);
                const ScComplexRefData& rRef = pT->GetDoubleRef();
                if (rRef.Ref1.IsRowRel() || rRef.Ref2.IsRowRel())
                    return false;
            }
            break;
            case svIndex:
                return false;
            default:
                ;
        }
    }

    return true;
}

bool ScTokenArray::IsReference( ScRange& rRange, const ScAddress& rPos ) const
{
    return ImplGetReference(rRange, rPos, false);
diff --git a/sc/source/filter/excel/excform.cxx b/sc/source/filter/excel/excform.cxx
index 4f60d7e..e236dad 100644
--- a/sc/source/filter/excel/excform.cxx
+++ b/sc/source/filter/excel/excform.cxx
@@ -101,7 +101,6 @@ void ImportExcel::Formula4()
void ImportExcel::Formula(
    const XclAddress& rXclPos, sal_uInt16 nXF, sal_uInt16 nFormLen, double fCurVal, bool bShrFmla)
{

    ScAddress aScPos( ScAddress::UNINITIALIZED );
    if (!GetAddressConverter().ConvertAddress(aScPos, rXclPos, GetCurrScTab(), true))
        // Conversion failed.
@@ -115,13 +114,18 @@ void ImportExcel::Formula(
    if (bShrFmla)
    {
        // This is a shared formula. Get the token array from the shared formula pool.
        pResult = pFormConv->GetShrFmla(maStrm, nFormLen);
        if (!pResult)
        ScFormulaCellGroupRef xGroup = pFormConv->GetSharedFormula(maStrm, aScPos.Col(), nFormLen);
        if (!xGroup)
            return;

        ScFormulaCell* pCell = new ScFormulaCell( pD, aScPos, pResult );
        ScFormulaCell* pCell = new ScFormulaCell(pD, aScPos, xGroup);
        pD->EnsureTable(aScPos.Tab());
        pCell = pD->SetFormulaCell(aScPos, pCell);
        bool bInserted = pD->SetGroupFormulaCell(aScPos, pCell);
        if (!bInserted)
        {
            delete pCell;
            return;
        }
        pCell->SetNeedNumberFormat(false);
        if (!rtl::math::isNan(fCurVal))
            pCell->SetResultDouble(fCurVal);
@@ -1673,38 +1677,33 @@ const ScTokenArray* ExcelToSc::GetBoolErr( XclBoolError eType )
    return pErgebnis;
}


// if a shared formula was found, stream seeks to first byte after <nFormulaLen>,
// else stream pointer stays unchanged
const ScTokenArray* ExcelToSc::GetShrFmla( XclImpStream& aIn, sal_Size nFormulaLen )
ScFormulaCellGroupRef ExcelToSc::GetSharedFormula( XclImpStream& aIn, SCCOL nCol, sal_Size nFormulaLen )
{
    if (!nFormulaLen)
        return NULL;
        return ScFormulaCellGroupRef();

    aIn.PushPosition();

    sal_uInt8 nOp;
    aIn >> nOp;

    if (nOp != 0x01)   // Shared Formula       [    277]
    if (nOp != 0x01)   // must be PtgExp token.
    {
        aIn.PopPosition();
        return NULL;
        return ScFormulaCellGroupRef();
    }

    sal_uInt16 nCol, nRow;
    aIn >> nRow >> nCol;
    sal_uInt16 nLeftCol, nRow;
    aIn >> nRow >> nLeftCol;

    aStack << aPool.StoreName( GetOldRoot().pShrfmlaBuff->Find(
        ScAddress(static_cast<SCCOL>(nCol), static_cast<SCROW>(nRow), GetCurrScTab())), true);
    ScAddress aRefPos(nCol, nRow, GetCurrScTab());
    ScFormulaCellGroupRef xGroup = GetOldRoot().pShrfmlaBuff->Find(aRefPos);

    aIn.PopPosition();

    aIn.Ignore(nFormulaLen);
    return aPool[aStack.Get()];
    return xGroup;
}


void ExcelToSc::SetError( ScFormulaCell &rCell, const ConvErr eErr )
{
    sal_uInt16  nInd;
diff --git a/sc/source/filter/excel/namebuff.cxx b/sc/source/filter/excel/namebuff.cxx
index 6279c87..ac609a4 100644
--- a/sc/source/filter/excel/namebuff.cxx
+++ b/sc/source/filter/excel/namebuff.cxx
@@ -20,9 +20,6 @@

#include "namebuff.hxx"

#include <string.h>

#include "rangenam.hxx"
#include "document.hxx"
#include "compiler.hxx"
#include "scextopt.hxx"
@@ -32,6 +29,7 @@
#include "xltools.hxx"
#include "xiroot.hxx"

#include <string.h>

sal_uInt32 StringHashEntry::MakeHashCode( const String& r )
{
@@ -70,84 +68,41 @@ void NameBuffer::operator <<( const String &rNewString )
    maHashes.push_back( new StringHashEntry( rNewString ) );
}

SharedFormulaBuffer::SharedFormulaBuffer( RootData* pRD ) : ExcRoot(pRD) {}

#if OSL_DEBUG_LEVEL > 0
sal_uInt16  nShrCnt;
#endif


size_t SharedFormulaBuffer::ScAddressHashFunc::operator() (const ScAddress &addr) const
{
    // Use something simple, it is just a hash.
    return static_cast< sal_uInt16 >( addr.Row() ) | (static_cast< sal_uInt8 >( addr.Col() ) << 16);
}

const size_t nBase = 16384; // Range~ und Shared~ Dingens mit jeweils der Haelfte Ids
SharedFormulaBuffer::SharedFormulaBuffer( RootData* pRD ) :
    ExcRoot( pRD ),
    mnCurrIdx (nBase)
{
#if OSL_DEBUG_LEVEL > 0
    nShrCnt = 0;
#endif
}

SharedFormulaBuffer::~SharedFormulaBuffer()
{
}
SharedFormulaBuffer::~SharedFormulaBuffer() {}

void SharedFormulaBuffer::Clear()
{
    index_hash.clear();
    // do not clear index_list, index calculation depends on complete list size...
    // do not change mnCurrIdx
    maFormulaGroups.clear();
}

void SharedFormulaBuffer::Store( const ScRange& rRange, const ScTokenArray& rToken )
void SharedFormulaBuffer::Store( const ScRange& rRange, const ScTokenArray& rArray )
{
    String          aName( CreateName( rRange.aStart ) );
    SCROW nGroupLen = rRange.aEnd.Row() - rRange.aStart.Row() + 1;
    for (SCCOL i = rRange.aStart.Col(); i <= rRange.aEnd.Col(); ++i)
    {
        // Create one group per column.
        ScAddress aPos = rRange.aStart;
        aPos.SetCol(i);

    OSL_ENSURE( mnCurrIdx <= 0xFFFF, "*ShrfmlaBuffer::Store(): I'm getting sick...!" );

    ScRangeData* pData = new ScRangeData( pExcRoot->pIR->GetDocPtr(), aName, rToken, rRange.aStart, RT_SHARED );
    const ScAddress& rMaxPos = pExcRoot->pIR->GetMaxPos();
    pData->SetMaxCol(rMaxPos.Col());
    pData->SetMaxRow(rMaxPos.Row());
    pData->SetIndex( static_cast< sal_uInt16 >( mnCurrIdx ) );
    pExcRoot->pIR->GetNamedRanges().insert( pData );
    index_hash[rRange.aStart] = static_cast< sal_uInt16 >( mnCurrIdx );
    index_list.push_front (rRange);
    ++mnCurrIdx;
        ScFormulaCellGroupRef xNewGroup(new ScFormulaCellGroup);
        xNewGroup->mnStart = rRange.aStart.Row();
        xNewGroup->mnLength = nGroupLen;
        xNewGroup->mpCode = rArray.Clone();
        xNewGroup->mbInvariant = rArray.IsInvariant();
        xNewGroup->setCode(rArray);
        maFormulaGroups.insert(FormulaGroupsType::value_type(aPos, xNewGroup));
    }
}


sal_uInt16 SharedFormulaBuffer::Find( const ScAddress & aAddr ) const
ScFormulaCellGroupRef SharedFormulaBuffer::Find( const ScAddress& rRefPos ) const
{
    ShrfmlaHash::const_iterator hash = index_hash.find (aAddr);
    if (hash != index_hash.end())
        return hash->second;
    FormulaGroupsType::const_iterator it = maFormulaGroups.find(rRefPos);
    if (it == maFormulaGroups.end())
        return ScFormulaCellGroupRef();

    // It was not hashed on the top left corner ?  do a brute force search
    unsigned int ind = nBase;
    for (ShrfmlaList::const_iterator ptr = index_list.end(); ptr != index_list.begin() ; ind++)
        if ((--ptr)->In (aAddr))
            return static_cast< sal_uInt16 >( ind );
    return static_cast< sal_uInt16 >( mnCurrIdx );
}


#define SHRFMLA_BASENAME    "SHARED_FORMULA_"

String SharedFormulaBuffer::CreateName( const ScRange& r )
{
    OUString aName = SHRFMLA_BASENAME +
                     OUString::number( r.aStart.Col() ) + "_" +
                     OUString::number( r.aStart.Row() ) + "_" +
                     OUString::number( r.aEnd.Col() )   + "_" +
                     OUString::number( r.aEnd.Row() )   + "_" +
                     OUString::number( r.aStart.Tab() );

    return aName;
    return it->second;
}

sal_Int16 ExtSheetBuffer::Add( const String& rFPAN, const String& rTN, const sal_Bool bSWB )
diff --git a/sc/source/filter/inc/excform.hxx b/sc/source/filter/inc/excform.hxx
index fb866fe..0e2004c 100644
--- a/sc/source/filter/inc/excform.hxx
+++ b/sc/source/filter/inc/excform.hxx
@@ -63,7 +63,7 @@ public:

    void                GetDummy( const ScTokenArray*& );
    const ScTokenArray* GetBoolErr( XclBoolError );
    const ScTokenArray* GetShrFmla( XclImpStream& rStrm, sal_Size nFormulaLen );
    ScFormulaCellGroupRef GetSharedFormula( XclImpStream& rStrm, SCCOL nCol, sal_Size nFormulaLen );

    static void         SetError( ScFormulaCell& rCell, const ConvErr eErr );

diff --git a/sc/source/filter/inc/namebuff.hxx b/sc/source/filter/inc/namebuff.hxx
index 6f5aa28..97aebc1 100644
--- a/sc/source/filter/inc/namebuff.hxx
+++ b/sc/source/filter/inc/namebuff.hxx
@@ -26,6 +26,8 @@
#include "xiroot.hxx"

#include "rangenam.hxx"
#include "formulacell.hxx"

#include <boost/unordered_map.hpp>
#include <list>

@@ -146,30 +148,21 @@ inline void NameBuffer::SetBase( sal_uInt16 nNewBase )
    nBase = nNewBase;
}




/**
 * Store and manage shared formula tokens.
 */
class SharedFormulaBuffer : public ExcRoot
{
    struct ScAddressHashFunc : public std::unary_function< const ScAddress &, size_t >
    {
        size_t operator() (const ScAddress &addr) const;
    };
    typedef boost::unordered_map <ScAddress, sal_uInt16, ScAddressHashFunc> ShrfmlaHash;
    typedef std::list <ScRange>                                  ShrfmlaList;
    typedef boost::unordered_map<ScAddress, ScFormulaCellGroupRef, ScAddressHashFunctor> FormulaGroupsType;

    ShrfmlaHash  index_hash;
    ShrfmlaList  index_list;
    size_t                  mnCurrIdx;
    FormulaGroupsType maFormulaGroups;

public:
    SharedFormulaBuffer( RootData* pRD );
    virtual ~SharedFormulaBuffer();
    void                    Clear();
    void                    Store( const ScRange& rRange, const ScTokenArray& );
    sal_uInt16                  Find (const ScAddress & rAddress ) const;

    static String           CreateName( const ScRange& );
    void Clear();
    void Store( const ScRange& rRange, const ScTokenArray& rArray );
    ScFormulaCellGroupRef Find( const ScAddress& rRefPos ) const;
};