diff options
author | Kohei Yoshida <kohei.yoshida@gmail.com> | 2017-12-13 22:27:03 -0500 |
---|---|---|
committer | Kohei Yoshida <libreoffice@kohei.us> | 2017-12-18 23:28:02 +0100 |
commit | 1c7fa9528f7373e6798b597a42abe38f20a306aa (patch) | |
tree | 9777e93c63ae9c7615eb6929ddd3739f80cfd373 /sc/source | |
parent | a72f3d40def7878ae487c8c34cd84da7d90fc99a (diff) |
Defer cell value insertion until later.
Because Calc's formula engine expects all named expressions to be
present at the time of re-calculations, we need to delay insertion
of cell values until after the named expressions are inserted into
the document.
Change-Id: I54c7d3dc86b3e2c5c192337b1cebfbdfb901ab1f
Reviewed-on: https://gerrit.libreoffice.org/46665
Reviewed-by: Kohei Yoshida <libreoffice@kohei.us>
Tested-by: Kohei Yoshida <libreoffice@kohei.us>
Diffstat (limited to 'sc/source')
-rw-r--r-- | sc/source/core/data/documentimport.cxx | 42 | ||||
-rw-r--r-- | sc/source/filter/inc/orcusinterface.hxx | 51 | ||||
-rw-r--r-- | sc/source/filter/orcus/interface.cxx | 293 |
3 files changed, 314 insertions, 72 deletions
diff --git a/sc/source/core/data/documentimport.cxx b/sc/source/core/data/documentimport.cxx index 183f3e1650af..6208789f416e 100644 --- a/sc/source/core/data/documentimport.cxx +++ b/sc/source/core/data/documentimport.cxx @@ -25,9 +25,7 @@ #include <svl/sharedstringpool.hxx> #include <svl/languageoptions.hxx> - -#include <memory> -#include <vector> +#include <o3tl/make_unique.hxx> namespace { @@ -271,7 +269,35 @@ void ScDocumentImport::setEditCell(const ScAddress& rPos, EditTextObject* pEditT } void ScDocumentImport::setFormulaCell( - const ScAddress& rPos, const OUString& rFormula, formula::FormulaGrammar::Grammar eGrammar) + 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); + + 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) @@ -282,9 +308,15 @@ void ScDocumentImport::setFormulaCell( if (!pBlockPos) return; + std::unique_ptr<ScFormulaCell> pFC = + o3tl::make_unique<ScFormulaCell>(&mpImpl->mrDoc, rPos, rFormula, eGrammar); + + // 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(), new ScFormulaCell(&mpImpl->mrDoc, rPos, rFormula, eGrammar)); + rCells.set(pBlockPos->miCellPos, rPos.Row(), pFC.release()); } void ScDocumentImport::setFormulaCell(const ScAddress& rPos, ScTokenArray* pArray) diff --git a/sc/source/filter/inc/orcusinterface.hxx b/sc/source/filter/inc/orcusinterface.hxx index 851271f9806a..07c35fa78dfe 100644 --- a/sc/source/filter/inc/orcusinterface.hxx +++ b/sc/source/filter/inc/orcusinterface.hxx @@ -273,6 +273,8 @@ public: virtual orcus::spreadsheet::range_size_t get_sheet_size() const override; SCTAB getIndex() const { return mnTab; } + + const sc::SharedFormulaGroups& getSharedFormulaGroups() const; }; class ScOrcusStyles : public orcus::spreadsheet::iface::import_styles @@ -508,28 +510,51 @@ public: class ScOrcusFactory : public orcus::spreadsheet::iface::import_factory { - struct StringCellCache + struct CellStoreToken { + enum class Type + { + Auto, + Numeric, + String, + Formula, + FormulaWithResult, + SharedFormula, + SharedFormulaWithResult, + Matrix + }; + ScAddress maPos; - size_t mnIndex; + Type meType; - StringCellCache(const ScAddress& rPos, size_t nIndex); + OUString maStr1; + OUString maStr2; + double mfValue; + + uint32_t mnIndex1; + uint32_t mnIndex2; + formula::FormulaGrammar::Grammar meGrammar; + + CellStoreToken( const ScAddress& rPos, Type eType ); + CellStoreToken( const ScAddress& rPos, double fValue ); + CellStoreToken( const ScAddress& rPos, uint32_t nIndex ); + CellStoreToken( const ScAddress& rPos, const OUString& rFormula, formula::FormulaGrammar::Grammar eGrammar ); }; typedef std::unordered_map<OUString, size_t> StringHashType; - typedef std::vector<StringCellCache> StringCellCaches; + typedef std::vector<CellStoreToken> CellStoreTokensType; ScDocumentImport maDoc; std::vector<OUString> maStrings; StringHashType maStringHash; - StringCellCaches maStringCells; + CellStoreTokensType maCellStoreTokens; ScOrcusGlobalSettings maGlobalSettings; ScOrcusRefResolver maRefResolver; ScOrcusSharedStrings maSharedStrings; ScOrcusNamedExpression maNamedExpressions; - std::vector< std::unique_ptr<ScOrcusSheet> > maSheets; + std::vector<std::unique_ptr<ScOrcusSheet>> maSheets; ScOrcusStyles maStyles; int mnProgress; @@ -552,7 +577,19 @@ public: size_t appendString(const OUString& rStr); size_t addString(const OUString& rStr); - void pushStringCell(const ScAddress& rPos, size_t nStrIndex); + void pushCellStoreAutoToken( const ScAddress& rPos, const OUString& rVal ); + void pushCellStoreToken( const ScAddress& rPos, uint32_t nStrIndex ); + void pushCellStoreToken( const ScAddress& rPos, double fValue ); + void pushCellStoreToken( + const ScAddress& rPos, const OUString& rFormula, formula::FormulaGrammar::Grammar eGrammar ); + + void pushSharedFormulaToken( const ScAddress& rPos, uint32_t nIndex ); + void pushMatrixFormulaToken( + const ScAddress& rPos, const OUString& rFormula, formula::FormulaGrammar::Grammar eGrammar, + uint32_t nRowRange, uint32_t nColRange ); + + void pushFormulaResult( const ScAddress& rPos, double fValue ); + void pushFormulaResult( const ScAddress& rPos, const OUString& rValue ); void incrementProgress(); diff --git a/sc/source/filter/orcus/interface.cxx b/sc/source/filter/orcus/interface.cxx index c2fa116d0617..04581dc70d72 100644 --- a/sc/source/filter/orcus/interface.cxx +++ b/sc/source/filter/orcus/interface.cxx @@ -177,8 +177,27 @@ void ScOrcusNamedExpression::define_name(const char* p_name, size_t n_name, cons pNames->insert(pRange, false); } -ScOrcusFactory::StringCellCache::StringCellCache(const ScAddress& rPos, size_t nIndex) : - maPos(rPos), mnIndex(nIndex) {} +ScOrcusFactory::CellStoreToken::CellStoreToken( const ScAddress& rPos, Type eType ) : + maPos(rPos), meType(eType) +{ + rtl::math::setNan(&mfValue); +} + +ScOrcusFactory::CellStoreToken::CellStoreToken( const ScAddress& rPos, double fValue ) : + maPos(rPos), meType(Type::Numeric), mfValue(fValue) {} + +ScOrcusFactory::CellStoreToken::CellStoreToken( const ScAddress& rPos, uint32_t nIndex ) : + maPos(rPos), meType(Type::String), mnIndex1(nIndex) +{ + rtl::math::setNan(&mfValue); +} + +ScOrcusFactory::CellStoreToken::CellStoreToken( + const ScAddress& rPos, const OUString& rFormula, formula::FormulaGrammar::Grammar eGrammar ) : + maPos(rPos), meType(Type::Formula), maStr1(rFormula), meGrammar(eGrammar) +{ + rtl::math::setNan(&mfValue); +} ScOrcusFactory::ScOrcusFactory(ScDocument& rDoc) : maDoc(rDoc), @@ -281,16 +300,112 @@ orcus::spreadsheet::iface::import_styles* ScOrcusFactory::get_styles() void ScOrcusFactory::finalize() { + auto toFormulaCell = [this]( const CellStoreToken& rToken ) -> std::unique_ptr<ScFormulaCell> + { + const ScOrcusSheet& rSheet = *maSheets.at(rToken.maPos.Tab()); + const sc::SharedFormulaGroups& rSFG = rSheet.getSharedFormulaGroups(); + const ScTokenArray* pArray = rSFG.get(rToken.mnIndex1); + if (!pArray) + return std::unique_ptr<ScFormulaCell>(); + + return o3tl::make_unique<ScFormulaCell>(&maDoc.getDoc(), rToken.maPos, *pArray); + }; + int nCellCount = 0; - StringCellCaches::const_iterator it = maStringCells.begin(), itEnd = maStringCells.end(); - for (; it != itEnd; ++it) + + for (const CellStoreToken& rToken : maCellStoreTokens) { - if (it->mnIndex >= maStrings.size()) - // String index out-of-bound! Something is up. - continue; + switch (rToken.meType) + { + case CellStoreToken::Type::Auto: + { + maDoc.setAutoInput(rToken.maPos, rToken.maStr1); + ++nCellCount; + break; + } + case CellStoreToken::Type::String: + { + if (rToken.mnIndex1 >= maStrings.size()) + // String index out-of-bound! Something is up. + break; + + maDoc.setStringCell(rToken.maPos, maStrings[rToken.mnIndex1]); + ++nCellCount; + break; + } + case CellStoreToken::Type::Numeric: + { + maDoc.setNumericCell(rToken.maPos, rToken.mfValue); + ++nCellCount; + break; + } + case CellStoreToken::Type::Formula: + { + maDoc.setFormulaCell( + rToken.maPos, rToken.maStr1, rToken.meGrammar); + + ++nCellCount; + break; + } + case CellStoreToken::Type::FormulaWithResult: + { + if (rtl::math::isFinite(rToken.mfValue)) + maDoc.setFormulaCell(rToken.maPos, rToken.maStr1, rToken.meGrammar, &rToken.mfValue); + else + maDoc.setFormulaCell(rToken.maPos, rToken.maStr1, rToken.meGrammar, rToken.maStr2); + + ++nCellCount; + break; + } + case CellStoreToken::Type::SharedFormula: + { + std::unique_ptr<ScFormulaCell> pCell = toFormulaCell(rToken); + if (!pCell) + break; + + maDoc.setFormulaCell(rToken.maPos, pCell.release()); + + ++nCellCount; + break; + } + case CellStoreToken::Type::SharedFormulaWithResult: + { + std::unique_ptr<ScFormulaCell> pCell = toFormulaCell(rToken); + if (!pCell) + break; + + if (rtl::math::isFinite(rToken.mfValue)) + pCell->SetResultDouble(rToken.mfValue); + else + pCell->SetHybridString( + maDoc.getDoc().GetSharedStringPool().intern(rToken.maStr2)); + + maDoc.setFormulaCell(rToken.maPos, pCell.release()); + + ++nCellCount; + break; + } + case CellStoreToken::Type::Matrix: + { + if (!rToken.mnIndex1 || !rToken.mnIndex2) + break; + + ScRange aRange(rToken.maPos); + aRange.aEnd.IncCol(rToken.mnIndex1-1); + aRange.aEnd.IncRow(rToken.mnIndex2-1); + + ScCompiler aComp(&maDoc.getDoc(), aRange.aStart, rToken.meGrammar); + std::unique_ptr<ScTokenArray> pArray(aComp.CompileString(rToken.maStr1)); + if (!pArray) + break; + + maDoc.setMatrixCells(aRange, *pArray, rToken.meGrammar); + break; + } + default: + ; + } - maDoc.setStringCell(it->maPos, maStrings[it->mnIndex]); - ++nCellCount; if (nCellCount == 100000) { incrementProgress(); @@ -323,9 +438,96 @@ size_t ScOrcusFactory::addString(const OUString& rStr) return appendString(rStr); } -void ScOrcusFactory::pushStringCell(const ScAddress& rPos, size_t nStrIndex) +void ScOrcusFactory::pushCellStoreAutoToken( const ScAddress& rPos, const OUString& rVal ) +{ + maCellStoreTokens.emplace_back(rPos, CellStoreToken::Type::Auto); + maCellStoreTokens.back().maStr1 = rVal; +} + +void ScOrcusFactory::pushCellStoreToken( const ScAddress& rPos, uint32_t nStrIndex ) +{ + maCellStoreTokens.emplace_back(rPos, nStrIndex); +} + +void ScOrcusFactory::pushCellStoreToken( const ScAddress& rPos, double fValue ) +{ + maCellStoreTokens.emplace_back(rPos, fValue); +} + +void ScOrcusFactory::pushCellStoreToken( + const ScAddress& rPos, const OUString& rFormula, formula::FormulaGrammar::Grammar eGrammar ) { - maStringCells.emplace_back(rPos, nStrIndex); + maCellStoreTokens.emplace_back(rPos, rFormula, eGrammar); +} + +void ScOrcusFactory::pushSharedFormulaToken( const ScAddress& rPos, uint32_t nIndex ) +{ + maCellStoreTokens.emplace_back(rPos, CellStoreToken::Type::SharedFormula); + maCellStoreTokens.back().mnIndex1 = nIndex; +} + +void ScOrcusFactory::pushMatrixFormulaToken( + const ScAddress& rPos, const OUString& rFormula, formula::FormulaGrammar::Grammar eGrammar, + uint32_t nRowRange, uint32_t nColRange ) +{ + maCellStoreTokens.emplace_back(rPos, CellStoreToken::Type::Matrix); + CellStoreToken& rT = maCellStoreTokens.back(); + rT.maStr1 = rFormula; + rT.meGrammar = eGrammar; + rT.mnIndex1 = nColRange; + rT.mnIndex2 = nRowRange; +} + +void ScOrcusFactory::pushFormulaResult( const ScAddress& rPos, double fValue ) +{ + // Formula result is expected to be pushed immediately following the + // formula token it belongs. + if (maCellStoreTokens.empty()) + return; + + CellStoreToken& rToken = maCellStoreTokens.back(); + if (rToken.maPos != rPos) + return; + + switch (rToken.meType) + { + case CellStoreToken::Type::Formula: + rToken.meType = CellStoreToken::Type::FormulaWithResult; + break; + case CellStoreToken::Type::SharedFormula: + rToken.meType = CellStoreToken::Type::SharedFormulaWithResult; + break; + default: + return; + } + + rToken.mfValue = fValue; +} + +void ScOrcusFactory::pushFormulaResult( const ScAddress& rPos, const OUString& rValue ) +{ + // Formula result is expected to be pushed immediately following the + // formula token it belongs. + if (maCellStoreTokens.empty()) + return; + + CellStoreToken& rToken = maCellStoreTokens.back(); + if (rToken.maPos != rPos) + return; + + switch (rToken.meType) + { + case CellStoreToken::Type::Formula: + rToken.meType = CellStoreToken::Type::FormulaWithResult; + break; + case CellStoreToken::Type::SharedFormula: + rToken.meType = CellStoreToken::Type::SharedFormulaWithResult; + break; + default: + return; + } + + rToken.maStr2 = rValue; } void ScOrcusFactory::incrementProgress() @@ -620,30 +822,25 @@ os::iface::import_conditional_format* ScOrcusSheet::get_conditional_format() void ScOrcusSheet::set_auto(os::row_t row, os::col_t col, const char* p, size_t n) { OUString aVal(p, n, RTL_TEXTENCODING_UTF8); - mrDoc.setAutoInput(ScAddress(col, row, mnTab), aVal); + mrFactory.pushCellStoreAutoToken(ScAddress(col, row, mnTab), aVal); cellInserted(); } void ScOrcusSheet::set_string(os::row_t row, os::col_t col, size_t sindex) { - // We need to defer string cells since the shared string pool is not yet - // populated at the time this method is called. Orcus imports string - // table after the cells get imported. We won't need to do this once we - // implement true shared strings in Calc core. - - mrFactory.pushStringCell(ScAddress(col, row, mnTab), sindex); + mrFactory.pushCellStoreToken(ScAddress(col, row, mnTab), uint32_t(sindex)); cellInserted(); } void ScOrcusSheet::set_value(os::row_t row, os::col_t col, double value) { - mrDoc.setNumericCell(ScAddress(col, row, mnTab), value); + mrFactory.pushCellStoreToken(ScAddress(col, row, mnTab), value); cellInserted(); } void ScOrcusSheet::set_bool(os::row_t row, os::col_t col, bool value) { - mrDoc.setNumericCell(ScAddress(col, row, mnTab), value ? 1.0 : 0.0); + mrFactory.pushCellStoreToken(ScAddress(col, row, mnTab), value ? 1.0 : 0.0); cellInserted(); } @@ -666,7 +863,7 @@ void ScOrcusSheet::set_date_time( fTime /= DATE_TIME_FACTOR; - mrDoc.setNumericCell(ScAddress(col, row, mnTab), nDateDiff + fTime); + mrFactory.pushCellStoreToken(ScAddress(col, row, mnTab), nDateDiff + fTime); cellInserted(); } @@ -692,35 +889,20 @@ void ScOrcusSheet::set_formula( os::row_t row, os::col_t col, os::formula_grammar_t grammar, const char* p, size_t n) { OUString aFormula(p, n, RTL_TEXTENCODING_UTF8); - formula::FormulaGrammar::Grammar eGrammar = getCalcGrammarFromOrcus( grammar ); - mrDoc.setFormulaCell(ScAddress(col,row,mnTab), aFormula, eGrammar); + mrFactory.pushCellStoreToken( + ScAddress(col, row, mnTab), aFormula, getCalcGrammarFromOrcus(grammar)); cellInserted(); } void ScOrcusSheet::set_formula_result(os::row_t row, os::col_t col, const char* p, size_t n) { - ScFormulaCell* pCell = mrDoc.getDoc().GetFormulaCell(ScAddress(col, row, mnTab)); - if (!pCell) - { - SAL_WARN("sc.orcus", "trying to set formula result for non formula " - "cell! Col: " << col << ";Row: " << row << ";Tab: " << mnTab); - return; - } OUString aResult( p, n, RTL_TEXTENCODING_UTF8); - pCell->SetHybridString(mrDoc.getDoc().GetSharedStringPool().intern(aResult)); + mrFactory.pushFormulaResult(ScAddress(col, row, mnTab), aResult); } -void ScOrcusSheet::set_formula_result(os::row_t row, os::col_t col, double /*val*/) +void ScOrcusSheet::set_formula_result(os::row_t row, os::col_t col, double val) { - ScFormulaCell* pCell = mrDoc.getDoc().GetFormulaCell(ScAddress(col, row, mnTab)); - if (!pCell) - { - SAL_WARN("sc.orcus", "trying to set formula result for non formula " - "cell! Col: " << col << ";Row: " << row << ";Tab: " << mnTab); - return; - } - - // TODO: FIXME + mrFactory.pushFormulaResult(ScAddress(col, row, mnTab), val); } void ScOrcusSheet::set_shared_formula( @@ -740,12 +922,8 @@ void ScOrcusSheet::set_shared_formula( maFormulaGroups.set(sindex, pArray); - ScFormulaCell* pCell = new ScFormulaCell(&mrDoc.getDoc(), aPos, *pArray); - mrDoc.setFormulaCell(aPos, pCell); + mrFactory.pushSharedFormulaToken(aPos, sindex); cellInserted(); - - // For now, orcus doesn't support setting cached result. Mark it for re-calculation. - pCell->SetDirty(); } void ScOrcusSheet::set_shared_formula( @@ -763,29 +941,19 @@ void ScOrcusSheet::set_shared_formula(os::row_t row, os::col_t col, size_t sinde if (!pArray) return; - ScFormulaCell* pCell = new ScFormulaCell(&mrDoc.getDoc(), aPos, *pArray); - mrDoc.setFormulaCell(aPos, pCell); + mrFactory.pushSharedFormulaToken(aPos, sindex); cellInserted(); - - // For now, orcus doesn't support setting cached result. Mark it for re-calculation. - pCell->SetDirty(); } void ScOrcusSheet::set_array_formula( os::row_t row, os::col_t col, os::formula_grammar_t grammar, const char* p, size_t n, os::row_t array_rows, os::col_t array_cols) { - formula::FormulaGrammar::Grammar eGrammar = getCalcGrammarFromOrcus( grammar ); OUString aFormula(p, n, RTL_TEXTENCODING_UTF8); + formula::FormulaGrammar::Grammar eGrammar = getCalcGrammarFromOrcus(grammar); - ScRange aRange(col, row, mnTab, col+array_cols, row + array_rows, mnTab); - - ScCompiler aComp(&mrDoc.getDoc(), aRange.aStart, eGrammar); - std::unique_ptr<ScTokenArray> pArray(aComp.CompileString(aFormula)); - if (!pArray) - return; - - mrDoc.setMatrixCells(aRange, *pArray, eGrammar); + ScAddress aPos(col, row, mnTab); + mrFactory.pushMatrixFormulaToken(aPos, aFormula, eGrammar, array_rows, array_cols); } void ScOrcusSheet::set_array_formula( @@ -803,6 +971,11 @@ orcus::spreadsheet::range_size_t ScOrcusSheet::get_sheet_size() const return ret; } +const sc::SharedFormulaGroups& ScOrcusSheet::getSharedFormulaGroups() const +{ + return maFormulaGroups; +} + ScOrcusSharedStrings::ScOrcusSharedStrings(ScOrcusFactory& rFactory) : mrFactory(rFactory) {} |