summaryrefslogtreecommitdiffstats
path: root/sc/source/core/data/dpsave.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sc/source/core/data/dpsave.cxx')
-rw-r--r--sc/source/core/data/dpsave.cxx1383
1 files changed, 1383 insertions, 0 deletions
diff --git a/sc/source/core/data/dpsave.cxx b/sc/source/core/data/dpsave.cxx
new file mode 100644
index 000000000..16de535bd
--- /dev/null
+++ b/sc/source/core/data/dpsave.cxx
@@ -0,0 +1,1383 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <dpsave.hxx>
+#include <dpdimsave.hxx>
+#include <miscuno.hxx>
+#include <unonames.hxx>
+#include <dputil.hxx>
+#include <generalfunction.hxx>
+#include <dptabdat.hxx>
+
+#include <sal/types.h>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <comphelper/stl_types.hxx>
+#include <unotools/charclass.hxx>
+
+#include <com/sun/star/sheet/XDimensionsSupplier.hpp>
+#include <com/sun/star/sheet/DataPilotFieldAutoShowInfo.hpp>
+#include <com/sun/star/sheet/DataPilotFieldLayoutInfo.hpp>
+#include <com/sun/star/sheet/DataPilotFieldReference.hpp>
+#include <com/sun/star/sheet/DataPilotFieldSortInfo.hpp>
+#include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
+#include <com/sun/star/sheet/XHierarchiesSupplier.hpp>
+#include <com/sun/star/sheet/XLevelsSupplier.hpp>
+#include <com/sun/star/sheet/XMembersSupplier.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/util/XCloneable.hpp>
+#include <tools/diagnose_ex.h>
+
+#include <unordered_map>
+#include <algorithm>
+
+using namespace com::sun::star;
+using namespace com::sun::star::sheet;
+using ::std::unique_ptr;
+
+#define SC_DPSAVEMODE_DONTKNOW 2
+
+static void lcl_SetBoolProperty( const uno::Reference<beans::XPropertySet>& xProp,
+ const OUString& rName, bool bValue )
+{
+ //TODO: move to ScUnoHelpFunctions?
+
+ xProp->setPropertyValue( rName, uno::Any( bValue ) );
+}
+
+ScDPSaveMember::ScDPSaveMember(const OUString& rName) :
+ aName( rName ),
+ nVisibleMode( SC_DPSAVEMODE_DONTKNOW ),
+ nShowDetailsMode( SC_DPSAVEMODE_DONTKNOW )
+{
+}
+
+ScDPSaveMember::ScDPSaveMember(const ScDPSaveMember& r) :
+ aName( r.aName ),
+ mpLayoutName( r.mpLayoutName ),
+ nVisibleMode( r.nVisibleMode ),
+ nShowDetailsMode( r.nShowDetailsMode )
+{
+}
+
+ScDPSaveMember::~ScDPSaveMember()
+{
+}
+
+bool ScDPSaveMember::operator== ( const ScDPSaveMember& r ) const
+{
+ return aName == r.aName &&
+ nVisibleMode == r.nVisibleMode &&
+ nShowDetailsMode == r.nShowDetailsMode;
+}
+
+bool ScDPSaveMember::HasIsVisible() const
+{
+ return nVisibleMode != SC_DPSAVEMODE_DONTKNOW;
+}
+
+void ScDPSaveMember::SetIsVisible(bool bSet)
+{
+ nVisibleMode = sal_uInt16(bSet);
+}
+
+bool ScDPSaveMember::HasShowDetails() const
+{
+ return nShowDetailsMode != SC_DPSAVEMODE_DONTKNOW;
+}
+
+void ScDPSaveMember::SetShowDetails(bool bSet)
+{
+ nShowDetailsMode = sal_uInt16(bSet);
+}
+
+void ScDPSaveMember::SetName( const OUString& rNew )
+{
+ // Used only if the source member was renamed (groups).
+ // For UI renaming of members, a layout name must be used.
+
+ aName = rNew;
+}
+
+void ScDPSaveMember::SetLayoutName( const OUString& rName )
+{
+ mpLayoutName = rName;
+}
+
+const std::optional<OUString> & ScDPSaveMember::GetLayoutName() const
+{
+ return mpLayoutName;
+}
+
+void ScDPSaveMember::RemoveLayoutName()
+{
+ mpLayoutName.reset();
+}
+
+void ScDPSaveMember::WriteToSource( const uno::Reference<uno::XInterface>& xMember, sal_Int32 nPosition )
+{
+ uno::Reference<beans::XPropertySet> xMembProp( xMember, uno::UNO_QUERY );
+ OSL_ENSURE( xMembProp.is(), "no properties at member" );
+ if ( !xMembProp.is() )
+ return;
+
+ // exceptions are caught at ScDPSaveData::WriteToSource
+
+ if ( nVisibleMode != SC_DPSAVEMODE_DONTKNOW )
+ lcl_SetBoolProperty( xMembProp,
+ SC_UNO_DP_ISVISIBLE, static_cast<bool>(nVisibleMode) );
+
+ if ( nShowDetailsMode != SC_DPSAVEMODE_DONTKNOW )
+ lcl_SetBoolProperty( xMembProp,
+ SC_UNO_DP_SHOWDETAILS, static_cast<bool>(nShowDetailsMode) );
+
+ if (mpLayoutName)
+ ScUnoHelpFunctions::SetOptionalPropertyValue(xMembProp, SC_UNO_DP_LAYOUTNAME, *mpLayoutName);
+
+ if ( nPosition >= 0 )
+ ScUnoHelpFunctions::SetOptionalPropertyValue(xMembProp, SC_UNO_DP_POSITION, nPosition);
+}
+
+#if DUMP_PIVOT_TABLE
+
+void ScDPSaveMember::Dump(int nIndent) const
+{
+ std::string aIndent(nIndent*4, ' ');
+ cout << aIndent << "* member name: '" << aName << "'" << endl;
+
+ cout << aIndent << " + layout name: ";
+ if (mpLayoutName)
+ cout << "'" << *mpLayoutName << "'";
+ else
+ cout << "(none)";
+ cout << endl;
+
+ cout << aIndent << " + visibility: ";
+ if (nVisibleMode == SC_DPSAVEMODE_DONTKNOW)
+ cout << "(unknown)";
+ else
+ cout << (nVisibleMode ? "visible" : "hidden");
+ cout << endl;
+}
+
+#endif
+
+ScDPSaveDimension::ScDPSaveDimension(const OUString& rName, bool bDataLayout) :
+ aName( rName ),
+ bIsDataLayout( bDataLayout ),
+ bDupFlag( false ),
+ nOrientation( sheet::DataPilotFieldOrientation_HIDDEN ),
+ nFunction( ScGeneralFunction::AUTO ),
+ nUsedHierarchy( -1 ),
+ nShowEmptyMode( SC_DPSAVEMODE_DONTKNOW ),
+ bRepeatItemLabels( false ),
+ bSubTotalDefault( true )
+{
+}
+
+ScDPSaveDimension::ScDPSaveDimension(const ScDPSaveDimension& r) :
+ aName( r.aName ),
+ mpLayoutName( r.mpLayoutName ),
+ mpSubtotalName( r.mpSubtotalName ),
+ bIsDataLayout( r.bIsDataLayout ),
+ bDupFlag( r.bDupFlag ),
+ nOrientation( r.nOrientation ),
+ nFunction( r.nFunction ),
+ nUsedHierarchy( r.nUsedHierarchy ),
+ nShowEmptyMode( r.nShowEmptyMode ),
+ bRepeatItemLabels( r.bRepeatItemLabels ),
+ bSubTotalDefault( r.bSubTotalDefault ),
+ maSubTotalFuncs( r.maSubTotalFuncs )
+{
+ for (const ScDPSaveMember* pMem : r.maMemberList)
+ {
+ const OUString& rName = pMem->GetName();
+ std::unique_ptr<ScDPSaveMember> pNew(new ScDPSaveMember( *pMem ));
+ maMemberList.push_back( pNew.get() );
+ maMemberHash[rName] = std::move(pNew);
+ }
+ if (r.pReferenceValue)
+ pReferenceValue.reset( new sheet::DataPilotFieldReference( *(r.pReferenceValue) ) );
+ if (r.pSortInfo)
+ pSortInfo.reset( new sheet::DataPilotFieldSortInfo( *(r.pSortInfo) ) );
+ if (r.pAutoShowInfo)
+ pAutoShowInfo.reset( new sheet::DataPilotFieldAutoShowInfo( *(r.pAutoShowInfo) ) );
+ if (r.pLayoutInfo)
+ pLayoutInfo.reset(new sheet::DataPilotFieldLayoutInfo( *(r.pLayoutInfo) ));
+}
+
+ScDPSaveDimension::~ScDPSaveDimension()
+{
+ maMemberHash.clear();
+ pReferenceValue.reset();
+ pSortInfo.reset();
+ pAutoShowInfo.reset();
+ pLayoutInfo.reset();
+}
+
+bool ScDPSaveDimension::operator== ( const ScDPSaveDimension& r ) const
+{
+ if ( aName != r.aName ||
+ bIsDataLayout != r.bIsDataLayout ||
+ bDupFlag != r.bDupFlag ||
+ nOrientation != r.nOrientation ||
+ nFunction != r.nFunction ||
+ nUsedHierarchy != r.nUsedHierarchy ||
+ nShowEmptyMode != r.nShowEmptyMode ||
+ bRepeatItemLabels!= r.bRepeatItemLabels||
+ bSubTotalDefault != r.bSubTotalDefault ||
+ maSubTotalFuncs != r.maSubTotalFuncs )
+ return false;
+
+ if (maMemberHash.size() != r.maMemberHash.size() )
+ return false;
+
+ if (!std::equal(maMemberList.begin(), maMemberList.end(), r.maMemberList.begin(), r.maMemberList.end(),
+ [](const ScDPSaveMember* a, const ScDPSaveMember* b) { return *a == *b; }))
+ return false;
+
+ if( pReferenceValue && r.pReferenceValue )
+ {
+ if ( *pReferenceValue != *r.pReferenceValue )
+ {
+ return false;
+ }
+ }
+ else if ( pReferenceValue || r.pReferenceValue )
+ {
+ return false;
+ }
+ if( this->pSortInfo && r.pSortInfo )
+ {
+ if ( *this->pSortInfo != *r.pSortInfo )
+ {
+ return false;
+ }
+ }
+ else if ( this->pSortInfo || r.pSortInfo )
+ {
+ return false;
+ }
+ if( this->pAutoShowInfo && r.pAutoShowInfo )
+ {
+ if ( *this->pAutoShowInfo != *r.pAutoShowInfo )
+ {
+ return false;
+ }
+ }
+ else if ( this->pAutoShowInfo || r.pAutoShowInfo )
+ {
+ return false;
+ }
+
+ return true;
+}
+
+void ScDPSaveDimension::AddMember(std::unique_ptr<ScDPSaveMember> pMember)
+{
+ const OUString & rName = pMember->GetName();
+ auto aExisting = maMemberHash.find( rName );
+ auto tmp = pMember.get();
+ if ( aExisting == maMemberHash.end() )
+ {
+ maMemberHash[rName] = std::move(pMember);
+ }
+ else
+ {
+ maMemberList.erase(std::remove(maMemberList.begin(), maMemberList.end(), aExisting->second.get()), maMemberList.end());
+ aExisting->second = std::move(pMember);
+ }
+ maMemberList.push_back( tmp );
+}
+
+void ScDPSaveDimension::SetName( const OUString& rNew )
+{
+ // Used only if the source dim was renamed (groups).
+ // For UI renaming of dimensions, the layout name must be used.
+
+ aName = rNew;
+}
+
+void ScDPSaveDimension::SetOrientation(css::sheet::DataPilotFieldOrientation nNew)
+{
+ nOrientation = nNew;
+}
+
+void ScDPSaveDimension::SetSubTotals(std::vector<ScGeneralFunction> && rFuncs)
+{
+ maSubTotalFuncs = std::move(rFuncs);
+ bSubTotalDefault = false;
+}
+
+bool ScDPSaveDimension::HasShowEmpty() const
+{
+ return nShowEmptyMode != SC_DPSAVEMODE_DONTKNOW;
+}
+
+void ScDPSaveDimension::SetShowEmpty(bool bSet)
+{
+ nShowEmptyMode = sal_uInt16(bSet);
+}
+
+void ScDPSaveDimension::SetRepeatItemLabels(bool bSet)
+{
+ bRepeatItemLabels = bSet;
+}
+
+void ScDPSaveDimension::SetFunction(ScGeneralFunction nNew)
+{
+ nFunction = nNew;
+}
+
+void ScDPSaveDimension::SetUsedHierarchy(tools::Long nNew)
+{
+ nUsedHierarchy = nNew;
+}
+
+void ScDPSaveDimension::SetSubtotalName(const OUString& rName)
+{
+ mpSubtotalName = rName;
+}
+
+const std::optional<OUString> & ScDPSaveDimension::GetSubtotalName() const
+{
+ return mpSubtotalName;
+}
+
+void ScDPSaveDimension::RemoveSubtotalName()
+{
+ mpSubtotalName.reset();
+}
+
+bool ScDPSaveDimension::IsMemberNameInUse(const OUString& rName) const
+{
+ return std::any_of(maMemberList.begin(), maMemberList.end(), [&rName](const ScDPSaveMember* pMem) {
+ if (rName.equalsIgnoreAsciiCase(pMem->GetName()))
+ return true;
+
+ const std::optional<OUString> & pLayoutName = pMem->GetLayoutName();
+ return pLayoutName && rName.equalsIgnoreAsciiCase(*pLayoutName);
+ });
+}
+
+void ScDPSaveDimension::SetLayoutName(const OUString& rName)
+{
+ mpLayoutName = rName;
+}
+
+const std::optional<OUString> & ScDPSaveDimension::GetLayoutName() const
+{
+ return mpLayoutName;
+}
+
+void ScDPSaveDimension::RemoveLayoutName()
+{
+ mpLayoutName.reset();
+}
+
+void ScDPSaveDimension::SetReferenceValue(const sheet::DataPilotFieldReference* pNew)
+{
+ if (pNew)
+ pReferenceValue.reset( new sheet::DataPilotFieldReference(*pNew) );
+ else
+ pReferenceValue.reset();
+}
+
+void ScDPSaveDimension::SetSortInfo(const sheet::DataPilotFieldSortInfo* pNew)
+{
+ if (pNew)
+ pSortInfo.reset( new sheet::DataPilotFieldSortInfo(*pNew) );
+ else
+ pSortInfo.reset();
+}
+
+void ScDPSaveDimension::SetAutoShowInfo(const sheet::DataPilotFieldAutoShowInfo* pNew)
+{
+ if (pNew)
+ pAutoShowInfo.reset( new sheet::DataPilotFieldAutoShowInfo(*pNew) );
+ else
+ pAutoShowInfo.reset();
+}
+
+void ScDPSaveDimension::SetLayoutInfo(const sheet::DataPilotFieldLayoutInfo* pNew)
+{
+ if (pNew)
+ pLayoutInfo.reset( new sheet::DataPilotFieldLayoutInfo(*pNew) );
+ else
+ pLayoutInfo.reset();
+}
+
+void ScDPSaveDimension::SetCurrentPage( const OUString* pPage )
+{
+ // We use member's visibility attribute to filter by page dimension.
+
+ // pPage == nullptr -> all members visible.
+ for (ScDPSaveMember* pMem : maMemberList)
+ {
+ bool bVisible = !pPage || pMem->GetName() == *pPage;
+ pMem->SetIsVisible(bVisible);
+ }
+}
+
+OUString ScDPSaveDimension::GetCurrentPage() const
+{
+ MemberList::const_iterator it = std::find_if(maMemberList.begin(), maMemberList.end(),
+ [](const ScDPSaveMember* pMem) { return pMem->GetIsVisible(); });
+ if (it != maMemberList.end())
+ return (*it)->GetName();
+
+ return OUString();
+}
+
+ScDPSaveMember* ScDPSaveDimension::GetExistingMemberByName(const OUString& rName)
+{
+ auto res = maMemberHash.find (rName);
+ if (res != maMemberHash.end())
+ return res->second.get();
+ return nullptr;
+}
+
+ScDPSaveMember* ScDPSaveDimension::GetMemberByName(const OUString& rName)
+{
+ auto res = maMemberHash.find (rName);
+ if (res != maMemberHash.end())
+ return res->second.get();
+
+ ScDPSaveMember* pNew = new ScDPSaveMember( rName );
+ maMemberHash[rName] = std::unique_ptr<ScDPSaveMember>(pNew);
+ maMemberList.push_back( pNew );
+ return pNew;
+}
+
+void ScDPSaveDimension::SetMemberPosition( const OUString& rName, sal_Int32 nNewPos )
+{
+ ScDPSaveMember* pMember = GetMemberByName( rName ); // make sure it exists and is in the hash
+
+ maMemberList.erase(std::remove( maMemberList.begin(), maMemberList.end(), pMember), maMemberList.end() );
+
+ maMemberList.insert( maMemberList.begin() + nNewPos, pMember );
+}
+
+void ScDPSaveDimension::WriteToSource( const uno::Reference<uno::XInterface>& xDim )
+{
+ uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
+ OSL_ENSURE( xDimProp.is(), "no properties at dimension" );
+ if ( xDimProp.is() )
+ {
+ // exceptions are caught at ScDPSaveData::WriteToSource
+
+ sheet::DataPilotFieldOrientation eOrient = nOrientation;
+ xDimProp->setPropertyValue( SC_UNO_DP_ORIENTATION, uno::Any(eOrient) );
+
+ sal_Int16 eFunc = static_cast<sal_Int16>(nFunction);
+ xDimProp->setPropertyValue( SC_UNO_DP_FUNCTION2, uno::Any(eFunc) );
+
+ if ( nUsedHierarchy >= 0 )
+ {
+ xDimProp->setPropertyValue( SC_UNO_DP_USEDHIERARCHY, uno::Any(static_cast<sal_Int32>(nUsedHierarchy)) );
+ }
+
+ if ( pReferenceValue )
+ {
+ ;
+ xDimProp->setPropertyValue( SC_UNO_DP_REFVALUE, uno::Any(*pReferenceValue) );
+ }
+
+ if (mpLayoutName)
+ ScUnoHelpFunctions::SetOptionalPropertyValue(xDimProp, SC_UNO_DP_LAYOUTNAME, *mpLayoutName);
+
+ const std::optional<OUString> & pSubTotalName = GetSubtotalName();
+ if (pSubTotalName)
+ // Custom subtotal name, with '?' being replaced by the visible field name later.
+ ScUnoHelpFunctions::SetOptionalPropertyValue(xDimProp, SC_UNO_DP_FIELD_SUBTOTALNAME, *pSubTotalName);
+ }
+
+ // Level loop outside of maMemberList loop
+ // because SubTotals have to be set independently of known members
+
+ tools::Long nCount = maMemberHash.size();
+
+ tools::Long nHierCount = 0;
+ uno::Reference<container::XIndexAccess> xHiers;
+ uno::Reference<sheet::XHierarchiesSupplier> xHierSupp( xDim, uno::UNO_QUERY );
+ if ( xHierSupp.is() )
+ {
+ uno::Reference<container::XNameAccess> xHiersName = xHierSupp->getHierarchies();
+ xHiers = new ScNameToIndexAccess( xHiersName );
+ nHierCount = xHiers->getCount();
+ }
+
+ bool bHasHiddenMember = false;
+
+ for (tools::Long nHier=0; nHier<nHierCount; nHier++)
+ {
+ tools::Long nLevCount = 0;
+ uno::Reference<container::XIndexAccess> xLevels;
+ uno::Reference<sheet::XLevelsSupplier> xLevSupp(xHiers->getByIndex(nHier), uno::UNO_QUERY);
+ if ( xLevSupp.is() )
+ {
+ uno::Reference<container::XNameAccess> xLevelsName = xLevSupp->getLevels();
+ xLevels = new ScNameToIndexAccess( xLevelsName );
+ nLevCount = xLevels->getCount();
+ }
+
+ for (tools::Long nLev=0; nLev<nLevCount; nLev++)
+ {
+ uno::Reference<uno::XInterface> xLevel(xLevels->getByIndex(nLev), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xLevProp( xLevel, uno::UNO_QUERY );
+ OSL_ENSURE( xLevProp.is(), "no properties at level" );
+ if ( xLevProp.is() )
+ {
+ if ( !bSubTotalDefault )
+ {
+ uno::Sequence<sal_Int16> aSeq(maSubTotalFuncs.size());
+ for(size_t i = 0; i < maSubTotalFuncs.size(); ++i)
+ aSeq.getArray()[i] = static_cast<sal_Int16>(maSubTotalFuncs[i]);
+ xLevProp->setPropertyValue( SC_UNO_DP_SUBTOTAL2, uno::Any(aSeq) );
+ }
+ if ( nShowEmptyMode != SC_DPSAVEMODE_DONTKNOW )
+ lcl_SetBoolProperty( xLevProp,
+ SC_UNO_DP_SHOWEMPTY, static_cast<bool>(nShowEmptyMode) );
+
+ lcl_SetBoolProperty( xLevProp,
+ SC_UNO_DP_REPEATITEMLABELS, bRepeatItemLabels );
+
+ if ( pSortInfo )
+ ScUnoHelpFunctions::SetOptionalPropertyValue(xLevProp, SC_UNO_DP_SORTING, *pSortInfo);
+
+ if ( pAutoShowInfo )
+ ScUnoHelpFunctions::SetOptionalPropertyValue(xLevProp, SC_UNO_DP_AUTOSHOW, *pAutoShowInfo);
+
+ if ( pLayoutInfo )
+ ScUnoHelpFunctions::SetOptionalPropertyValue(xLevProp, SC_UNO_DP_LAYOUT, *pLayoutInfo);
+
+ // exceptions are caught at ScDPSaveData::WriteToSource
+ }
+
+ if ( nCount > 0 )
+ {
+ uno::Reference<sheet::XMembersSupplier> xMembSupp( xLevel, uno::UNO_QUERY );
+ if ( xMembSupp.is() )
+ {
+ uno::Reference<sheet::XMembersAccess> xMembers = xMembSupp->getMembers();
+ if ( xMembers.is() )
+ {
+ sal_Int32 nPosition = -1; // set position only in manual mode
+ if ( !pSortInfo || pSortInfo->Mode == sheet::DataPilotFieldSortMode::MANUAL )
+ nPosition = 0;
+
+ for (ScDPSaveMember* pMember : maMemberList)
+ {
+ if (!pMember->GetIsVisible())
+ bHasHiddenMember = true;
+ OUString aMemberName = pMember->GetName();
+ if ( xMembers->hasByName( aMemberName ) )
+ {
+ uno::Reference<uno::XInterface> xMemberInt(
+ xMembers->getByName(aMemberName), uno::UNO_QUERY);
+ pMember->WriteToSource( xMemberInt, nPosition );
+
+ if ( nPosition >= 0 )
+ ++nPosition; // increase if initialized
+ }
+ // missing member is no error
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (xDimProp.is())
+ ScUnoHelpFunctions::SetOptionalPropertyValue(xDimProp, SC_UNO_DP_HAS_HIDDEN_MEMBER, bHasHiddenMember);
+}
+
+void ScDPSaveDimension::UpdateMemberVisibility(const std::unordered_map<OUString, bool>& rData)
+{
+ for (ScDPSaveMember* pMem : maMemberList)
+ {
+ const OUString& rMemName = pMem->GetName();
+ auto itr = rData.find(rMemName);
+ if (itr != rData.end())
+ pMem->SetIsVisible(itr->second);
+ }
+}
+
+bool ScDPSaveDimension::HasInvisibleMember() const
+{
+ return std::any_of(maMemberList.begin(), maMemberList.end(),
+ [](const ScDPSaveMember* pMem) { return !pMem->GetIsVisible(); });
+}
+
+void ScDPSaveDimension::RemoveObsoleteMembers(const MemberSetType& rMembers)
+{
+ MemberList aNew;
+ for (ScDPSaveMember* pMem : maMemberList)
+ {
+ if (rMembers.count(pMem->GetName()))
+ {
+ // This member still exists.
+ aNew.push_back(pMem);
+ }
+ else
+ {
+ maMemberHash.erase(pMem->GetName());
+ }
+ }
+
+ maMemberList.swap(aNew);
+}
+
+#if DUMP_PIVOT_TABLE
+
+void ScDPSaveDimension::Dump(int nIndent) const
+{
+ static const char* pOrientNames[] = { "hidden", "column", "row", "page", "data" };
+ std::string aIndent(nIndent*4, ' ');
+
+ cout << aIndent << "* dimension name: '" << aName << "'" << endl;
+
+ cout << aIndent << " + orientation: ";
+ if (nOrientation <= DataPilotFieldOrientation_DATA)
+ cout << pOrientNames[static_cast<int>(nOrientation)];
+ else
+ cout << "(invalid)";
+ cout << endl;
+
+ cout << aIndent << " + layout name: ";
+ if (mpLayoutName)
+ cout << "'" << *mpLayoutName << "'";
+ else
+ cout << "(none)";
+ cout << endl;
+
+ cout << aIndent << " + subtotal name: ";
+ if (mpSubtotalName)
+ cout << "'" << *mpSubtotalName << "'";
+ else
+ cout << "(none)";
+ cout << endl;
+
+ cout << aIndent << " + is data layout: " << (bIsDataLayout ? "yes" : "no") << endl;
+ cout << aIndent << " + is duplicate: " << (bDupFlag ? "yes" : "no") << endl;
+
+ for (ScDPSaveMember* pMem : maMemberList)
+ {
+ pMem->Dump(nIndent+1);
+ }
+
+ cout << endl; // blank line
+}
+
+#endif
+
+ScDPSaveData::ScDPSaveData() :
+ nColumnGrandMode( SC_DPSAVEMODE_DONTKNOW ),
+ nRowGrandMode( SC_DPSAVEMODE_DONTKNOW ),
+ nIgnoreEmptyMode( SC_DPSAVEMODE_DONTKNOW ),
+ nRepeatEmptyMode( SC_DPSAVEMODE_DONTKNOW ),
+ bFilterButton( true ),
+ bDrillDown( true ),
+ mbDimensionMembersBuilt(false)
+{
+}
+
+ScDPSaveData::ScDPSaveData(const ScDPSaveData& r) :
+ nColumnGrandMode( r.nColumnGrandMode ),
+ nRowGrandMode( r.nRowGrandMode ),
+ nIgnoreEmptyMode( r.nIgnoreEmptyMode ),
+ nRepeatEmptyMode( r.nRepeatEmptyMode ),
+ bFilterButton( r.bFilterButton ),
+ bDrillDown( r.bDrillDown ),
+ mbDimensionMembersBuilt(r.mbDimensionMembersBuilt),
+ mpGrandTotalName(r.mpGrandTotalName)
+{
+ if ( r.pDimensionData )
+ pDimensionData.reset( new ScDPDimensionSaveData( *r.pDimensionData ) );
+
+ for (auto const& it : r.m_DimList)
+ {
+ m_DimList.push_back(std::make_unique<ScDPSaveDimension>(*it));
+ }
+}
+
+ScDPSaveData& ScDPSaveData::operator= ( const ScDPSaveData& r )
+{
+ if ( &r != this )
+ {
+ this->~ScDPSaveData();
+ new( this ) ScDPSaveData ( r );
+ }
+ return *this;
+}
+
+bool ScDPSaveData::operator== ( const ScDPSaveData& r ) const
+{
+ if ( nColumnGrandMode != r.nColumnGrandMode ||
+ nRowGrandMode != r.nRowGrandMode ||
+ nIgnoreEmptyMode != r.nIgnoreEmptyMode ||
+ nRepeatEmptyMode != r.nRepeatEmptyMode ||
+ bFilterButton != r.bFilterButton ||
+ bDrillDown != r.bDrillDown ||
+ mbDimensionMembersBuilt != r.mbDimensionMembersBuilt)
+ return false;
+
+ if ( pDimensionData || r.pDimensionData )
+ if ( !pDimensionData || !r.pDimensionData || !( *pDimensionData == *r.pDimensionData ) )
+ return false;
+
+ if (!(::comphelper::ContainerUniquePtrEquals(m_DimList, r.m_DimList)))
+ return false;
+
+ if (mpGrandTotalName)
+ {
+ if (!r.mpGrandTotalName)
+ return false;
+ if (*mpGrandTotalName != *r.mpGrandTotalName)
+ return false;
+ }
+ else if (r.mpGrandTotalName)
+ return false;
+
+ return true;
+}
+
+ScDPSaveData::~ScDPSaveData()
+{
+}
+
+void ScDPSaveData::SetGrandTotalName(const OUString& rName)
+{
+ mpGrandTotalName = rName;
+}
+
+const std::optional<OUString> & ScDPSaveData::GetGrandTotalName() const
+{
+ return mpGrandTotalName;
+}
+
+namespace {
+
+class DimOrderInserter
+{
+ ScDPSaveData::DimOrderType& mrNames;
+public:
+ explicit DimOrderInserter(ScDPSaveData::DimOrderType& rNames) : mrNames(rNames) {}
+
+ void operator() (const ScDPSaveDimension* pDim)
+ {
+ size_t nRank = mrNames.size();
+ mrNames.emplace(ScGlobal::getCharClass().uppercase(pDim->GetName()), nRank);
+ }
+};
+
+}
+
+const ScDPSaveData::DimOrderType& ScDPSaveData::GetDimensionSortOrder() const
+{
+ if (!mpDimOrder)
+ {
+ mpDimOrder.reset(new DimOrderType);
+ std::vector<const ScDPSaveDimension*> aRowDims, aColDims;
+ GetAllDimensionsByOrientation(sheet::DataPilotFieldOrientation_ROW, aRowDims);
+ GetAllDimensionsByOrientation(sheet::DataPilotFieldOrientation_COLUMN, aColDims);
+
+ std::for_each(aRowDims.begin(), aRowDims.end(), DimOrderInserter(*mpDimOrder));
+ std::for_each(aColDims.begin(), aColDims.end(), DimOrderInserter(*mpDimOrder));
+ }
+ return *mpDimOrder;
+}
+
+void ScDPSaveData::GetAllDimensionsByOrientation(
+ sheet::DataPilotFieldOrientation eOrientation, std::vector<const ScDPSaveDimension*>& rDims) const
+{
+ std::vector<const ScDPSaveDimension*> aDims;
+ for (auto const& it : m_DimList)
+ {
+ const ScDPSaveDimension& rDim = *it;
+ if (rDim.GetOrientation() != eOrientation)
+ continue;
+
+ aDims.push_back(&rDim);
+ }
+
+ rDims.swap(aDims);
+}
+
+void ScDPSaveData::AddDimension(ScDPSaveDimension* pDim)
+{
+ if (!pDim)
+ return;
+
+ CheckDuplicateName(*pDim);
+ m_DimList.push_back(std::unique_ptr<ScDPSaveDimension>(pDim));
+
+ DimensionsChanged();
+}
+
+ScDPSaveDimension* ScDPSaveData::GetDimensionByName(const OUString& rName)
+{
+ for (auto const& iter : m_DimList)
+ {
+ if (iter->GetName() == rName && !iter->IsDataLayout() )
+ return &(*iter);
+ }
+
+ return AppendNewDimension(rName, false);
+}
+
+ScDPSaveDimension* ScDPSaveData::GetExistingDimensionByName(std::u16string_view rName) const
+{
+ for (auto const& iter : m_DimList)
+ {
+ if (iter->GetName() == rName && !iter->IsDataLayout() )
+ return &(*iter);
+ }
+ return nullptr; // don't create new
+}
+
+ScDPSaveDimension* ScDPSaveData::GetNewDimensionByName(const OUString& rName)
+{
+ for (auto const& iter : m_DimList)
+ {
+ if (iter->GetName() == rName && !iter->IsDataLayout() )
+ return DuplicateDimension(rName);
+ }
+
+ return AppendNewDimension(rName, false);
+}
+
+ScDPSaveDimension* ScDPSaveData::GetDataLayoutDimension()
+{
+ ScDPSaveDimension* pDim = GetExistingDataLayoutDimension();
+ if (pDim)
+ return pDim;
+
+ return AppendNewDimension(OUString(), true);
+}
+
+ScDPSaveDimension* ScDPSaveData::GetExistingDataLayoutDimension() const
+{
+ for (auto const& iter : m_DimList)
+ {
+ if ( iter->IsDataLayout() )
+ return &(*iter);
+ }
+ return nullptr;
+}
+
+ScDPSaveDimension* ScDPSaveData::DuplicateDimension(std::u16string_view rName)
+{
+ // always insert new
+
+ ScDPSaveDimension* pOld = GetExistingDimensionByName(rName);
+ if (!pOld)
+ return nullptr;
+
+ ScDPSaveDimension* pNew = new ScDPSaveDimension( *pOld );
+ AddDimension(pNew);
+ return pNew;
+}
+
+void ScDPSaveData::RemoveDimensionByName(const OUString& rName)
+{
+ auto iter = std::find_if(m_DimList.begin(), m_DimList.end(),
+ [&rName](const std::unique_ptr<ScDPSaveDimension>& rxDim) {
+ return rxDim->GetName() == rName && !rxDim->IsDataLayout(); });
+ if (iter != m_DimList.end())
+ {
+ m_DimList.erase(iter);
+ RemoveDuplicateNameCount(rName);
+ DimensionsChanged();
+ }
+}
+
+ScDPSaveDimension& ScDPSaveData::DuplicateDimension( const ScDPSaveDimension& rDim )
+{
+ ScDPSaveDimension* pNew = new ScDPSaveDimension( rDim );
+ AddDimension(pNew);
+ return *pNew;
+}
+
+ScDPSaveDimension* ScDPSaveData::GetInnermostDimension(DataPilotFieldOrientation nOrientation)
+{
+ // return the innermost dimension for the given orientation,
+ // excluding data layout dimension
+
+ auto iter = std::find_if(m_DimList.rbegin(), m_DimList.rend(),
+ [&nOrientation](const std::unique_ptr<ScDPSaveDimension>& rxDim) {
+ return rxDim->GetOrientation() == nOrientation && !rxDim->IsDataLayout(); });
+ if (iter != m_DimList.rend())
+ return iter->get();
+
+ return nullptr;
+}
+
+ScDPSaveDimension* ScDPSaveData::GetFirstDimension(sheet::DataPilotFieldOrientation eOrientation)
+{
+ for (auto const& iter : m_DimList)
+ {
+ if (iter->GetOrientation() == eOrientation && !iter->IsDataLayout())
+ return &(*iter);
+ }
+ return nullptr;
+}
+
+tools::Long ScDPSaveData::GetDataDimensionCount() const
+{
+ tools::Long nDataCount = 0;
+
+ for (auto const& iter : m_DimList)
+ {
+ if (iter->GetOrientation() == sheet::DataPilotFieldOrientation_DATA)
+ ++nDataCount;
+ }
+
+ return nDataCount;
+}
+
+void ScDPSaveData::SetPosition( ScDPSaveDimension* pDim, tools::Long nNew )
+{
+ // position (nNew) is counted within dimensions of the same orientation
+
+ DataPilotFieldOrientation nOrient = pDim->GetOrientation();
+
+ auto it = std::find_if(m_DimList.begin(), m_DimList.end(),
+ [&pDim](const std::unique_ptr<ScDPSaveDimension>& rxDim) { return pDim == rxDim.get(); });
+ if (it != m_DimList.end())
+ {
+ // Tell vector<unique_ptr> to give up ownership of this element.
+ // Don't delete this instance as it is re-inserted into the
+ // container later.
+ it->release();
+ m_DimList.erase(it);
+ }
+
+ auto iterInsert = std::find_if(m_DimList.begin(), m_DimList.end(),
+ [&nOrient, &nNew](const std::unique_ptr<ScDPSaveDimension>& rxDim) {
+ if (rxDim->GetOrientation() == nOrient )
+ --nNew;
+ return nNew <= 0;
+ });
+
+ m_DimList.insert(iterInsert, std::unique_ptr<ScDPSaveDimension>(pDim));
+ DimensionsChanged();
+}
+
+void ScDPSaveData::SetColumnGrand(bool bSet)
+{
+ nColumnGrandMode = sal_uInt16(bSet);
+}
+
+void ScDPSaveData::SetRowGrand(bool bSet)
+{
+ nRowGrandMode = sal_uInt16(bSet);
+}
+
+void ScDPSaveData::SetIgnoreEmptyRows(bool bSet)
+{
+ nIgnoreEmptyMode = sal_uInt16(bSet);
+}
+
+void ScDPSaveData::SetRepeatIfEmpty(bool bSet)
+{
+ nRepeatEmptyMode = sal_uInt16(bSet);
+}
+
+void ScDPSaveData::SetFilterButton(bool bSet)
+{
+ bFilterButton = bSet;
+}
+
+void ScDPSaveData::SetDrillDown(bool bSet)
+{
+ bDrillDown = bSet;
+}
+
+static void lcl_ResetOrient( const uno::Reference<sheet::XDimensionsSupplier>& xSource )
+{
+ uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions();
+ uno::Reference<container::XIndexAccess> xIntDims = new ScNameToIndexAccess( xDimsName );
+ tools::Long nIntCount = xIntDims->getCount();
+ for (tools::Long nIntDim=0; nIntDim<nIntCount; nIntDim++)
+ {
+ uno::Reference<beans::XPropertySet> xDimProp(xIntDims->getByIndex(nIntDim), uno::UNO_QUERY);
+ if (xDimProp.is())
+ {
+ xDimProp->setPropertyValue( SC_UNO_DP_ORIENTATION, uno::Any(sheet::DataPilotFieldOrientation_HIDDEN) );
+ }
+ }
+}
+
+void ScDPSaveData::WriteToSource( const uno::Reference<sheet::XDimensionsSupplier>& xSource )
+{
+ if (!xSource.is())
+ return;
+
+ // source options must be first!
+
+ uno::Reference<beans::XPropertySet> xSourceProp( xSource, uno::UNO_QUERY );
+ SAL_WARN_IF( !xSourceProp.is(), "sc.core", "no properties at source" );
+ if ( xSourceProp.is() )
+ {
+ // source options are not available for external sources
+ //TODO: use XPropertySetInfo to test for availability?
+
+ try
+ {
+ if ( nIgnoreEmptyMode != SC_DPSAVEMODE_DONTKNOW )
+ lcl_SetBoolProperty( xSourceProp,
+ SC_UNO_DP_IGNOREEMPTY, static_cast<bool>(nIgnoreEmptyMode) );
+ if ( nRepeatEmptyMode != SC_DPSAVEMODE_DONTKNOW )
+ lcl_SetBoolProperty( xSourceProp,
+ SC_UNO_DP_REPEATEMPTY, static_cast<bool>(nRepeatEmptyMode) );
+ }
+ catch(uno::Exception&)
+ {
+ // no error
+ }
+
+ const std::optional<OUString> & pGrandTotalName = GetGrandTotalName();
+ if (pGrandTotalName)
+ ScUnoHelpFunctions::SetOptionalPropertyValue(xSourceProp, SC_UNO_DP_GRANDTOTAL_NAME, *pGrandTotalName);
+ }
+
+ // exceptions in the other calls are errors
+ try
+ {
+ // reset all orientations
+ //TODO: "forgetSettings" or similar at source ?????
+ //TODO: reset all duplicated dimensions, or reuse them below !!!
+ SAL_INFO("sc.core", "ScDPSaveData::WriteToSource");
+
+ lcl_ResetOrient( xSource );
+
+ uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions();
+ uno::Reference<container::XIndexAccess> xIntDims = new ScNameToIndexAccess( xDimsName );
+ tools::Long nIntCount = xIntDims->getCount();
+
+ for (const auto& rxDim : m_DimList)
+ {
+ OUString aName = rxDim->GetName();
+ OUString aCoreName = ScDPUtil::getSourceDimensionName(aName);
+
+ SAL_INFO("sc.core", aName);
+
+ bool bData = rxDim->IsDataLayout();
+
+ //TODO: getByName for ScDPSource, including DataLayoutDimension !!!!!!!!
+
+ bool bFound = false;
+ for (tools::Long nIntDim=0; nIntDim<nIntCount && !bFound; nIntDim++)
+ {
+ uno::Reference<uno::XInterface> xIntDim(xIntDims->getByIndex(nIntDim),
+ uno::UNO_QUERY);
+ if ( bData )
+ {
+ uno::Reference<beans::XPropertySet> xDimProp( xIntDim, uno::UNO_QUERY );
+ if ( xDimProp.is() )
+ {
+ bFound = ScUnoHelpFunctions::GetBoolProperty( xDimProp,
+ SC_UNO_DP_ISDATALAYOUT );
+ //TODO: error checking -- is "IsDataLayoutDimension" property required??
+ }
+ }
+ else
+ {
+ uno::Reference<container::XNamed> xDimName( xIntDim, uno::UNO_QUERY );
+ if (xDimName.is() && xDimName->getName() == aCoreName)
+ bFound = true;
+ }
+
+ if (bFound)
+ {
+ if (rxDim->GetDupFlag())
+ {
+ uno::Reference<util::XCloneable> xCloneable(xIntDim, uno::UNO_QUERY);
+ SAL_WARN_IF(!xCloneable.is(), "sc.core", "cannot clone dimension");
+ if (xCloneable.is())
+ {
+ uno::Reference<util::XCloneable> xNew = xCloneable->createClone();
+ uno::Reference<container::XNamed> xNewName(xNew, uno::UNO_QUERY);
+ if (xNewName.is())
+ {
+ xNewName->setName(aName);
+ rxDim->WriteToSource(xNew);
+ }
+ }
+ }
+ else
+ rxDim->WriteToSource( xIntDim );
+ }
+ }
+ SAL_WARN_IF(!bFound, "sc.core", "WriteToSource: Dimension not found: " + aName + ".");
+ }
+
+ if ( xSourceProp.is() )
+ {
+ if ( nColumnGrandMode != SC_DPSAVEMODE_DONTKNOW )
+ lcl_SetBoolProperty( xSourceProp,
+ SC_UNO_DP_COLGRAND, static_cast<bool>(nColumnGrandMode) );
+ if ( nRowGrandMode != SC_DPSAVEMODE_DONTKNOW )
+ lcl_SetBoolProperty( xSourceProp,
+ SC_UNO_DP_ROWGRAND, static_cast<bool>(nRowGrandMode) );
+ }
+ }
+ catch(uno::Exception const &)
+ {
+ TOOLS_WARN_EXCEPTION("sc.core", "WriteToSource");
+ }
+}
+
+bool ScDPSaveData::IsEmpty() const
+{
+ for (auto const& iter : m_DimList)
+ {
+ if (iter->GetOrientation() != sheet::DataPilotFieldOrientation_HIDDEN && !iter->IsDataLayout())
+ return false;
+ }
+ return true; // no entries that are not hidden
+}
+
+void ScDPSaveData::RemoveAllGroupDimensions( const OUString& rSrcDimName, std::vector<OUString>* pDeletedNames )
+{
+ if (!pDimensionData)
+ // No group dimensions exist. Nothing to do.
+ return;
+
+ // Remove numeric group dimension (exists once at most). No need to delete
+ // anything in save data (grouping was done inplace in an existing base
+ // dimension).
+ pDimensionData->RemoveNumGroupDimension(rSrcDimName);
+
+ // Remove named group dimension(s). Dimensions have to be removed from
+ // dimension save data and from save data too.
+ const ScDPSaveGroupDimension* pExistingGroup = pDimensionData->GetGroupDimForBase(rSrcDimName);
+ while ( pExistingGroup )
+ {
+ OUString aGroupDimName = pExistingGroup->GetGroupDimName();
+ pDimensionData->RemoveGroupDimension(aGroupDimName); // pExistingGroup is deleted
+
+ // also remove SaveData settings for the dimension that no longer exists
+ RemoveDimensionByName(aGroupDimName);
+
+ if (pDeletedNames)
+ pDeletedNames->push_back(aGroupDimName);
+
+ // see if there are more group dimensions
+ pExistingGroup = pDimensionData->GetGroupDimForBase(rSrcDimName);
+
+ if ( pExistingGroup && pExistingGroup->GetGroupDimName() == aGroupDimName )
+ {
+ // still get the same group dimension?
+ OSL_FAIL("couldn't remove group dimension");
+ pExistingGroup = nullptr; // avoid endless loop
+ }
+ }
+}
+
+ScDPDimensionSaveData* ScDPSaveData::GetDimensionData()
+{
+ if (!pDimensionData)
+ pDimensionData.reset( new ScDPDimensionSaveData );
+ return pDimensionData.get();
+}
+
+void ScDPSaveData::SetDimensionData( const ScDPDimensionSaveData* pNew )
+{
+ if ( pNew )
+ pDimensionData.reset( new ScDPDimensionSaveData( *pNew ) );
+ else
+ pDimensionData.reset();
+}
+
+void ScDPSaveData::BuildAllDimensionMembers(ScDPTableData* pData)
+{
+ if (mbDimensionMembersBuilt)
+ return;
+
+ // First, build a dimension name-to-index map.
+ typedef std::unordered_map<OUString, tools::Long> NameIndexMap;
+ NameIndexMap aMap;
+ tools::Long nColCount = pData->GetColumnCount();
+ for (tools::Long i = 0; i < nColCount; ++i)
+ aMap.emplace(pData->getDimensionName(i), i);
+
+ NameIndexMap::const_iterator itrEnd = aMap.end();
+
+ for (auto const& iter : m_DimList)
+ {
+ const OUString& rDimName = iter->GetName();
+ if (rDimName.isEmpty())
+ // empty dimension name. It must be data layout.
+ continue;
+
+ NameIndexMap::const_iterator itr = aMap.find(rDimName);
+ if (itr == itrEnd)
+ // dimension name not in the data. This should never happen!
+ continue;
+
+ tools::Long nDimIndex = itr->second;
+ const std::vector<SCROW>& rMembers = pData->GetColumnEntries(nDimIndex);
+ size_t nMemberCount = rMembers.size();
+ for (size_t j = 0; j < nMemberCount; ++j)
+ {
+ const ScDPItemData* pMemberData = pData->GetMemberById( nDimIndex, rMembers[j] );
+ OUString aMemName = pData->GetFormattedString(nDimIndex, *pMemberData, false);
+ if (iter->GetExistingMemberByName(aMemName))
+ // this member instance already exists. nothing to do.
+ continue;
+
+ unique_ptr<ScDPSaveMember> pNewMember(new ScDPSaveMember(aMemName));
+ pNewMember->SetIsVisible(true);
+ iter->AddMember(std::move(pNewMember));
+ }
+ }
+
+ mbDimensionMembersBuilt = true;
+}
+
+void ScDPSaveData::SyncAllDimensionMembers(ScDPTableData* pData)
+{
+ typedef std::unordered_map<OUString, tools::Long> NameIndexMap;
+
+ // First, build a dimension name-to-index map.
+ NameIndexMap aMap;
+ tools::Long nColCount = pData->GetColumnCount();
+ for (tools::Long i = 0; i < nColCount; ++i)
+ aMap.emplace(pData->getDimensionName(i), i);
+
+ NameIndexMap::const_iterator itMapEnd = aMap.end();
+
+ for (auto const& it : m_DimList)
+ {
+ const OUString& rDimName = it->GetName();
+ if (rDimName.isEmpty())
+ // empty dimension name. It must be data layout.
+ continue;
+
+ NameIndexMap::const_iterator itMap = aMap.find(rDimName);
+ if (itMap == itMapEnd)
+ // dimension name not in the data. This should never happen!
+ continue;
+
+ ScDPSaveDimension::MemberSetType aMemNames;
+ tools::Long nDimIndex = itMap->second;
+ const std::vector<SCROW>& rMembers = pData->GetColumnEntries(nDimIndex);
+ size_t nMemberCount = rMembers.size();
+ for (size_t j = 0; j < nMemberCount; ++j)
+ {
+ const ScDPItemData* pMemberData = pData->GetMemberById(nDimIndex, rMembers[j]);
+ // ScDPCache::GetItemDataById() (via
+ // ScDPTableData::GetMemberById(),
+ // ScDPGroupTableData::GetMemberById() through
+ // GetCacheTable().getCache()) may return nullptr.
+ if (pMemberData)
+ {
+ OUString aMemName = pData->GetFormattedString(nDimIndex, *pMemberData, false);
+ aMemNames.insert(aMemName);
+ }
+ else
+ {
+ SAL_WARN("sc.core", "No pMemberData for nDimIndex " << nDimIndex << ", rMembers[j] " << rMembers[j]
+ << ", j " << j);
+ }
+ }
+
+ it->RemoveObsoleteMembers(aMemNames);
+ }
+}
+
+bool ScDPSaveData::HasInvisibleMember(std::u16string_view rDimName) const
+{
+ ScDPSaveDimension* pDim = GetExistingDimensionByName(rDimName);
+ if (!pDim)
+ return false;
+
+ return pDim->HasInvisibleMember();
+}
+
+#if DUMP_PIVOT_TABLE
+
+void ScDPSaveData::Dump() const
+{
+ for (auto const& itDim : m_DimList)
+ {
+ const ScDPSaveDimension& rDim = *itDim;
+ rDim.Dump();
+ }
+}
+
+#endif
+
+void ScDPSaveData::CheckDuplicateName(ScDPSaveDimension& rDim)
+{
+ const OUString aName = ScDPUtil::getSourceDimensionName(rDim.GetName());
+ DupNameCountType::iterator it = maDupNameCounts.find(aName);
+ if (it != maDupNameCounts.end())
+ {
+ rDim.SetName(ScDPUtil::createDuplicateDimensionName(aName, ++it->second));
+ rDim.SetDupFlag(true);
+ }
+ else
+ // New name.
+ maDupNameCounts.emplace(aName, 0);
+}
+
+void ScDPSaveData::RemoveDuplicateNameCount(const OUString& rName)
+{
+ OUString aCoreName = rName;
+ if (ScDPUtil::isDuplicateDimension(rName))
+ aCoreName = ScDPUtil::getSourceDimensionName(rName);
+
+ DupNameCountType::iterator it = maDupNameCounts.find(aCoreName);
+ if (it == maDupNameCounts.end())
+ return;
+
+ if (!it->second)
+ {
+ maDupNameCounts.erase(it);
+ return;
+ }
+
+ --it->second;
+}
+
+ScDPSaveDimension* ScDPSaveData::AppendNewDimension(const OUString& rName, bool bDataLayout)
+{
+ if (ScDPUtil::isDuplicateDimension(rName))
+ // This call is for original dimensions only.
+ return nullptr;
+
+ ScDPSaveDimension* pNew = new ScDPSaveDimension(rName, bDataLayout);
+ m_DimList.push_back(std::unique_ptr<ScDPSaveDimension>(pNew));
+ if (!maDupNameCounts.count(rName))
+ maDupNameCounts.emplace(rName, 0);
+
+ DimensionsChanged();
+ return pNew;
+}
+
+void ScDPSaveData::DimensionsChanged()
+{
+ mpDimOrder.reset();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */