sc/CppunitTest_sc_cond_format_merge.mk | 116 +++++++++++ sc/Module_sc.mk | 1 sc/inc/conditio.hxx | 3 sc/qa/extras/testdocuments/cond_format_merge.ods |binary sc/qa/unit/cond_format_merge.cxx | 155 +++++++++++++++ sc/source/core/data/conditio.cxx | 19 + sc/source/filter/xml/xmlcondformat.cxx | 227 ++++++++++++++++++++++- sc/source/filter/xml/xmlcondformat.hxx | 26 ++ 8 files changed, 541 insertions(+), 6 deletions(-)
New commits: commit 7540cdbc007c937f39db65bd5ed9280b0f759e32 Author: Tor Lillqvist <t...@collabora.com> Date: Sun Nov 26 23:28:05 2017 +0200 Deduplicate conditional formats loaded from .ods If there are several separate conditional format elements that can be represented as just one (with several ranges), try to do that. A particular customer document used to take 3 minutes 20 seconds to load, and it contained so many (tens of thousands) conditional formats that the Format> Conditional Formatting> Manage... dialog was practically impossible to use. Now loading that document takes 15 seconds and there are just a handful of separate conditional formats. Also add a simple unit test to verify the deduplication. Change-Id: I7c468af99956d4646ee5507390f1476caff52325 Reviewed-on: https://gerrit.libreoffice.org/45479 Reviewed-by: Thorsten Behrens <thorsten.behr...@cib.de> Tested-by: Thorsten Behrens <thorsten.behr...@cib.de> diff --git a/sc/CppunitTest_sc_cond_format_merge.mk b/sc/CppunitTest_sc_cond_format_merge.mk new file mode 100644 index 000000000000..bfb7dc2bba3f --- /dev/null +++ b/sc/CppunitTest_sc_cond_format_merge.mk @@ -0,0 +1,116 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# 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/. +# + +$(eval $(call gb_CppunitTest_CppunitTest,sc_cond_format_merge)) + +$(eval $(call gb_CppunitTest_add_exception_objects,sc_cond_format_merge, \ + sc/qa/unit/cond_format_merge \ +)) + +$(eval $(call gb_CppunitTest_use_externals,sc_cond_format_merge, \ + boost_headers \ +)) + +$(eval $(call gb_CppunitTest_use_libraries,sc_cond_format_merge, \ + basegfx \ + comphelper \ + cppu \ + cppuhelper \ + drawinglayer \ + editeng \ + for \ + forui \ + i18nlangtag \ + msfilter \ + oox \ + sal \ + salhelper \ + sax \ + sb \ + sc \ + scqahelper \ + sfx \ + sot \ + subsequenttest \ + svl \ + svt \ + svx \ + svxcore \ + test \ + tk \ + tl \ + ucbhelper \ + unotest \ + utl \ + vbahelper \ + vcl \ + xo \ +)) + +$(eval $(call gb_CppunitTest_set_include,sc_cond_format_merge,\ + -I$(SRCDIR)/sc/source/ui/inc \ + -I$(SRCDIR)/sc/inc \ + $$(INCLUDE) \ +)) + +$(eval $(call gb_CppunitTest_use_sdk_api,sc_cond_format_merge)) + +$(eval $(call gb_CppunitTest_use_ure,sc_cond_format_merge)) +$(eval $(call gb_CppunitTest_use_vcl,sc_cond_format_merge)) + +$(eval $(call gb_CppunitTest_use_components,sc_cond_format_merge,\ + basic/util/sb \ + chart2/source/chartcore \ + chart2/source/controller/chartcontroller \ + comphelper/util/comphelp \ + configmgr/source/configmgr \ + dbaccess/util/dba \ + embeddedobj/util/embobj \ + eventattacher/source/evtatt \ + filter/source/config/cache/filterconfig1 \ + filter/source/storagefilterdetect/storagefd \ + forms/util/frm \ + framework/util/fwk \ + i18npool/util/i18npool \ + oox/util/oox \ + package/source/xstor/xstor \ + package/util/package2 \ + sax/source/expatwrap/expwrap \ + scaddins/source/analysis/analysis \ + scaddins/source/datefunc/date \ + scripting/source/basprov/basprov \ + scripting/util/scriptframe \ + sc/util/sc \ + sc/util/scd \ + sc/util/scfilt \ + $(call gb_Helper_optional,SCRIPTING, \ + sc/util/vbaobj) \ + sfx2/util/sfx \ + sot/util/sot \ + svl/source/fsstor/fsstorage \ + svl/util/svl \ + svtools/util/svt \ + svx/util/svx \ + svx/util/svxcore \ + toolkit/util/tk \ + ucb/source/core/ucb1 \ + ucb/source/ucp/file/ucpfile1 \ + ucb/source/ucp/tdoc/ucptdoc1 \ + unotools/util/utl \ + unoxml/source/rdf/unordf \ + unoxml/source/service/unoxml \ + uui/util/uui \ + xmloff/util/xo \ +)) + +$(eval $(call gb_CppunitTest_use_configuration,sc_cond_format_merge)) + +$(eval $(call gb_CppunitTest_use_unittest_configuration,sc_cond_format_merge)) + +# vim: set noet sw=4 ts=4: diff --git a/sc/Module_sc.mk b/sc/Module_sc.mk index 7175f01a2658..26f967d7b267 100644 --- a/sc/Module_sc.mk +++ b/sc/Module_sc.mk @@ -52,6 +52,7 @@ $(eval $(call gb_Module_add_check_targets,sc,\ )) $(eval $(call gb_Module_add_slowcheck_targets,sc, \ + CppunitTest_sc_cond_format_merge \ CppunitTest_sc_condformats \ CppunitTest_sc_new_cond_format_api \ CppunitTest_sc_subsequent_filters_test \ diff --git a/sc/inc/conditio.hxx b/sc/inc/conditio.hxx index 104fe4293ebf..2f6b266f7468 100644 --- a/sc/inc/conditio.hxx +++ b/sc/inc/conditio.hxx @@ -233,6 +233,8 @@ public: bool operator== ( const ScConditionEntry& r ) const; + bool EqualIgnoringSrcPos( const ScConditionEntry& r ) const; + virtual void SetParent( ScConditionalFormat* pNew ) override; bool IsCellValid( ScRefCellValue& rCell, const ScAddress& rPos ) const; @@ -241,6 +243,7 @@ public: void SetOperation(ScConditionMode eMode); bool IsIgnoreBlank() const { return ( nOptions & SC_COND_NOBLANKS ) == 0; } void SetIgnoreBlank(bool bSet); + OUString GetSrcString() const { return aSrcString; } const ScAddress& GetSrcPos() const { return aSrcPos; } ScAddress GetValidSrcPos() const; // adjusted to allow textual representation of expressions diff --git a/sc/qa/extras/testdocuments/cond_format_merge.ods b/sc/qa/extras/testdocuments/cond_format_merge.ods new file mode 100644 index 000000000000..43b676d22080 Binary files /dev/null and b/sc/qa/extras/testdocuments/cond_format_merge.ods differ diff --git a/sc/qa/unit/cond_format_merge.cxx b/sc/qa/unit/cond_format_merge.cxx new file mode 100644 index 000000000000..0ce3f21909bd --- /dev/null +++ b/sc/qa/unit/cond_format_merge.cxx @@ -0,0 +1,155 @@ +/* -*- 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/. + */ + +#include <sal/config.h> + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/sheet/XConditionalFormats.hpp> +#include <com/sun/star/sheet/XSpreadsheet.hpp> +#include <test/bootstrapfixture.hxx> +#include <test/calc_unoapi_test.hxx> + +#include <global.hxx> +#include <document.hxx> + +#include "helper/qahelper.hxx" + +using namespace css; + +class ScCondFormatMergeTest : public CalcUnoApiTest +{ +public: + ScCondFormatMergeTest(); + + void testCondFormatMerge(); + + CPPUNIT_TEST_SUITE(ScCondFormatMergeTest); + CPPUNIT_TEST(testCondFormatMerge); + CPPUNIT_TEST_SUITE_END(); +}; + +ScCondFormatMergeTest::ScCondFormatMergeTest() + : CalcUnoApiTest("sc/qa/extras/testdocuments/") +{ +} + +void ScCondFormatMergeTest::testCondFormatMerge() +{ + OUString aFileURL; + createFileURL("cond_format_merge.ods", aFileURL); + uno::Reference<lang::XComponent> mxComponent = loadFromDesktop(aFileURL); + + CPPUNIT_ASSERT_MESSAGE("Component not loaded", mxComponent.is()); + + // get the first sheet + uno::Reference<sheet::XSpreadsheetDocument> xDoc(mxComponent, uno::UNO_QUERY_THROW); + uno::Reference<container::XIndexAccess> xIndex(xDoc->getSheets(), uno::UNO_QUERY_THROW); + uno::Reference<sheet::XSpreadsheet> xSheet(xIndex->getByIndex(0), uno::UNO_QUERY_THROW); + + uno::Reference<beans::XPropertySet> xProps(xSheet, uno::UNO_QUERY_THROW); + uno::Any aAny = xProps->getPropertyValue("ConditionalFormats"); + uno::Reference<sheet::XConditionalFormats> xCondFormats; + + CPPUNIT_ASSERT(aAny >>= xCondFormats); + CPPUNIT_ASSERT(xCondFormats.is()); + + CPPUNIT_ASSERT_EQUAL(sal_Int32(5), xCondFormats->getLength()); + + uno::Sequence<uno::Reference<sheet::XConditionalFormat>> xCondFormatSeq + = xCondFormats->getConditionalFormats(); + CPPUNIT_ASSERT_EQUAL(sal_Int32(5), xCondFormatSeq.getLength()); + + int nRanges = 0; + for (sal_Int32 i = 0, n = xCondFormatSeq.getLength(); i < n; ++i) + { + CPPUNIT_ASSERT(xCondFormatSeq[i].is()); + + uno::Reference<sheet::XConditionalFormat> xCondFormat = xCondFormatSeq[i]; + CPPUNIT_ASSERT(xCondFormat.is()); + + uno::Reference<beans::XPropertySet> xPropSet(xCondFormat, uno::UNO_QUERY_THROW); + + aAny = xPropSet->getPropertyValue("Range"); + uno::Reference<sheet::XSheetCellRanges> xCellRanges; + CPPUNIT_ASSERT(aAny >>= xCellRanges); + CPPUNIT_ASSERT(xCellRanges.is()); + + uno::Sequence<table::CellRangeAddress> aRanges = xCellRanges->getRangeAddresses(); + CPPUNIT_ASSERT_GREATEREQUAL(sal_Int32(1), aRanges.getLength()); + + table::CellRangeAddress aRange0 = aRanges[0]; + CPPUNIT_ASSERT_EQUAL(sal_Int16(0), aRange0.Sheet); + CPPUNIT_ASSERT_EQUAL(aRange0.StartColumn, aRange0.EndColumn); + + table::CellRangeAddress aRange1; + + switch (aRange0.StartColumn) + { + case 3: + switch (aRange0.StartRow) + { + case 0: // D1:D2,D5::D8 + nRanges++; + CPPUNIT_ASSERT_EQUAL(sal_Int32(1), aRange0.EndRow); + CPPUNIT_ASSERT_EQUAL(sal_Int32(2), aRanges.getLength()); + aRange1 = aRanges[1]; + CPPUNIT_ASSERT_EQUAL(sal_Int16(0), aRange1.Sheet); + CPPUNIT_ASSERT_EQUAL(aRange1.StartColumn, aRange1.EndColumn); + CPPUNIT_ASSERT_EQUAL(sal_Int32(3), aRange1.StartColumn); + CPPUNIT_ASSERT_EQUAL(sal_Int32(4), aRange1.StartRow); + CPPUNIT_ASSERT_EQUAL(sal_Int32(7), aRange1.EndRow); + break; + default: + CPPUNIT_FAIL("Unexpected range in column D"); + } + break; + case 5: + switch (aRange0.StartRow) + { + case 0: // F1:F2 + nRanges++; + CPPUNIT_ASSERT_EQUAL(sal_Int32(1), aRange0.EndRow); + CPPUNIT_ASSERT_EQUAL(sal_Int32(1), aRanges.getLength()); + break; + case 2: // F3 + nRanges++; + CPPUNIT_ASSERT_EQUAL(sal_Int32(2), aRange0.EndRow); + CPPUNIT_ASSERT_EQUAL(sal_Int32(1), aRanges.getLength()); + break; + case 3: // F4 + nRanges++; + CPPUNIT_ASSERT_EQUAL(sal_Int32(3), aRange0.EndRow); + CPPUNIT_ASSERT_EQUAL(sal_Int32(1), aRanges.getLength()); + break; + case 4: // F5 + nRanges++; + CPPUNIT_ASSERT_EQUAL(sal_Int32(4), aRange0.EndRow); + CPPUNIT_ASSERT_EQUAL(sal_Int32(1), aRanges.getLength()); + break; + default: + CPPUNIT_FAIL("Unexpected range in column F"); + } + break; + default: + CPPUNIT_FAIL("Unexpected range"); + } + } + + CPPUNIT_ASSERT_EQUAL(5, nRanges); + + closeDocument(mxComponent); + mxComponent.clear(); +} + +CPPUNIT_TEST_SUITE_REGISTRATION(ScCondFormatMergeTest); + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/core/data/conditio.cxx b/sc/source/core/data/conditio.cxx index 68fa98beb206..d3d996d93239 100644 --- a/sc/source/core/data/conditio.cxx +++ b/sc/source/core/data/conditio.cxx @@ -694,6 +694,25 @@ bool ScConditionEntry::operator== ( const ScConditionEntry& r ) const return bEq; } +bool ScConditionEntry::EqualIgnoringSrcPos( const ScConditionEntry& r ) const +{ + bool bEq = (eOp == r.eOp && nOptions == r.nOptions && + lcl_IsEqual( pFormula1, r.pFormula1 ) && + lcl_IsEqual( pFormula2, r.pFormula2 )); + if (bEq) + { + // Here, ignore the aSrcPoses and aSrcStrings + + // If not formulas, compare values + if ( !pFormula1 && ( nVal1 != r.nVal1 || aStrVal1 != r.aStrVal1 || bIsStr1 != r.bIsStr1 ) ) + bEq = false; + if ( !pFormula2 && ( nVal2 != r.nVal2 || aStrVal2 != r.aStrVal2 || bIsStr2 != r.bIsStr2 ) ) + bEq = false; + } + + return bEq; +} + void ScConditionEntry::Interpret( const ScAddress& rPos ) { // Create formula cells diff --git a/sc/source/filter/xml/xmlcondformat.cxx b/sc/source/filter/xml/xmlcondformat.cxx index 7e0e0c063ce1..f9704a1afb09 100644 --- a/sc/source/filter/xml/xmlcondformat.cxx +++ b/sc/source/filter/xml/xmlcondformat.cxx @@ -19,6 +19,7 @@ #include "docfunc.hxx" #include "XMLConverter.hxx" #include "stylehelper.hxx" +#include "tokenarray.hxx" ScXMLConditionalFormatsContext::ScXMLConditionalFormatsContext( ScXMLImport& rImport, sal_uInt16 nPrfx, const OUString& rLName): @@ -38,7 +39,7 @@ SvXMLImportContext* ScXMLConditionalFormatsContext::CreateChildContext( sal_uInt switch (nToken) { case XML_TOK_CONDFORMATS_CONDFORMAT: - pContext = new ScXMLConditionalFormatContext( GetScImport(), nPrefix, rLocalName, xAttrList ); + pContext = new ScXMLConditionalFormatContext( GetScImport(), nPrefix, rLocalName, xAttrList, *this ); break; } @@ -54,11 +55,18 @@ void ScXMLConditionalFormatsContext::EndElement() bool bDeleted = !pCondFormatList->CheckAllEntries(); SAL_WARN_IF(bDeleted, "sc", "conditional formats have been deleted because they contained empty range info"); + + for (const auto& i : mvCondFormatData) + { + pDoc->AddCondFormatData( i.mpFormat->GetRange(), i.mnTab, i.mpFormat->GetKey() ); + } } ScXMLConditionalFormatContext::ScXMLConditionalFormatContext( ScXMLImport& rImport, sal_uInt16 nPrfx, - const OUString& rLName, const css::uno::Reference< css::xml::sax::XAttributeList>& xAttrList): - SvXMLImportContext( rImport, nPrfx, rLName ) + const OUString& rLName, const css::uno::Reference< css::xml::sax::XAttributeList>& xAttrList, + ScXMLConditionalFormatsContext& rParent ): + SvXMLImportContext( rImport, nPrfx, rLName ), + mrParent( rParent ) { OUString sRange; @@ -120,16 +128,225 @@ SvXMLImportContext* ScXMLConditionalFormatContext::CreateChildContext( sal_uInt1 return pContext; } +static bool HasRelRefIgnoringSheet0Relative( ScDocument* pDoc, ScTokenArray* pTokens, sal_uInt16 nRecursion = 0 ) +{ + if (pTokens) + { + formula::FormulaToken* t; + for( t = pTokens->First(); t; t = pTokens->Next() ) + { + switch( t->GetType() ) + { + case formula::svDoubleRef: + { + ScSingleRefData& rRef2 = t->GetDoubleRef()->Ref2; + if ( rRef2.IsColRel() || rRef2.IsRowRel() || (rRef2.IsFlag3D() && rRef2.IsTabRel()) ) + return true; + SAL_FALLTHROUGH; + } + + case formula::svSingleRef: + { + ScSingleRefData& rRef1 = *t->GetSingleRef(); + if ( rRef1.IsColRel() || rRef1.IsRowRel() || (rRef1.IsFlag3D() && rRef1.IsTabRel()) ) + return true; + } + break; + + case formula::svIndex: + { + if( t->GetOpCode() == ocName ) // DB areas always absolute + if( ScRangeData* pRangeData = pDoc->FindRangeNameBySheetAndIndex( t->GetSheet(), t->GetIndex()) ) + if( (nRecursion < 42) && HasRelRefIgnoringSheet0Relative( pDoc, pRangeData->GetCode(), nRecursion + 1 ) ) + return true; + } + break; + + // #i34474# function result dependent on cell position + case formula::svByte: + { + switch( t->GetOpCode() ) + { + case ocRow: // ROW() returns own row index + case ocColumn: // COLUMN() returns own column index + case ocSheet: // SHEET() returns own sheet index + case ocCell: // CELL() may return own cell address + return true; + default: + break; + } + } + break; + + default: + break; + } + } + } + return false; +} + +static bool HasOneSingleFullyRelativeReference( ScTokenArray* pTokens, ScSingleRefData& rOffset ) +{ + int nCount = 0; + if (pTokens) + { + formula::FormulaToken* t; + for( t = pTokens->First(); t; t = pTokens->Next() ) + { + switch( t->GetType() ) + { + case formula::svSingleRef: + { + ScSingleRefData& rRef1 = *t->GetSingleRef(); + if ( rRef1.IsColRel() && rRef1.IsRowRel() && !rRef1.IsFlag3D() && rRef1.IsTabRel() ) + { + nCount++; + if (nCount == 1) + { + rOffset = rRef1; + } + } + } + break; + + default: + break; + } + } + } + return nCount == 1; +} + void ScXMLConditionalFormatContext::EndElement() { ScDocument* pDoc = GetScImport().GetDocument(); SCTAB nTab = GetScImport().GetTables().GetCurrentSheet(); ScConditionalFormat* pFormat = mxFormat.release(); + + bool bEligibleForCache = true; + bool bSingleRelativeReference = false; + ScSingleRefData aOffsetForSingleRelRef; + ScTokenArray* pTokens = nullptr; + for (size_t nFormatEntryIx = 0; nFormatEntryIx < pFormat->size(); ++nFormatEntryIx) + { + auto pFormatEntry = pFormat->GetEntry(nFormatEntryIx); + auto pCondFormatEntry = static_cast<const ScCondFormatEntry*>(pFormatEntry); + + if (pCondFormatEntry->GetOperation() != SC_COND_EQUAL && + pCondFormatEntry->GetOperation() != SC_COND_DIRECT) + { + bEligibleForCache = false; + break; + } + + ScAddress aSrcPos; + OUString aSrcString = pCondFormatEntry->GetSrcString(); + if ( !aSrcString.isEmpty() ) + aSrcPos.Parse( aSrcString, pDoc ); + ScCompiler aComp( pDoc, aSrcPos ); + aComp.SetGrammar( formula::FormulaGrammar::GRAM_ODFF ); + pTokens = aComp.CompileString( pCondFormatEntry->GetExpression(aSrcPos, 0), "" ); + if (HasRelRefIgnoringSheet0Relative( pDoc, pTokens )) + { + // In general not eligible, but some might be. We handle one very special case: When the + // conditional format has one entry, the reference position is the first cell of the + // range, and with a single fully relative reference in its expression. (Possibly these + // conditions could be loosened, but I am too tired to think on that right now.) + if (pFormat->size() == 1 && + pFormat->GetRange().size() == 1 && + pFormat->GetRange()[0]->aStart == aSrcPos && + HasOneSingleFullyRelativeReference( pTokens, aOffsetForSingleRelRef )) + { + bSingleRelativeReference = true; + } + else + { + bEligibleForCache = false; + break; + } + } + } + + if (bEligibleForCache) + { + for (auto& aCacheEntry : mrParent.maCache) + if (aCacheEntry.mnAge < SAL_MAX_INT64) + aCacheEntry.mnAge++; + + for (auto& aCacheEntry : mrParent.maCache) + { + if (!aCacheEntry.mpFormat) + continue; + + if (aCacheEntry.mpFormat->size() != pFormat->size()) + continue; + + // Check if the conditional format is identical to an existing one (but with different range) and can be shared + for (size_t nFormatEntryIx = 0; nFormatEntryIx < pFormat->size(); ++nFormatEntryIx) + { + auto pCacheFormatEntry = aCacheEntry.mpFormat->GetEntry(nFormatEntryIx); + auto pFormatEntry = pFormat->GetEntry(nFormatEntryIx); + if (pCacheFormatEntry->GetType() != pFormatEntry->GetType() || + pFormatEntry->GetType() != condformat::CONDITION) + break; + + auto pCacheCondFormatEntry = static_cast<const ScCondFormatEntry*>(pCacheFormatEntry); + auto pCondFormatEntry = static_cast<const ScCondFormatEntry*>(pFormatEntry); + + if (pCacheCondFormatEntry->GetStyle() != pCondFormatEntry->GetStyle()) + break; + + // Note That comparing the formulas of the ScConditionEntry at this stage is + // comparing just the *strings* of the formulas. For the bSingleRelativeReference + // case we compare the tokenized ("compiled") formulas. + if (bSingleRelativeReference) + { + if (aCacheEntry.mbSingleRelativeReference && + pTokens->EqualTokens(aCacheEntry.mpTokens.get())) + ; + else + break; + } + else if (!pCacheCondFormatEntry->EqualIgnoringSrcPos(*pCondFormatEntry)) + { + break; + } + // If we get here on the last round through the for loop, we have a cache hit + if (nFormatEntryIx == pFormat->size() - 1) + { + // Mark cache entry as fresh, do necessary mangling of it and just return + aCacheEntry.mnAge = 0; + for (size_t k = 0; k < pFormat->GetRange().size(); ++k) + aCacheEntry.mpFormat->GetRangeList().Join(*(pFormat->GetRange()[k])); + return; + } + } + } + + // Not found in cache, replace oldest cache entry + sal_Int64 nOldestAge = -1; + size_t nIndexOfOldest = 0; + for (auto& aCacheEntry : mrParent.maCache) + { + if (aCacheEntry.mnAge > nOldestAge) + { + nOldestAge = aCacheEntry.mnAge; + nIndexOfOldest = (&aCacheEntry - &mrParent.maCache.front()); + } + } + mrParent.maCache[nIndexOfOldest].mpFormat = pFormat; + mrParent.maCache[nIndexOfOldest].mbSingleRelativeReference = bSingleRelativeReference; + mrParent.maCache[nIndexOfOldest].mpTokens.reset(pTokens); + mrParent.maCache[nIndexOfOldest].mnAge = 0; + } + sal_uLong nIndex = pDoc->AddCondFormat(pFormat, nTab); - pFormat->SetKey(nIndex); + (void) nIndex; // Avoid 'unused variable' warning when assert() expands to empty + assert(pFormat->GetKey() == nIndex); - pDoc->AddCondFormatData( pFormat->GetRange(), nTab, nIndex); + mrParent.mvCondFormatData.push_back( { pFormat, nTab } ); } ScXMLConditionalFormatContext::~ScXMLConditionalFormatContext() diff --git a/sc/source/filter/xml/xmlcondformat.hxx b/sc/source/filter/xml/xmlcondformat.hxx index ca42c0b6eed5..7a94d6d7fe4a 100644 --- a/sc/source/filter/xml/xmlcondformat.hxx +++ b/sc/source/filter/xml/xmlcondformat.hxx @@ -10,9 +10,11 @@ #ifndef INCLUDED_SC_SOURCE_FILTER_XML_XMLCONDFORMAT_HXX #define INCLUDED_SC_SOURCE_FILTER_XML_XMLCONDFORMAT_HXX +#include <array> #include <xmloff/xmlictxt.hxx> #include "xmlimprt.hxx" #include "rangelst.hxx" +#include "tokenarray.hxx" class ScColorScaleFormat; class ScColorScaleEntry; @@ -23,6 +25,21 @@ struct ScIconSetFormatData; class ScXMLConditionalFormatsContext : public SvXMLImportContext { +private: + struct CacheEntry + { + ScConditionalFormat* mpFormat = nullptr; + bool mbSingleRelativeReference; + std::unique_ptr<const ScTokenArray> mpTokens; + sal_Int64 mnAge = SAL_MAX_INT64; + }; + + struct CondFormatData + { + ScConditionalFormat* mpFormat; + SCTAB mnTab; + }; + const ScXMLImport& GetScImport() const { return static_cast<const ScXMLImport&>(GetImport()); } ScXMLImport& GetScImport() { return static_cast<ScXMLImport&>(GetImport()); } public: @@ -36,6 +53,10 @@ public: const css::uno::Reference<css::xml::sax::XAttributeList>& xAttrList ) override; virtual void EndElement() override; + + std::array<CacheEntry, 4> maCache; + + std::vector<CondFormatData> mvCondFormatData; }; class ScXMLConditionalFormatContext : public SvXMLImportContext @@ -45,7 +66,8 @@ class ScXMLConditionalFormatContext : public SvXMLImportContext public: ScXMLConditionalFormatContext( ScXMLImport& rImport, sal_uInt16 nPrfx, const OUString& rLName, - const css::uno::Reference<css::xml::sax::XAttributeList>& xAttrList); + const css::uno::Reference<css::xml::sax::XAttributeList>& xAttrList, + ScXMLConditionalFormatsContext& rParent ); virtual ~ScXMLConditionalFormatContext(); @@ -58,6 +80,8 @@ private: std::unique_ptr<ScConditionalFormat> mxFormat; ScRangeList maRange; + + ScXMLConditionalFormatsContext& mrParent; }; class ScXMLColorScaleFormatContext : public SvXMLImportContext _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits