/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
 
#include <documentimport.hxx>
#include <document.hxx>
#include <table.hxx>
#include <column.hxx>
#include <formulacell.hxx>
#include <docoptio.hxx>
#include <globalnames.hxx>
#include <mtvelements.hxx>
#include <tokenarray.hxx>
#include <stringutil.hxx>
#include <compiler.hxx>
#include <paramisc.hxx>
#include <listenercontext.hxx>
#include <attarray.hxx>
#include <sharedformula.hxx>
#include <bcaslot.hxx>
#include <scopetools.hxx>
 
#include <svl/sharedstringpool.hxx>
#include <svl/languageoptions.hxx>
#include <o3tl/make_unique.hxx>
#include <unotools/configmgr.hxx>
 
namespace {
 
struct ColAttr
{
    bool mbLatinNumFmtOnly;
 
    ColAttr() : mbLatinNumFmtOnly(false) {}
};
 
struct TabAttr
{
    std::vector<ColAttr> maCols;
};
 
}
 
struct ScDocumentImportImpl
{
    ScDocument& mrDoc;
    sc::StartListeningContext maListenCxt;
    std::vector<sc::TableColumnBlockPositionSet> maBlockPosSet;
    SvtScriptType mnDefaultScriptNumeric;
    std::vector<TabAttr> maTabAttrs;
 
    explicit ScDocumentImportImpl(ScDocument& rDoc) :
        mrDoc(rDoc),
        maListenCxt(rDoc),
        mnDefaultScriptNumeric(SvtScriptType::UNKNOWN) {}
 
    static bool isValid( size_t nTab, size_t nCol )
    {
        return (nTab <= size_t(MAXTAB) && nCol <= size_t(MAXCOL));
    }
 
    ColAttr* getColAttr( size_t nTab, size_t nCol )
    {
        if (!isValid(nTab, nCol))
            return nullptr;
 
        if (nTab >= maTabAttrs.size())
            maTabAttrs.resize(nTab+1);
 
        TabAttr& rTab = maTabAttrs[nTab];
        if (nCol >= rTab.maCols.size())
            rTab.maCols.resize(nCol+1);
 
        return &rTab.maCols[nCol];
    }
 
    sc::ColumnBlockPosition* getBlockPosition( SCTAB nTab, SCCOL nCol )
    {
        if (!isValid(nTab, nCol))
            return nullptr;
 
        if (size_t(nTab) >= maBlockPosSet.size())
        {
            for (SCTAB i = maBlockPosSet.size(); i <= nTab; ++i)
                maBlockPosSet.emplace_back(mrDoc, i);
        }
 
        sc::TableColumnBlockPositionSet& rTab = maBlockPosSet[nTab];
        return rTab.getBlockPosition(nCol);
    }
 
    void initForSheets()
    {
        size_t n = mrDoc.GetTableCount();
        for (size_t i = maBlockPosSet.size(); i < n; ++i)
            maBlockPosSet.emplace_back(mrDoc, i);
 
        if (maTabAttrs.size() < n)
            maTabAttrs.resize(n);
    }
};
 
ScDocumentImport::Attrs::Attrs() : mbLatinNumFmtOnly(false) {}
 
ScDocumentImport::Attrs::~Attrs() {}
 
ScDocumentImport::ScDocumentImport(ScDocument& rDoc) : mpImpl(new ScDocumentImportImpl(rDoc)) {}
 
ScDocumentImport::~ScDocumentImport()
{
}
 
ScDocument& ScDocumentImport::getDoc()
{
    return mpImpl->mrDoc;
}
 
const ScDocument& ScDocumentImport::getDoc() const
{
    return mpImpl->mrDoc;
}
 
void ScDocumentImport::initForSheets()
{
    mpImpl->initForSheets();
}
 
void ScDocumentImport::setDefaultNumericScript(SvtScriptType nScript)
{
    mpImpl->mnDefaultScriptNumeric = nScript;
}
 
void ScDocumentImport::setCellStyleToSheet(SCTAB nTab, const ScStyleSheet& rStyle)
{
    ScTable* pTab = mpImpl->mrDoc.FetchTable(nTab);
    if (!pTab)
        return;
 
    pTab->ApplyStyleArea(0, 0, MAXCOL, MAXROW, rStyle);
}
 
