Perform formula cell compilations in multiple threads during xlsx import.

One sheet per thread. Right now the thread count is set to 1 due to non
re-entrancy of a large portion of Calc core, and beyond. We need to fix
that first before setting the thread count to more than 1.

Change-Id: I6997c1e9540de939f1f00b1798e2b32059787ae5
diff --git a/sc/source/filter/inc/formulabuffer.hxx b/sc/source/filter/inc/formulabuffer.hxx
index f9e5229..7e881ee 100644
--- a/sc/source/filter/inc/formulabuffer.hxx
+++ b/sc/source/filter/inc/formulabuffer.hxx
@@ -13,6 +13,8 @@
#include <utility>
#include "oox/helper/refmap.hxx"
#include "oox/helper/refvector.hxx"
#include "salhelper/thread.hxx"
#include "osl/mutex.hxx"
#include "workbookhelper.hxx"
#include <com/sun/star/table/CellAddress.hpp>
#include <com/sun/star/table/CellRangeAddress.hpp>
@@ -29,6 +31,21 @@ namespace oox { namespace xls {

class FormulaBuffer : public WorkbookHelper
{
    class FinalizeThread : public salhelper::Thread
    {
        FormulaBuffer& mrParent;
        size_t mnThreadCount;
    public:
        FinalizeThread( FormulaBuffer& rParent, size_t nThreadCount );
        virtual ~FinalizeThread();

    protected:
        virtual void execute();
    };

    friend class FinalizeThread;

public:
    /**
     * Represents a shared formula definition.
     */
@@ -74,6 +91,20 @@ class FormulaBuffer : public WorkbookHelper
        TokenRangeAddressItem( const TokenAddressItem& rTokenAndAddress, const ::com::sun::star::table::CellRangeAddress& rCellRangeAddress ) : maTokenAndAddress( rTokenAndAddress ), maCellRangeAddress( rCellRangeAddress ) {}
    };

    typedef std::pair<com::sun::star::table::CellAddress, double> ValueAddressPair;

    struct SheetItem
    {
        std::vector<TokenAddressItem>* mpCellFormulas;
        std::vector<TokenRangeAddressItem>* mpArrayFormulas;
        std::vector<ValueAddressPair>* mpCellFormulaValues;
        std::vector<SharedFormulaEntry>* mpSharedFormulaEntries;
        std::vector<SharedFormulaDesc>* mpSharedFormulaIDs;

        SheetItem();
    };

private:
    typedef ::std::map< SCTAB, std::vector<TokenAddressItem> > FormulaDataMap;
    typedef ::std::map< SCTAB, std::vector<TokenRangeAddressItem> > ArrayFormulaDataMap;
    // sheet -> list of shared formula descriptions
@@ -81,19 +112,16 @@ class FormulaBuffer : public WorkbookHelper
    // sheet -> stuff needed to create shared formulae
    typedef ::std::map< SCTAB, std::vector<SharedFormulaEntry> >  SheetToFormulaEntryMap;

    typedef ::std::pair< ::com::sun::star::table::CellAddress, double > ValueAddressPair;
    typedef ::std::map< SCTAB, std::vector<ValueAddressPair> > FormulaValueMap;

    osl::Mutex maMtxData;
    FormulaDataMap maCellFormulas;
    ArrayFormulaDataMap maCellArrayFormulas;
    SheetToFormulaEntryMap maSharedFormulas;
    SheetToSharedFormulaid maSharedFormulaIds;
    FormulaValueMap maCellFormulaValues;

    void                applyArrayFormulas(  const std::vector< TokenRangeAddressItem >& rVector );
    void                applyCellFormulas(  const std::vector< TokenAddressItem >& rVector );
    void                applyCellFormulaValues( const std::vector< ValueAddressPair >& rVector );
    void applySharedFormulas( SCTAB nTab );
    SheetItem getSheetItem( SCTAB nTab );

public:
    explicit            FormulaBuffer( const WorkbookHelper& rHelper );
diff --git a/sc/source/filter/oox/formulabuffer.cxx b/sc/source/filter/oox/formulabuffer.cxx
index 04be139..c3e1d24 100644
--- a/sc/source/filter/oox/formulabuffer.cxx
+++ b/sc/source/filter/oox/formulabuffer.cxx
@@ -33,114 +33,22 @@ using namespace ::com::sun::star::table;
using namespace ::com::sun::star::sheet;
using namespace ::com::sun::star::container;

#include <boost/scoped_ptr.hpp>

namespace oox { namespace xls {

FormulaBuffer::SharedFormulaEntry::SharedFormulaEntry(
    const table::CellAddress& rAddr, const table::CellRangeAddress& rRange,
    const OUString& rTokenStr, sal_Int32 nSharedId ) :
    maAddress(rAddr), maRange(rRange), maTokenStr(rTokenStr), mnSharedId(nSharedId) {}
namespace {

FormulaBuffer::SharedFormulaDesc::SharedFormulaDesc(
    const com::sun::star::table::CellAddress& rAddr, sal_Int32 nSharedId,
    const OUString& rCellValue, sal_Int32 nValueType ) :
    maAddress(rAddr), mnSharedId(nSharedId), maCellValue(rCellValue), mnValueType(nValueType) {}

FormulaBuffer::FormulaBuffer( const WorkbookHelper& rHelper ) : WorkbookHelper( rHelper )
void applySharedFormulas(
    ScDocumentImport& rDoc,
    SvNumberFormatter& rFormatter,
    std::vector<FormulaBuffer::SharedFormulaEntry>& rSharedFormulas,
    std::vector<FormulaBuffer::SharedFormulaDesc>& rCells )
{
}

void FormulaBuffer::finalizeImport()
{
    ISegmentProgressBarRef xFormulaBar = getProgressBar().createSegment( getProgressBar().getFreeLength() );

    ScDocument& rDoc = getScDocument();
    rDoc.SetAutoNameCache( new ScAutoNameCache( &rDoc ) );
    for (SCTAB nTab = 0, nElem = rDoc.GetTableCount(); nTab < nElem; ++nTab)
    {
        double fPosition = static_cast< double> (nTab + 1) /static_cast<double>(nElem);
        xFormulaBar->setPosition( fPosition );

        applySharedFormulas(nTab);

        FormulaDataMap::iterator cellIt = maCellFormulas.find( nTab );
        if ( cellIt != maCellFormulas.end() )
        {
            applyCellFormulas( cellIt->second );
        }

        ArrayFormulaDataMap::iterator itArray = maCellArrayFormulas.find( nTab );
        if ( itArray != maCellArrayFormulas.end() )
        {
            applyArrayFormulas( itArray->second );
        }

        FormulaValueMap::iterator itValues = maCellFormulaValues.find( nTab );
        if ( itValues != maCellFormulaValues.end() )
        {
            std::vector< ValueAddressPair > & rVector = itValues->second;
            applyCellFormulaValues( rVector );
        }
    }
    rDoc.SetAutoNameCache( NULL );
    xFormulaBar->setPosition( 1.0 );
}

void FormulaBuffer::applyCellFormulas( const std::vector< TokenAddressItem >& rVector )
{
    ScDocumentImport& rDoc = getDocImport();
    ScExternalRefManager::ApiGuard aExtRefGuard(&rDoc.getDoc());
    for ( std::vector< TokenAddressItem >::const_iterator it = rVector.begin(), it_end = rVector.end(); it != it_end; ++it )
    {
        ScAddress aPos;
        ScUnoConversion::FillScAddress(aPos, it->maCellAddress);
        ScCompiler aCompiler(&rDoc.getDoc(), aPos);
        aCompiler.SetGrammar(formula::FormulaGrammar::GRAM_ENGLISH_XL_OOX);
        ScTokenArray* pCode = aCompiler.CompileString(it->maTokenStr);
        if (!pCode)
            continue;

        rDoc.setFormulaCell(aPos, pCode);
    }
}

void FormulaBuffer::applyCellFormulaValues( const std::vector< ValueAddressPair >& rVector )
{
    ScDocument& rDoc = getScDocument();
    for ( std::vector< ValueAddressPair >::const_iterator it = rVector.begin(), it_end = rVector.end(); it != it_end; ++it )
    {
        ScAddress aCellPos;
        ScUnoConversion::FillScAddress( aCellPos, it->first );
        ScFormulaCell* pCell = rDoc.GetFormulaCell(aCellPos);
        if (pCell)
        {
            pCell->SetHybridDouble( it->second );
            pCell->ResetDirty();
            pCell->SetChanged(false);
        }
    }
}

void FormulaBuffer::applySharedFormulas( SCTAB nTab )
{
    SheetToFormulaEntryMap::const_iterator itShared = maSharedFormulas.find(nTab);
    if (itShared == maSharedFormulas.end())
        // There is no shared formulas for this sheet.
        return;

    SheetToSharedFormulaid::const_iterator itCells = maSharedFormulaIds.find(nTab);
    if (itCells == maSharedFormulaIds.end())
        // There is no formula cells that use shared formulas for this sheet.
        return;

    const std::vector<SharedFormulaEntry>& rSharedFormulas = itShared->second;
    const std::vector<SharedFormulaDesc>& rCells = itCells->second;

    ScDocumentImport& rDoc = getDocImport();

    sc::SharedFormulaGroups aGroups;
    {
        // Process shared formulas first.
        std::vector<SharedFormulaEntry>::const_iterator it = rSharedFormulas.begin(), itEnd = rSharedFormulas.end();
        std::vector<FormulaBuffer::SharedFormulaEntry>::const_iterator it = rSharedFormulas.begin(), itEnd = rSharedFormulas.end();
        for (; it != itEnd; ++it)
        {
            const table::CellAddress& rAddr = it->maAddress;
@@ -150,6 +58,7 @@ void FormulaBuffer::applySharedFormulas( SCTAB nTab )
            ScAddress aPos;
            ScUnoConversion::FillScAddress(aPos, rAddr);
            ScCompiler aComp(&rDoc.getDoc(), aPos);
            aComp.SetNumberFormatter(&rFormatter);
            aComp.SetGrammar(formula::FormulaGrammar::GRAM_ENGLISH_XL_OOX);
            ScTokenArray* pArray = aComp.CompileString(rTokenStr);
            if (pArray)
@@ -159,7 +68,7 @@ void FormulaBuffer::applySharedFormulas( SCTAB nTab )

    {
        // Process formulas that use shared formulas.
        std::vector<SharedFormulaDesc>::const_iterator it = rCells.begin(), itEnd = rCells.end();
        std::vector<FormulaBuffer::SharedFormulaDesc>::const_iterator it = rCells.begin(), itEnd = rCells.end();
        for (; it != itEnd; ++it)
        {
            const table::CellAddress& rAddr = it->maAddress;
@@ -194,10 +103,32 @@ void FormulaBuffer::applySharedFormulas( SCTAB nTab )
    }
}

void FormulaBuffer::applyArrayFormulas( const std::vector< TokenRangeAddressItem >& rVector )
void applyCellFormulas(
    ScDocumentImport& rDoc, SvNumberFormatter& rFormatter,
    const std::vector<FormulaBuffer::TokenAddressItem>& rCells )
{
    ScDocumentImport& rDocImport = getDocImport();
    std::vector<TokenRangeAddressItem>::const_iterator it = rVector.begin(), itEnd = rVector.end();
    ScExternalRefManager::ApiGuard aExtRefGuard(&rDoc.getDoc());
    std::vector<FormulaBuffer::TokenAddressItem>::const_iterator it = rCells.begin(), itEnd = rCells.end();
    for (; it != itEnd; ++it)
    {
        ScAddress aPos;
        ScUnoConversion::FillScAddress(aPos, it->maCellAddress);
        ScCompiler aCompiler(&rDoc.getDoc(), aPos);
        aCompiler.SetNumberFormatter(&rFormatter);
        aCompiler.SetGrammar(formula::FormulaGrammar::GRAM_ENGLISH_XL_OOX);
        ScTokenArray* pCode = aCompiler.CompileString(it->maTokenStr);
        if (!pCode)
            continue;

        rDoc.setFormulaCell(aPos, pCode);
    }
}

void applyArrayFormulas(
    ScDocumentImport& rDoc, SvNumberFormatter& rFormatter,
    const std::vector<FormulaBuffer::TokenRangeAddressItem>& rArrays )
{
    std::vector<FormulaBuffer::TokenRangeAddressItem>::const_iterator it = rArrays.begin(), itEnd = rArrays.end();
    for (; it != itEnd; ++it)
    {
        ScAddress aPos;
@@ -205,14 +136,187 @@ void FormulaBuffer::applyArrayFormulas( const std::vector< TokenRangeAddressItem
        ScRange aRange;
        ScUnoConversion::FillScRange(aRange, it->maCellRangeAddress);

        ScCompiler aComp(&rDocImport.getDoc(), aPos);
        ScCompiler aComp(&rDoc.getDoc(), aPos);
        aComp.SetNumberFormatter(&rFormatter);
        aComp.SetGrammar(formula::FormulaGrammar::GRAM_ENGLISH_XL_OOX);
        boost::scoped_ptr<ScTokenArray> pArray(aComp.CompileString(it->maTokenAndAddress.maTokenStr));
        if (pArray)
            rDocImport.setMatrixCells(aRange, *pArray, formula::FormulaGrammar::GRAM_ENGLISH_XL_OOX);
            rDoc.setMatrixCells(aRange, *pArray, formula::FormulaGrammar::GRAM_ENGLISH_XL_OOX);
    }
}

void applyCellFormulaValues(
    ScDocumentImport& rDoc, const std::vector<FormulaBuffer::ValueAddressPair>& rVector )
{
    std::vector<FormulaBuffer::ValueAddressPair>::const_iterator it = rVector.begin(), itEnd = rVector.end();
    for (; it != itEnd; ++it)
    {
        ScAddress aCellPos;
        ScUnoConversion::FillScAddress(aCellPos, it->first);
        ScFormulaCell* pCell = rDoc.getDoc().GetFormulaCell(aCellPos);
        if (pCell)
        {
            pCell->SetHybridDouble(it->second);
            pCell->ResetDirty();
            pCell->SetChanged(false);
        }
    }
}

class WorkerThread : public salhelper::Thread
{
    ScDocumentImport& mrDoc;
    FormulaBuffer::SheetItem& mrItem;
    boost::scoped_ptr<SvNumberFormatter> mpFormatter;

    WorkerThread( const WorkerThread& );
    WorkerThread& operator= ( const WorkerThread& );

public:
    WorkerThread( ScDocumentImport& rDoc, FormulaBuffer::SheetItem& rItem, SvNumberFormatter* pFormatter ) :
        salhelper::Thread("xlsx-import-formula-buffer-worker-thread"),
        mrDoc(rDoc), mrItem(rItem), mpFormatter(pFormatter) {}

    virtual ~WorkerThread() {}

protected:
    virtual void execute()
    {
        if (mrItem.mpSharedFormulaEntries && mrItem.mpSharedFormulaIDs)
            applySharedFormulas(mrDoc, *mpFormatter, *mrItem.mpSharedFormulaEntries, *mrItem.mpSharedFormulaIDs);

        if (mrItem.mpCellFormulas)
            applyCellFormulas(mrDoc, *mpFormatter, *mrItem.mpCellFormulas);

        if (mrItem.mpArrayFormulas)
            applyArrayFormulas(mrDoc, *mpFormatter, *mrItem.mpArrayFormulas);

        if (mrItem.mpCellFormulaValues)
            applyCellFormulaValues(mrDoc, *mrItem.mpCellFormulaValues);
    }
};

}

FormulaBuffer::SharedFormulaEntry::SharedFormulaEntry(
    const table::CellAddress& rAddr, const table::CellRangeAddress& rRange,
    const OUString& rTokenStr, sal_Int32 nSharedId ) :
    maAddress(rAddr), maRange(rRange), maTokenStr(rTokenStr), mnSharedId(nSharedId) {}

FormulaBuffer::SharedFormulaDesc::SharedFormulaDesc(
    const com::sun::star::table::CellAddress& rAddr, sal_Int32 nSharedId,
    const OUString& rCellValue, sal_Int32 nValueType ) :
    maAddress(rAddr), mnSharedId(nSharedId), maCellValue(rCellValue), mnValueType(nValueType) {}

FormulaBuffer::SheetItem::SheetItem() :
    mpCellFormulas(NULL),
    mpArrayFormulas(NULL),
    mpCellFormulaValues(NULL),
    mpSharedFormulaEntries(NULL),
    mpSharedFormulaIDs(NULL) {}

FormulaBuffer::FinalizeThread::FinalizeThread( FormulaBuffer& rParent, size_t nThreadCount ) :
    salhelper::Thread("xlsx-import-formula-buffer-finalize-thread"),
    mrParent(rParent), mnThreadCount(nThreadCount) {}

FormulaBuffer::FinalizeThread::~FinalizeThread() {}

void FormulaBuffer::FinalizeThread::execute()
{
    ScDocumentImport& rDoc = mrParent.getDocImport();
    rDoc.getDoc().SetAutoNameCache(new ScAutoNameCache(&rDoc.getDoc()));
    SCTAB nTabCount = rDoc.getDoc().GetTableCount();

    std::vector<SheetItem> aSheetItems;
    aSheetItems.reserve(nTabCount);
    for (SCTAB nTab = 0; nTab < nTabCount; ++nTab)
        aSheetItems.push_back(mrParent.getSheetItem(nTab));

    typedef rtl::Reference<WorkerThread> WorkerThreadRef;
    std::vector<WorkerThreadRef> aThreads;
    aThreads.reserve(mnThreadCount);

    std::vector<SheetItem>::iterator it = aSheetItems.begin(), itEnd = aSheetItems.end();

    while (it != itEnd)
    {
        for (size_t i = 0; i < mnThreadCount; ++i)
        {
            if (it == itEnd)
                break;

            WorkerThreadRef xThread(new WorkerThread(rDoc, *it, rDoc.getDoc().CreateFormatTable()));
            ++it;
            aThreads.push_back(xThread);
            xThread->launch();
        }

        for (size_t i = 0, n = aThreads.size(); i < n; ++i)
        {
            if (aThreads[i].is())
                aThreads[i]->join();
        }

        aThreads.clear();
    }

    rDoc.getDoc().SetAutoNameCache(NULL);
}

FormulaBuffer::FormulaBuffer( const WorkbookHelper& rHelper ) : WorkbookHelper( rHelper )
{
}

void FormulaBuffer::finalizeImport()
{
    ISegmentProgressBarRef xFormulaBar = getProgressBar().createSegment( getProgressBar().getFreeLength() );

    rtl::Reference<FinalizeThread> xThreadMgr(new FinalizeThread(*this, 1));
    xThreadMgr->launch();

    if (xThreadMgr.is())
        xThreadMgr->join();

    xFormulaBar->setPosition( 1.0 );
}

FormulaBuffer::SheetItem FormulaBuffer::getSheetItem( SCTAB nTab )
{
    osl::MutexGuard aGuard(&maMtxData);

    SheetItem aItem;
    {
        FormulaDataMap::iterator it = maCellFormulas.find(nTab);
        if (it != maCellFormulas.end())
            aItem.mpCellFormulas = &it->second;
    }

    {
        ArrayFormulaDataMap::iterator it = maCellArrayFormulas.find(nTab);
        if (it != maCellArrayFormulas.end())
            aItem.mpArrayFormulas = &it->second;
    }

    {
        FormulaValueMap::iterator it = maCellFormulaValues.find(nTab);
        if (it != maCellFormulaValues.end())
            aItem.mpCellFormulaValues = &it->second;
    }

    {
        SheetToFormulaEntryMap::iterator it = maSharedFormulas.find(nTab);
        if (it != maSharedFormulas.end())
            aItem.mpSharedFormulaEntries = &it->second;
    }

    {
        SheetToSharedFormulaid::iterator it = maSharedFormulaIds.find(nTab);
        if (it != maSharedFormulaIds.end())
            aItem.mpSharedFormulaIDs = &it->second;
    }
    return aItem;
}

void FormulaBuffer::createSharedFormulaMapEntry(
    const table::CellAddress& rAddress, const table::CellRangeAddress& rRange,
    sal_Int32 nSharedId, const OUString& rTokens )