1
0
Fork 0
libreoffice/sc/source/core/data/dpsave.cxx
Daniel Baumann 8e63e14cf6
Adding upstream version 4:25.2.3.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-22 16:20:04 +02:00

1403 lines
43 KiB
C++

/* -*- 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 <pivot/PivotTableFormats.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 <comphelper/diagnose_ex.hxx>
#include <unordered_map>
#include <algorithm>
#include <utility>
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(OUString _aName) :
aName(std::move( _aName )),
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(OUString _aName, bool bDataLayout) :
aName(std::move( _aName )),
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
{
std::erase(maMemberList, aExisting->second.get());
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)
{
ScDPSaveMember* pResult = GetExistingMemberByName(rName);
if (pResult)
return pResult;
pResult = new ScDPSaveMember(rName);
maMemberHash[rName] = std::unique_ptr<ScDPSaveMember>(pResult);
maMemberList.push_back(pResult);
return pResult;
}
void ScDPSaveDimension::SetMemberPosition( const OUString& rName, sal_Int32 nNewPos )
{
ScDPSaveMember* pMember = GetMemberByName( rName ); // make sure it exists and is in the hash
std::erase(maMemberList, pMember);
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() )
{
xHiers = new ScNameToIndexAccess(xHierSupp->getHierarchies());
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() )
{
xLevels = new ScNameToIndexAccess(xLevSupp->getLevels());
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()
: mnColumnGrandMode(SC_DPSAVEMODE_DONTKNOW)
, mnRowGrandMode(SC_DPSAVEMODE_DONTKNOW)
, mnIgnoreEmptyMode(SC_DPSAVEMODE_DONTKNOW)
, mnRepeatEmptyMode(SC_DPSAVEMODE_DONTKNOW)
, mbFilterButton(true)
, mbDrillDown(true)
, mbExpandCollapse(false)
, mbDimensionMembersBuilt(false)
{
}
ScDPSaveData::ScDPSaveData(const ScDPSaveData& rOther)
: mnColumnGrandMode(rOther.mnColumnGrandMode)
, mnRowGrandMode(rOther.mnRowGrandMode)
, mnIgnoreEmptyMode(rOther.mnIgnoreEmptyMode)
, mnRepeatEmptyMode(rOther.mnRepeatEmptyMode)
, mbFilterButton(rOther.mbFilterButton)
, mbDrillDown(rOther.mbDrillDown)
, mbExpandCollapse(rOther.mbExpandCollapse)
, mbDimensionMembersBuilt(rOther.mbDimensionMembersBuilt)
, mpGrandTotalName(rOther.mpGrandTotalName)
{
if (rOther.mpDimensionData)
mpDimensionData.reset(new ScDPDimensionSaveData(*rOther.mpDimensionData));
if (rOther.mpFormats)
mpFormats.reset(new sc::PivotTableFormats(*rOther.mpFormats));
for (auto const& rOtherSaveDimension : rOther.m_DimList)
{
m_DimList.push_back(std::make_unique<ScDPSaveDimension>(*rOtherSaveDimension));
}
}
ScDPSaveData& ScDPSaveData::operator=(const ScDPSaveData& rOther)
{
if (&rOther != this)
{
this->~ScDPSaveData();
new(this)ScDPSaveData(rOther);
}
return *this;
}
bool ScDPSaveData::operator== (const ScDPSaveData& rOther) const
{
if (mnColumnGrandMode != rOther.mnColumnGrandMode ||
mnRowGrandMode != rOther.mnRowGrandMode ||
mnIgnoreEmptyMode != rOther.mnIgnoreEmptyMode ||
mnRepeatEmptyMode != rOther.mnRepeatEmptyMode ||
mbFilterButton != rOther.mbFilterButton ||
mbDrillDown != rOther.mbDrillDown ||
mbDimensionMembersBuilt != rOther.mbDimensionMembersBuilt)
return false;
if (mpDimensionData || rOther.mpDimensionData)
if (!mpDimensionData || !rOther.mpDimensionData || !(*mpDimensionData == *rOther.mpDimensionData))
return false;
if (!(::comphelper::ContainerUniquePtrEquals(m_DimList, rOther.m_DimList)))
return false;
if (mpGrandTotalName)
{
if (!rOther.mpGrandTotalName)
return false;
if (*mpGrandTotalName != *rOther.mpGrandTotalName)
return false;
}
else if (rOther.mpGrandTotalName)
return false;
return true;
}
ScDPSaveData::~ScDPSaveData()
{
}
void ScDPSaveData::setFormats(sc::PivotTableFormats const& rPivotTableFormats)
{
mpFormats.reset(new sc::PivotTableFormats(rPivotTableFormats));
}
bool ScDPSaveData::hasFormats()
{
return bool(mpFormats);
}
sc::PivotTableFormats const& ScDPSaveData::getFormats()
{
return *mpFormats;
}
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.
// coverity[leaked_storage] - 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)
{
mnColumnGrandMode = sal_uInt16(bSet);
}
void ScDPSaveData::SetRowGrand(bool bSet)
{
mnRowGrandMode = sal_uInt16(bSet);
}
void ScDPSaveData::SetIgnoreEmptyRows(bool bSet)
{
mnIgnoreEmptyMode = sal_uInt16(bSet);
}
void ScDPSaveData::SetRepeatIfEmpty(bool bSet)
{
mnRepeatEmptyMode = sal_uInt16(bSet);
}
void ScDPSaveData::SetFilterButton(bool bSet)
{
mbFilterButton = bSet;
}
void ScDPSaveData::SetDrillDown(bool bSet)
{
mbDrillDown = bSet;
}
void ScDPSaveData::SetExpandCollapse(bool bSet)
{
mbExpandCollapse = 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 (mnIgnoreEmptyMode != SC_DPSAVEMODE_DONTKNOW)
lcl_SetBoolProperty(xSourceProp, SC_UNO_DP_IGNOREEMPTY, bool(mnIgnoreEmptyMode));
if (mnRepeatEmptyMode != SC_DPSAVEMODE_DONTKNOW)
lcl_SetBoolProperty(xSourceProp, SC_UNO_DP_REPEATEMPTY, bool(mnRepeatEmptyMode));
}
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 (mnColumnGrandMode != SC_DPSAVEMODE_DONTKNOW)
lcl_SetBoolProperty(xSourceProp, SC_UNO_DP_COLGRAND, bool(mnColumnGrandMode));
if (mnRowGrandMode != SC_DPSAVEMODE_DONTKNOW)
lcl_SetBoolProperty(xSourceProp, SC_UNO_DP_ROWGRAND, bool(mnRowGrandMode));
}
}
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 (!mpDimensionData)
// 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).
mpDimensionData->RemoveNumGroupDimension(rSrcDimName);
// Remove named group dimension(s). Dimensions have to be removed from
// dimension save data and from save data too.
const ScDPSaveGroupDimension* pExistingGroup = mpDimensionData->GetGroupDimForBase(rSrcDimName);
while ( pExistingGroup )
{
OUString aGroupDimName = pExistingGroup->GetGroupDimName();
mpDimensionData->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 = mpDimensionData->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 (!mpDimensionData)
mpDimensionData.reset(new ScDPDimensionSaveData);
return mpDimensionData.get();
}
void ScDPSaveData::SetDimensionData(const ScDPDimensionSaveData* pNew)
{
if (pNew)
mpDimensionData.reset(new ScDPDimensionSaveData(*pNew));
else
mpDimensionData.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: */