SCTAB ScDocumentImport::getSheetIndex(const OUString& rName) const
{
    SCTAB nTab = -1;
    if (!mpImpl->mrDoc.GetTable(rName, nTab))
        return -1;
 
    return nTab;
}
 
SCTAB ScDocumentImport::getSheetCount() const
{
    return mpImpl->mrDoc.maTabs.size();
}
 
bool ScDocumentImport::appendSheet(const OUString& rName)
{
    SCTAB nTabCount = mpImpl->mrDoc.maTabs.size();
    if (!ValidTab(nTabCount))
        return false;
 
    mpImpl->mrDoc.maTabs.emplace_back(new ScTable(&mpImpl->mrDoc, nTabCount, rName));
    return true;
}
 
void ScDocumentImport::setSheetName(SCTAB nTab, const OUString& rName)
{
    mpImpl->mrDoc.SetTabNameOnLoad(nTab, rName);
}
 
void ScDocumentImport::setOriginDate(sal_uInt16 nYear, sal_uInt16 nMonth, sal_uInt16 nDay)
{
    if (!mpImpl->mrDoc.pDocOptions)
        mpImpl->mrDoc.pDocOptions.reset( new ScDocOptions );
 
    mpImpl->mrDoc.pDocOptions->SetDate(nDay, nMonth, nYear);
}
 
void ScDocumentImport::setAutoInput(const ScAddress& rPos, const OUString& rStr, const ScSetStringParam* pStringParam)
{
    ScTable* pTab = mpImpl->mrDoc.FetchTable(rPos.Tab());
    if (!pTab)
        return;
 
    sc::ColumnBlockPosition* pBlockPos = mpImpl->getBlockPosition(rPos.Tab(), rPos.Col());
 
    if (!pBlockPos)
        return;
 
    // If ScSetStringParam was given, ScColumn::ParseString() shall take care
    // of checking. Ensure caller said so.
    assert(!pStringParam || pStringParam->mbCheckLinkFormula);
 
    ScCellValue aCell;
    pTab->aCol[rPos.Col()].ParseString(
        aCell, rPos.Row(), rPos.Tab(), rStr, mpImpl->mrDoc.GetAddressConvention(), pStringParam);
 
    sc::CellStoreType& rCells = pTab->aCol[rPos.Col()].maCells;
    switch (aCell.meType)
    {
        case CELLTYPE_STRING:
            // string is copied.
            pBlockPos->miCellPos = rCells.set(pBlockPos->miCellPos, rPos.Row(), *aCell.mpString);
        break;
        case CELLTYPE_EDIT:
            // Cell takes the ownership of the text object.
            pBlockPos->miCellPos = rCells.set(pBlockPos->miCellPos, rPos.Row(), aCell.mpEditText);
            aCell.mpEditText = nullptr;
        break;
        case CELLTYPE_VALUE:
            pBlockPos->miCellPos = rCells.set(pBlockPos->miCellPos, rPos.Row(), aCell.mfValue);
        break;
        case CELLTYPE_FORMULA:
            if (!pStringParam)
                mpImpl->mrDoc.CheckLinkFormulaNeedingCheck( *aCell.mpFormula->GetCode());
            // This formula cell instance is directly placed in the document without copying.
            pBlockPos->miCellPos = rCells.set(pBlockPos->miCellPos, rPos.Row(), aCell.mpFormula);
            aCell.mpFormula = nullptr;
        break;
        default:
            pBlockPos->miCellPos = rCells.set_empty(pBlockPos->miCellPos, rPos.Row(), rPos.Row());
    }
}
 
void ScDocumentImport::setNumericCell(const ScAddress& rPos, double fVal)
{
    ScTable* pTab = mpImpl->mrDoc.FetchTable(rPos.Tab());
    if (!pTab)
        return;
 
    sc::ColumnBlockPosition* pBlockPos = mpImpl->getBlockPosition(rPos.Tab(), rPos.Col());
 
    if (!pBlockPos)
        return;
 
    sc::CellStoreType& rCells = pTab->aCol[rPos.Col()].maCells;
    pBlockPos->miCellPos = rCells.set(pBlockPos->miCellPos, rPos.Row(), fVal);
}
 
void ScDocumentImport::setStringCell(const ScAddress& rPos, const OUString& rStr)
{
    ScTable* pTab = mpImpl->mrDoc.FetchTable(rPos.Tab());
    if (!pTab)
        return;
 
    sc::ColumnBlockPosition* pBlockPos = mpImpl->getBlockPosition(rPos.Tab(), rPos.Col());
 
    if (!pBlockPos)
        return;
 
    svl::SharedString aSS = mpImpl->mrDoc.GetSharedStringPool().intern(rStr);
    if (!aSS.getData())
        return;
 
    sc::CellStoreType& rCells = pTab->aCol[rPos.Col()].maCells;
    pBlockPos->miCellPos = rCells.set(pBlockPos->miCellPos, rPos.Row(), aSS);
}
 
void ScDocumentImport::setEditCell(const ScAddress& rPos, std::unique_ptr<EditTextObject> pEditText)
{
    ScTable* pTab = mpImpl->mrDoc.FetchTable(rPos.Tab());
    if (!pTab)
        return;
 
    sc::ColumnBlockPosition* pBlockPos = mpImpl->getBlockPosition(rPos.Tab(), rPos.Col());
 
    if (!pBlockPos)
        return;
 
    pEditText->NormalizeString(mpImpl->mrDoc.GetSharedStringPool());
    sc::CellStoreType& rCells = pTab->aCol[rPos.Col()].maCells;
    pBlockPos->miCellPos = rCells.set(pBlockPos->miCellPos, rPos.Row(), pEditText.release());
}
 
void ScDocumentImport::setFormulaCell(
    const ScAddress& rPos, const OUString& rFormula, formula::FormulaGrammar::Grammar eGrammar,
    const double* pResult )
{
    ScTable* pTab = mpImpl->mrDoc.FetchTable(rPos.Tab());
    if (!pTab)
        return;
 
    sc::ColumnBlockPosition* pBlockPos = mpImpl->getBlockPosition(rPos.Tab(), rPos.Col());
 
    if (!pBlockPos)
        return;
 
    std::unique_ptr<ScFormulaCell> pFC =
        o3tl::make_unique<ScFormulaCell>(&mpImpl->mrDoc, rPos, rFormula, eGrammar);
 
    mpImpl->mrDoc.CheckLinkFormulaNeedingCheck( *pFC->GetCode());
 
    if (pResult)
    {
        // Set cached result to this formula cell.
        pFC->SetResultDouble(*pResult);
    }
 
    sc::CellStoreType& rCells = pTab->aCol[rPos.Col()].maCells;
    pBlockPos->miCellPos =
        rCells.set(pBlockPos->miCellPos, rPos.Row(), pFC.release());
}
 
void ScDocumentImport::setFormulaCell(
    const ScAddress& rPos, const OUString& rFormula, formula::FormulaGrammar::Grammar eGrammar,
    const OUString& rResult )
{
    ScTable* pTab = mpImpl->mrDoc.FetchTable(rPos.Tab());
    if (!pTab)
        return;
 
    sc::ColumnBlockPosition* pBlockPos = mpImpl->getBlockPosition(rPos.Tab(), rPos.Col());
 
    if (!pBlockPos)
        return;
 
    std::unique_ptr<ScFormulaCell> pFC =
        o3tl::make_unique<ScFormulaCell>(&mpImpl->mrDoc, rPos, rFormula, eGrammar);
 
    mpImpl->mrDoc.CheckLinkFormulaNeedingCheck( *pFC->GetCode());
 
    // Set cached result to this formula cell.
    pFC->SetHybridString(mpImpl->mrDoc.GetSharedStringPool().intern(rResult));
 
    sc::CellStoreType& rCells = pTab->aCol[rPos.Col()].maCells;
    pBlockPos->miCellPos =
        rCells.set(pBlockPos->miCellPos, rPos.Row(), pFC.release());
}
 
void ScDocumentImport::setFormulaCell(const ScAddress& rPos, ScTokenArray* pArray)
{
    ScTable* pTab = mpImpl->mrDoc.FetchTable(rPos.Tab());
    if (!pTab)
        return;
 
    sc::ColumnBlockPosition* pBlockPos = mpImpl->getBlockPosition(rPos.Tab(), rPos.Col());
 
    if (!pBlockPos)
        return;
 
    std::unique_ptr<ScFormulaCell> pFC =
        o3tl::make_unique<ScFormulaCell>(&mpImpl->mrDoc, rPos, pArray);
 
    mpImpl->mrDoc.CheckLinkFormulaNeedingCheck( *pFC->GetCode());
 
    sc::CellStoreType& rCells = pTab->aCol[rPos.Col()].maCells;
    pBlockPos->miCellPos =
        rCells.set(pBlockPos->miCellPos, rPos.Row(), pFC.release());
}
 
void ScDocumentImport::setFormulaCell(const ScAddress& rPos, ScFormulaCell* pCell)
{
    ScTable* pTab = mpImpl->mrDoc.FetchTable(rPos.Tab());
    if (!pTab)
        return;
 
    sc::ColumnBlockPosition* pBlockPos = mpImpl->getBlockPosition(rPos.Tab(), rPos.Col());
 
    if (!pBlockPos)
        return;
 
    if (pCell)
        mpImpl->mrDoc.CheckLinkFormulaNeedingCheck( *pCell->GetCode());
 
    sc::CellStoreType& rCells = pTab->aCol[rPos.Col()].maCells;
    pBlockPos->miCellPos =
        rCells.set(pBlockPos->miCellPos, rPos.Row(), pCell);
}
 
void ScDocumentImport::setMatrixCells(
    const ScRange& rRange, const ScTokenArray& rArray, formula::FormulaGrammar::Grammar eGram)
{
    const ScAddress& rBasePos = rRange.aStart;
 
    ScTable* pTab = mpImpl->mrDoc.FetchTable(rBasePos.Tab());
    if (!pTab)
        return;
 
    sc::ColumnBlockPosition* pBlockPos = mpImpl->getBlockPosition(rBasePos.Tab(), rBasePos.Col());
 
    if (!pBlockPos)
        return;
 
    if (utl::ConfigManager::IsFuzzing()) //just too slow
        return;
 
    sc::CellStoreType& rCells = pTab->aCol[rBasePos.Col()].maCells;
 
    // Set the master cell.
    ScFormulaCell* pCell = new ScFormulaCell(&mpImpl->mrDoc, rBasePos, rArray, eGram, ScMatrixMode::Formula);
 
    mpImpl->mrDoc.CheckLinkFormulaNeedingCheck( *pCell->GetCode());
 
    pBlockPos->miCellPos =
        rCells.set(pBlockPos->miCellPos, rBasePos.Row(), pCell);
 
    // Matrix formulas currently need re-calculation on import.
    pCell->SetMatColsRows(
        rRange.aEnd.Col()-rRange.aStart.Col()+1, rRange.aEnd.Row()-rRange.aStart.Row()+1);
 
    // Set the reference cells.
    ScSingleRefData aRefData;
    aRefData.InitFlags();
    aRefData.SetColRel(true);
    aRefData.SetRowRel(true);
    aRefData.SetTabRel(true);
    aRefData.SetAddress(rBasePos, rBasePos);
 
    ScTokenArray aArr; // consists only of one single reference token.
    formula::FormulaToken* t = aArr.AddMatrixSingleReference(aRefData);
 
    ScAddress aPos = rBasePos;
    for (SCROW nRow = rRange.aStart.Row()+1; nRow <= rRange.aEnd.Row(); ++nRow)
    {
        // Token array must be cloned so that each formula cell receives its own copy.
        aPos.SetRow(nRow);
        // Reference in each cell must point to the origin cell relative to the current cell.
        aRefData.SetAddress(rBasePos, aPos);
        *t->GetSingleRef() = aRefData;
        std::unique_ptr<ScTokenArray> pTokArr(aArr.Clone());
        pCell = new ScFormulaCell(&mpImpl->mrDoc, aPos, *pTokArr, eGram, ScMatrixMode::Reference);
        pBlockPos->miCellPos =
            rCells.set(pBlockPos->miCellPos, aPos.Row(), pCell);
    }
 
    for (SCCOL nCol = rRange.aStart.Col()+1; nCol <= rRange.aEnd.Col(); ++nCol)
    {
        pBlockPos = mpImpl->getBlockPosition(rBasePos.Tab(), nCol);
        if (!pBlockPos)
            return;
 
        sc::CellStoreType& rColCells = pTab->aCol[nCol].maCells;
 
        aPos.SetCol(nCol);
        for (SCROW nRow = rRange.aStart.Row(); nRow <= rRange.aEnd.Row(); ++nRow)
        {
            aPos.SetRow(nRow);
            aRefData.SetAddress(rBasePos, aPos);
            *t->GetSingleRef() = aRefData;
            std::unique_ptr<ScTokenArray> pTokArr(aArr.Clone());
            pCell = new ScFormulaCell(&mpImpl->mrDoc, aPos, *pTokArr, eGram, ScMatrixMode::Reference);
            pBlockPos->miCellPos =
                rColCells.set(pBlockPos->miCellPos, aPos.Row(), pCell);
        }
    }
}
 
void ScDocumentImport::setTableOpCells(const ScRange& rRange, const ScTabOpParam& rParam)
{
    SCTAB nTab = rRange.aStart.Tab();
    SCCOL nCol1 = rRange.aStart.Col();
    SCROW nRow1 = rRange.aStart.Row();
    SCCOL nCol2 = rRange.aEnd.Col();
    SCROW nRow2 = rRange.aEnd.Row();
 
    ScTable* pTab = mpImpl->mrDoc.FetchTable(nTab);
    if (!pTab)
        return;
 
    ScDocument* pDoc = &mpImpl->mrDoc;
    ScRefAddress aRef;
    OUStringBuffer aFormulaBuf;
    aFormulaBuf.append('=');
    aFormulaBuf.append(ScCompiler::GetNativeSymbol(ocTableOp));
    aFormulaBuf.append(ScCompiler::GetNativeSymbol(ocOpen));
 
    OUString aSep = ScCompiler::GetNativeSymbol(ocSep);
    if (rParam.meMode == ScTabOpParam::Column) // column only
    {
        aRef.Set(rParam.aRefFormulaCell.GetAddress(), true, false, false);
        aFormulaBuf.append(aRef.GetRefString(pDoc, nTab));
        aFormulaBuf.append(aSep);
        aFormulaBuf.append(rParam.aRefColCell.GetRefString(pDoc, nTab));
        aFormulaBuf.append(aSep);
        aRef.Set(nCol1, nRow1, nTab, false, true, true);
        aFormulaBuf.append(aRef.GetRefString(pDoc, nTab));
        nCol1++;
        nCol2 = std::min( nCol2, static_cast<SCCOL>(rParam.aRefFormulaEnd.Col() -
                    rParam.aRefFormulaCell.Col() + nCol1 + 1));
    }
    else if (rParam.meMode == ScTabOpParam::Row) // row only
    {
        aRef.Set(rParam.aRefFormulaCell.GetAddress(), false, true, false);
        aFormulaBuf.append(aRef.GetRefString(pDoc, nTab));
        aFormulaBuf.append(aSep);
        aFormulaBuf.append(rParam.aRefRowCell.GetRefString(pDoc, nTab));
        aFormulaBuf.append(aSep);
        aRef.Set(nCol1, nRow1, nTab, true, false, true);
        aFormulaBuf.append(aRef.GetRefString(pDoc, nTab));
        ++nRow1;
        nRow2 = std::min(
            nRow2, rParam.aRefFormulaEnd.Row() - rParam.aRefFormulaCell.Row() + nRow1 + 1);
    }
    else // both
    {
        aFormulaBuf.append(rParam.aRefFormulaCell.GetRefString(pDoc, nTab));
        aFormulaBuf.append(aSep);
        aFormulaBuf.append(rParam.aRefColCell.GetRefString(pDoc, nTab));
        aFormulaBuf.append(aSep);
        aRef.Set(nCol1, nRow1 + 1, nTab, false, true, true);
        aFormulaBuf.append(aRef.GetRefString(pDoc, nTab));
        aFormulaBuf.append(aSep);
        aFormulaBuf.append(rParam.aRefRowCell.GetRefString(pDoc, nTab));
        aFormulaBuf.append(aSep);
        aRef.Set(nCol1 + 1, nRow1, nTab, true, false, true);
        aFormulaBuf.append(aRef.GetRefString(pDoc, nTab));
        ++nCol1;
        ++nRow1;
    }
 
    aFormulaBuf.append(ScCompiler::GetNativeSymbol(ocClose));
 
    ScFormulaCell aRefCell(
        pDoc, ScAddress(nCol1, nRow1, nTab), aFormulaBuf.makeStringAndClear(),
        formula::FormulaGrammar::GRAM_NATIVE, ScMatrixMode::NONE);
 
    for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
    {
        sc::ColumnBlockPosition* pBlockPos = mpImpl->getBlockPosition(nTab, nCol);
 
        if (!pBlockPos)
            // Something went horribly wrong.
            return;
 
        sc::CellStoreType& rColCells = pTab->aCol[nCol].maCells;
 
        for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
        {
            ScAddress aPos(nCol, nRow, nTab);
            ScFormulaCell* pCell = new ScFormulaCell(aRefCell, *pDoc, aPos);
            pBlockPos->miCellPos =
                rColCells.set(pBlockPos->miCellPos, nRow, pCell);
        }
    }
}
 
void ScDocumentImport::setAttrEntries( SCTAB nTab, SCCOL nCol, Attrs&& rAttrs )
{
    ScTable* pTab = mpImpl->mrDoc.FetchTable(nTab);
    if (!pTab)
        return;
 
    ScColumn* pCol = pTab->FetchColumn(nCol);
    if (!pCol)
        return;
 
    ColAttr* pColAttr = mpImpl->getColAttr(nTab, nCol);
    if (pColAttr)
        pColAttr->mbLatinNumFmtOnly = rAttrs.mbLatinNumFmtOnly;
 
    pCol->pAttrArray->SetAttrEntries(std::move(rAttrs.mvData));
}
 
void ScDocumentImport::setRowsVisible(SCTAB nTab, SCROW nRowStart, SCROW nRowEnd, bool bVisible)
{
    if (!bVisible)
    {
        getDoc().ShowRows(nRowStart, nRowEnd, nTab, false);
        getDoc().SetDrawPageSize(nTab);
        getDoc().UpdatePageBreaks( nTab );
    }
    else
    {
        assert(false);
    }
}
 
void ScDocumentImport::setMergedCells(SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2)
{
    ScTable* pTab = mpImpl->mrDoc.FetchTable(nTab);
    if (!pTab)
        return;
 
    pTab->SetMergedCells(nCol1, nRow1, nCol2, nRow2);
}
 
namespace {
 
class CellStoreInitializer
{
    // The pimpl pattern here is intentional.
    //
    // The problem with having the attributes in CellStoreInitializer
    // directly is that, as a functor, it might be copied around. In
    // that case miPos in _copied_ object points to maAttrs in the
    // original object, not in the copy. So later, deep in mdds, we end
    // up comparing iterators from different sequences.
    //
    // This could be solved by defining copy constructor and operator=,
    // but given the limited usage of the class, I think it is simpler
    // to let copies share the state.
    struct Impl
    {
        sc::CellTextAttrStoreType maAttrs;
        sc::CellTextAttrStoreType::iterator miPos;
        SvtScriptType mnScriptNumeric;
 
        explicit Impl(const SvtScriptType nScriptNumeric)
            : maAttrs(MAXROWCOUNT), miPos(maAttrs.begin()), mnScriptNumeric(nScriptNumeric)
        {}
    };
 
    ScDocumentImportImpl& mrDocImpl;
    SCTAB mnTab;
    SCCOL mnCol;
 
public:
    CellStoreInitializer( ScDocumentImportImpl& rDocImpl, SCTAB nTab, SCCOL nCol ) :
        mrDocImpl(rDocImpl),
        mnTab(nTab),
        mnCol(nCol),
        mpImpl(new Impl(mrDocImpl.mnDefaultScriptNumeric))
    {}
 
    std::shared_ptr<Impl> mpImpl;
 
    void operator() (const sc::CellStoreType::value_type& node)
    {
        if (node.type == sc::element_type_empty)
            return;
 
        // Fill with default values for non-empty cell segments.
        sc::CellTextAttr aDefault;
        switch (node.type)
        {
            case sc::element_type_numeric:
            {
                aDefault.mnScriptType = mpImpl->mnScriptNumeric;
                const ColAttr* p = mrDocImpl.getColAttr(mnTab, mnCol);
                if (p && p->mbLatinNumFmtOnly)
                    aDefault.mnScriptType = SvtScriptType::LATIN;
            }
            break;
            case sc::element_type_formula:
            {
                const ColAttr* p = mrDocImpl.getColAttr(mnTab, mnCol);
                if (p && p->mbLatinNumFmtOnly)
                {
                    // We can assume latin script type if the block only
                    // contains formula cells with numeric results.
                    ScFormulaCell** pp = &sc::formula_block::at(*node.data, 0);
                    ScFormulaCell** ppEnd = pp + node.size;
                    bool bNumResOnly = true;
                    for (; pp != ppEnd; ++pp)
                    {
                        const ScFormulaCell& rCell = **pp;
                        if (!rCell.IsValueNoError())
                        {
                            bNumResOnly = false;
                            break;
                        }
                    }
 
                    if (bNumResOnly)
                        aDefault.mnScriptType = SvtScriptType::LATIN;
                }
            }
            break;
            default:
                ;
        }
 
        std::vector<sc::CellTextAttr> aDefaults(node.size, aDefault);
        mpImpl->miPos = mpImpl->maAttrs.set(mpImpl->miPos, node.position, aDefaults.begin(), aDefaults.end());
 
        if (node.type == sc::element_type_formula)
        {
            // Have all formula cells start listening to the document.
            ScFormulaCell** pp = &sc::formula_block::at(*node.data, 0);
            ScFormulaCell** ppEnd = pp + node.size;
            for (; pp != ppEnd; ++pp)
            {
                ScFormulaCell& rFC = **pp;
                if (rFC.IsSharedTop())
                {
                    // Register formula cells as a group.
                    sc::SharedFormulaUtil::startListeningAsGroup(mrDocImpl.maListenCxt, pp);
                    pp += rFC.GetSharedLength() - 1; // Move to the last one in the group.
                }
                else
                    rFC.StartListeningTo(mrDocImpl.maListenCxt);
            }
        }
    }
 
    void swap(sc::CellTextAttrStoreType& rAttrs)
    {
        mpImpl->maAttrs.swap(rAttrs);
    }
};
 
}
 
void ScDocumentImport::finalize()
{
    // Populate the text width and script type arrays in all columns. Also
    // activate all formula cells.
    ScDocument::TableContainer::iterator itTab = mpImpl->mrDoc.maTabs.begin(), itTabEnd = mpImpl->mrDoc.maTabs.end();
    for (; itTab != itTabEnd; ++itTab)
    {
        if (!*itTab)
            continue;
 
        ScTable& rTab = **itTab;
        SCCOL nNumCols = rTab.aCol.size();
        for (SCCOL nColIdx = 0; nColIdx < nNumCols; ++nColIdx)
            initColumn(rTab.aCol[nColIdx]);
    }
 
    mpImpl->mrDoc.finalizeOutlineImport();
}
 
void ScDocumentImport::initColumn(ScColumn& rCol)
{
    rCol.RegroupFormulaCells();
 
    CellStoreInitializer aFunc(*mpImpl, rCol.nTab, rCol.nCol);
    std::for_each(rCol.maCells.begin(), rCol.maCells.end(), aFunc);
    aFunc.swap(rCol.maCellTextAttrs);
 
    rCol.CellStorageModified();
}
 
namespace {
 
class CellStoreAfterImportBroadcaster
{
public:
 
    CellStoreAfterImportBroadcaster() {}
 
    void operator() (const sc::CellStoreType::value_type& node)
    {
        if (node.type == sc::element_type_formula)
        {
            // Broadcast all formula cells marked for recalc.
            ScFormulaCell** pp = &sc::formula_block::at(*node.data, 0);
            ScFormulaCell** ppEnd = pp + node.size;
            for (; pp != ppEnd; ++pp)
            {
                if ((*pp)->GetCode()->IsRecalcModeMustAfterImport())
                    (*pp)->SetDirty();
            }
        }
    }
};
 
}
 
void ScDocumentImport::broadcastRecalcAfterImport()
{
    sc::AutoCalcSwitch aACSwitch( mpImpl->mrDoc, false);
    ScBulkBroadcast aBulkBroadcast( mpImpl->mrDoc.GetBASM(), SfxHintId::ScDataChanged);
 
    ScDocument::TableContainer::iterator itTab = mpImpl->mrDoc.maTabs.begin(), itTabEnd = mpImpl->mrDoc.maTabs.end();
    for (; itTab != itTabEnd; ++itTab)
    {
        if (!*itTab)
            continue;
 
        ScTable& rTab = **itTab;
        SCCOL nNumCols = rTab.aCol.size();
        for (SCCOL nColIdx = 0; nColIdx < nNumCols; ++nColIdx)
            broadcastRecalcAfterImportColumn(rTab.aCol[nColIdx]);
    }
}
 
void ScDocumentImport::broadcastRecalcAfterImportColumn(ScColumn& rCol)
{
    CellStoreAfterImportBroadcaster aFunc;
    std::for_each(rCol.maCells.begin(), rCol.maCells.end(), aFunc);
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V1029 Numeric Truncation Error. Result of the 'size' function is written to the 16-bit variable.