1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
22 #include <dpdimsave.hxx>
23 #include <miscuno.hxx>
24 #include <unonames.hxx>
26 #include <generalfunction.hxx>
27 #include <dptabdat.hxx>
29 #include <sal/types.h>
30 #include <sal/log.hxx>
31 #include <osl/diagnose.h>
32 #include <comphelper/stl_types.hxx>
33 #include <unotools/charclass.hxx>
35 #include <com/sun/star/sheet/XDimensionsSupplier.hpp>
36 #include <com/sun/star/sheet/DataPilotFieldAutoShowInfo.hpp>
37 #include <com/sun/star/sheet/DataPilotFieldLayoutInfo.hpp>
38 #include <com/sun/star/sheet/DataPilotFieldReference.hpp>
39 #include <com/sun/star/sheet/DataPilotFieldSortInfo.hpp>
40 #include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
41 #include <com/sun/star/sheet/XHierarchiesSupplier.hpp>
42 #include <com/sun/star/sheet/XLevelsSupplier.hpp>
43 #include <com/sun/star/sheet/XMembersSupplier.hpp>
44 #include <com/sun/star/container/XNamed.hpp>
45 #include <com/sun/star/util/XCloneable.hpp>
46 #include <comphelper/diagnose_ex.hxx>
48 #include <unordered_map>
52 using namespace com::sun::star
;
53 using namespace com::sun::star::sheet
;
54 using ::std::unique_ptr
;
56 #define SC_DPSAVEMODE_DONTKNOW 2
58 static void lcl_SetBoolProperty( const uno::Reference
<beans::XPropertySet
>& xProp
,
59 const OUString
& rName
, bool bValue
)
61 //TODO: move to ScUnoHelpFunctions?
63 xProp
->setPropertyValue( rName
, uno::Any( bValue
) );
66 ScDPSaveMember::ScDPSaveMember(OUString _aName
) :
67 aName(std::move( _aName
)),
68 nVisibleMode( SC_DPSAVEMODE_DONTKNOW
),
69 nShowDetailsMode( SC_DPSAVEMODE_DONTKNOW
)
73 ScDPSaveMember::ScDPSaveMember(const ScDPSaveMember
& r
) :
75 mpLayoutName( r
.mpLayoutName
),
76 nVisibleMode( r
.nVisibleMode
),
77 nShowDetailsMode( r
.nShowDetailsMode
)
81 ScDPSaveMember::~ScDPSaveMember()
85 bool ScDPSaveMember::operator== ( const ScDPSaveMember
& r
) const
87 return aName
== r
.aName
&&
88 nVisibleMode
== r
.nVisibleMode
&&
89 nShowDetailsMode
== r
.nShowDetailsMode
;
92 bool ScDPSaveMember::HasIsVisible() const
94 return nVisibleMode
!= SC_DPSAVEMODE_DONTKNOW
;
97 void ScDPSaveMember::SetIsVisible(bool bSet
)
99 nVisibleMode
= sal_uInt16(bSet
);
102 bool ScDPSaveMember::HasShowDetails() const
104 return nShowDetailsMode
!= SC_DPSAVEMODE_DONTKNOW
;
107 void ScDPSaveMember::SetShowDetails(bool bSet
)
109 nShowDetailsMode
= sal_uInt16(bSet
);
112 void ScDPSaveMember::SetName( const OUString
& rNew
)
114 // Used only if the source member was renamed (groups).
115 // For UI renaming of members, a layout name must be used.
120 void ScDPSaveMember::SetLayoutName( const OUString
& rName
)
122 mpLayoutName
= rName
;
125 const std::optional
<OUString
> & ScDPSaveMember::GetLayoutName() const
130 void ScDPSaveMember::RemoveLayoutName()
132 mpLayoutName
.reset();
135 void ScDPSaveMember::WriteToSource( const uno::Reference
<uno::XInterface
>& xMember
, sal_Int32 nPosition
)
137 uno::Reference
<beans::XPropertySet
> xMembProp( xMember
, uno::UNO_QUERY
);
138 OSL_ENSURE( xMembProp
.is(), "no properties at member" );
139 if ( !xMembProp
.is() )
142 // exceptions are caught at ScDPSaveData::WriteToSource
144 if ( nVisibleMode
!= SC_DPSAVEMODE_DONTKNOW
)
145 lcl_SetBoolProperty( xMembProp
,
146 SC_UNO_DP_ISVISIBLE
, static_cast<bool>(nVisibleMode
) );
148 if ( nShowDetailsMode
!= SC_DPSAVEMODE_DONTKNOW
)
149 lcl_SetBoolProperty( xMembProp
,
150 SC_UNO_DP_SHOWDETAILS
, static_cast<bool>(nShowDetailsMode
) );
153 ScUnoHelpFunctions::SetOptionalPropertyValue(xMembProp
, SC_UNO_DP_LAYOUTNAME
, *mpLayoutName
);
155 if ( nPosition
>= 0 )
156 ScUnoHelpFunctions::SetOptionalPropertyValue(xMembProp
, SC_UNO_DP_POSITION
, nPosition
);
161 void ScDPSaveMember::Dump(int nIndent
) const
163 std::string
aIndent(nIndent
*4, ' ');
164 cout
<< aIndent
<< "* member name: '" << aName
<< "'" << endl
;
166 cout
<< aIndent
<< " + layout name: ";
168 cout
<< "'" << *mpLayoutName
<< "'";
173 cout
<< aIndent
<< " + visibility: ";
174 if (nVisibleMode
== SC_DPSAVEMODE_DONTKNOW
)
177 cout
<< (nVisibleMode
? "visible" : "hidden");
183 ScDPSaveDimension::ScDPSaveDimension(OUString _aName
, bool bDataLayout
) :
184 aName(std::move( _aName
)),
185 bIsDataLayout( bDataLayout
),
187 nOrientation( sheet::DataPilotFieldOrientation_HIDDEN
),
188 nFunction( ScGeneralFunction::AUTO
),
189 nUsedHierarchy( -1 ),
190 nShowEmptyMode( SC_DPSAVEMODE_DONTKNOW
),
191 bRepeatItemLabels( false ),
192 bSubTotalDefault( true )
196 ScDPSaveDimension::ScDPSaveDimension(const ScDPSaveDimension
& r
) :
198 mpLayoutName( r
.mpLayoutName
),
199 mpSubtotalName( r
.mpSubtotalName
),
200 bIsDataLayout( r
.bIsDataLayout
),
201 bDupFlag( r
.bDupFlag
),
202 nOrientation( r
.nOrientation
),
203 nFunction( r
.nFunction
),
204 nUsedHierarchy( r
.nUsedHierarchy
),
205 nShowEmptyMode( r
.nShowEmptyMode
),
206 bRepeatItemLabels( r
.bRepeatItemLabels
),
207 bSubTotalDefault( r
.bSubTotalDefault
),
208 maSubTotalFuncs( r
.maSubTotalFuncs
)
210 for (const ScDPSaveMember
* pMem
: r
.maMemberList
)
212 const OUString
& rName
= pMem
->GetName();
213 std::unique_ptr
<ScDPSaveMember
> pNew(new ScDPSaveMember( *pMem
));
214 maMemberList
.push_back( pNew
.get() );
215 maMemberHash
[rName
] = std::move(pNew
);
217 if (r
.pReferenceValue
)
218 pReferenceValue
.reset( new sheet::DataPilotFieldReference( *(r
.pReferenceValue
) ) );
220 pSortInfo
.reset( new sheet::DataPilotFieldSortInfo( *(r
.pSortInfo
) ) );
222 pAutoShowInfo
.reset( new sheet::DataPilotFieldAutoShowInfo( *(r
.pAutoShowInfo
) ) );
224 pLayoutInfo
.reset(new sheet::DataPilotFieldLayoutInfo( *(r
.pLayoutInfo
) ));
227 ScDPSaveDimension::~ScDPSaveDimension()
229 maMemberHash
.clear();
230 pReferenceValue
.reset();
232 pAutoShowInfo
.reset();
236 bool ScDPSaveDimension::operator== ( const ScDPSaveDimension
& r
) const
238 if ( aName
!= r
.aName
||
239 bIsDataLayout
!= r
.bIsDataLayout
||
240 bDupFlag
!= r
.bDupFlag
||
241 nOrientation
!= r
.nOrientation
||
242 nFunction
!= r
.nFunction
||
243 nUsedHierarchy
!= r
.nUsedHierarchy
||
244 nShowEmptyMode
!= r
.nShowEmptyMode
||
245 bRepeatItemLabels
!= r
.bRepeatItemLabels
||
246 bSubTotalDefault
!= r
.bSubTotalDefault
||
247 maSubTotalFuncs
!= r
.maSubTotalFuncs
)
250 if (maMemberHash
.size() != r
.maMemberHash
.size() )
253 if (!std::equal(maMemberList
.begin(), maMemberList
.end(), r
.maMemberList
.begin(), r
.maMemberList
.end(),
254 [](const ScDPSaveMember
* a
, const ScDPSaveMember
* b
) { return *a
== *b
; }))
257 if( pReferenceValue
&& r
.pReferenceValue
)
259 if ( *pReferenceValue
!= *r
.pReferenceValue
)
264 else if ( pReferenceValue
|| r
.pReferenceValue
)
268 if( this->pSortInfo
&& r
.pSortInfo
)
270 if ( *this->pSortInfo
!= *r
.pSortInfo
)
275 else if ( this->pSortInfo
|| r
.pSortInfo
)
279 if( this->pAutoShowInfo
&& r
.pAutoShowInfo
)
281 if ( *this->pAutoShowInfo
!= *r
.pAutoShowInfo
)
286 else if ( this->pAutoShowInfo
|| r
.pAutoShowInfo
)
294 void ScDPSaveDimension::AddMember(std::unique_ptr
<ScDPSaveMember
> pMember
)
296 const OUString
& rName
= pMember
->GetName();
297 auto aExisting
= maMemberHash
.find( rName
);
298 auto tmp
= pMember
.get();
299 if ( aExisting
== maMemberHash
.end() )
301 maMemberHash
[rName
] = std::move(pMember
);
305 std::erase(maMemberList
, aExisting
->second
.get());
306 aExisting
->second
= std::move(pMember
);
308 maMemberList
.push_back( tmp
);
311 void ScDPSaveDimension::SetName( const OUString
& rNew
)
313 // Used only if the source dim was renamed (groups).
314 // For UI renaming of dimensions, the layout name must be used.
319 void ScDPSaveDimension::SetOrientation(css::sheet::DataPilotFieldOrientation nNew
)
324 void ScDPSaveDimension::SetSubTotals(std::vector
<ScGeneralFunction
> && rFuncs
)
326 maSubTotalFuncs
= std::move(rFuncs
);
327 bSubTotalDefault
= false;
330 bool ScDPSaveDimension::HasShowEmpty() const
332 return nShowEmptyMode
!= SC_DPSAVEMODE_DONTKNOW
;
335 void ScDPSaveDimension::SetShowEmpty(bool bSet
)
337 nShowEmptyMode
= sal_uInt16(bSet
);
340 void ScDPSaveDimension::SetRepeatItemLabels(bool bSet
)
342 bRepeatItemLabels
= bSet
;
345 void ScDPSaveDimension::SetFunction(ScGeneralFunction nNew
)
350 void ScDPSaveDimension::SetUsedHierarchy(tools::Long nNew
)
352 nUsedHierarchy
= nNew
;
355 void ScDPSaveDimension::SetSubtotalName(const OUString
& rName
)
357 mpSubtotalName
= rName
;
360 const std::optional
<OUString
> & ScDPSaveDimension::GetSubtotalName() const
362 return mpSubtotalName
;
365 void ScDPSaveDimension::RemoveSubtotalName()
367 mpSubtotalName
.reset();
370 bool ScDPSaveDimension::IsMemberNameInUse(const OUString
& rName
) const
372 return std::any_of(maMemberList
.begin(), maMemberList
.end(), [&rName
](const ScDPSaveMember
* pMem
) {
373 if (rName
.equalsIgnoreAsciiCase(pMem
->GetName()))
376 const std::optional
<OUString
> & pLayoutName
= pMem
->GetLayoutName();
377 return pLayoutName
&& rName
.equalsIgnoreAsciiCase(*pLayoutName
);
381 void ScDPSaveDimension::SetLayoutName(const OUString
& rName
)
383 mpLayoutName
= rName
;
386 const std::optional
<OUString
> & ScDPSaveDimension::GetLayoutName() const
391 void ScDPSaveDimension::RemoveLayoutName()
393 mpLayoutName
.reset();
396 void ScDPSaveDimension::SetReferenceValue(const sheet::DataPilotFieldReference
* pNew
)
399 pReferenceValue
.reset( new sheet::DataPilotFieldReference(*pNew
) );
401 pReferenceValue
.reset();
404 void ScDPSaveDimension::SetSortInfo(const sheet::DataPilotFieldSortInfo
* pNew
)
407 pSortInfo
.reset( new sheet::DataPilotFieldSortInfo(*pNew
) );
412 void ScDPSaveDimension::SetAutoShowInfo(const sheet::DataPilotFieldAutoShowInfo
* pNew
)
415 pAutoShowInfo
.reset( new sheet::DataPilotFieldAutoShowInfo(*pNew
) );
417 pAutoShowInfo
.reset();
420 void ScDPSaveDimension::SetLayoutInfo(const sheet::DataPilotFieldLayoutInfo
* pNew
)
423 pLayoutInfo
.reset( new sheet::DataPilotFieldLayoutInfo(*pNew
) );
428 void ScDPSaveDimension::SetCurrentPage( const OUString
* pPage
)
430 // We use member's visibility attribute to filter by page dimension.
432 // pPage == nullptr -> all members visible.
433 for (ScDPSaveMember
* pMem
: maMemberList
)
435 bool bVisible
= !pPage
|| pMem
->GetName() == *pPage
;
436 pMem
->SetIsVisible(bVisible
);
440 OUString
ScDPSaveDimension::GetCurrentPage() const
442 MemberList::const_iterator it
= std::find_if(maMemberList
.begin(), maMemberList
.end(),
443 [](const ScDPSaveMember
* pMem
) { return pMem
->GetIsVisible(); });
444 if (it
!= maMemberList
.end())
445 return (*it
)->GetName();
450 ScDPSaveMember
* ScDPSaveDimension::GetExistingMemberByName(const OUString
& rName
)
452 auto res
= maMemberHash
.find (rName
);
453 if (res
!= maMemberHash
.end())
454 return res
->second
.get();
458 ScDPSaveMember
* ScDPSaveDimension::GetMemberByName(const OUString
& rName
)
460 auto res
= maMemberHash
.find (rName
);
461 if (res
!= maMemberHash
.end())
462 return res
->second
.get();
464 ScDPSaveMember
* pNew
= new ScDPSaveMember( rName
);
465 maMemberHash
[rName
] = std::unique_ptr
<ScDPSaveMember
>(pNew
);
466 maMemberList
.push_back( pNew
);
470 void ScDPSaveDimension::SetMemberPosition( const OUString
& rName
, sal_Int32 nNewPos
)
472 ScDPSaveMember
* pMember
= GetMemberByName( rName
); // make sure it exists and is in the hash
474 std::erase(maMemberList
, pMember
);
476 maMemberList
.insert( maMemberList
.begin() + nNewPos
, pMember
);
479 void ScDPSaveDimension::WriteToSource( const uno::Reference
<uno::XInterface
>& xDim
)
481 uno::Reference
<beans::XPropertySet
> xDimProp( xDim
, uno::UNO_QUERY
);
482 OSL_ENSURE( xDimProp
.is(), "no properties at dimension" );
485 // exceptions are caught at ScDPSaveData::WriteToSource
487 sheet::DataPilotFieldOrientation eOrient
= nOrientation
;
488 xDimProp
->setPropertyValue( SC_UNO_DP_ORIENTATION
, uno::Any(eOrient
) );
490 sal_Int16 eFunc
= static_cast<sal_Int16
>(nFunction
);
491 xDimProp
->setPropertyValue( SC_UNO_DP_FUNCTION2
, uno::Any(eFunc
) );
493 if ( nUsedHierarchy
>= 0 )
495 xDimProp
->setPropertyValue( SC_UNO_DP_USEDHIERARCHY
, uno::Any(static_cast<sal_Int32
>(nUsedHierarchy
)) );
498 if ( pReferenceValue
)
501 xDimProp
->setPropertyValue( SC_UNO_DP_REFVALUE
, uno::Any(*pReferenceValue
) );
505 ScUnoHelpFunctions::SetOptionalPropertyValue(xDimProp
, SC_UNO_DP_LAYOUTNAME
, *mpLayoutName
);
507 const std::optional
<OUString
> & pSubTotalName
= GetSubtotalName();
509 // Custom subtotal name, with '?' being replaced by the visible field name later.
510 ScUnoHelpFunctions::SetOptionalPropertyValue(xDimProp
, SC_UNO_DP_FIELD_SUBTOTALNAME
, *pSubTotalName
);
513 // Level loop outside of maMemberList loop
514 // because SubTotals have to be set independently of known members
516 tools::Long nCount
= maMemberHash
.size();
518 tools::Long nHierCount
= 0;
519 uno::Reference
<container::XIndexAccess
> xHiers
;
520 uno::Reference
<sheet::XHierarchiesSupplier
> xHierSupp( xDim
, uno::UNO_QUERY
);
521 if ( xHierSupp
.is() )
523 xHiers
= new ScNameToIndexAccess(xHierSupp
->getHierarchies());
524 nHierCount
= xHiers
->getCount();
527 bool bHasHiddenMember
= false;
529 for (tools::Long nHier
=0; nHier
<nHierCount
; nHier
++)
531 tools::Long nLevCount
= 0;
532 uno::Reference
<container::XIndexAccess
> xLevels
;
533 uno::Reference
<sheet::XLevelsSupplier
> xLevSupp(xHiers
->getByIndex(nHier
), uno::UNO_QUERY
);
536 uno::Reference
<container::XNameAccess
> xLevelsName
= xLevSupp
->getLevels();
537 xLevels
= new ScNameToIndexAccess( xLevelsName
);
538 nLevCount
= xLevels
->getCount();
541 for (tools::Long nLev
=0; nLev
<nLevCount
; nLev
++)
543 uno::Reference
<uno::XInterface
> xLevel(xLevels
->getByIndex(nLev
), uno::UNO_QUERY
);
544 uno::Reference
<beans::XPropertySet
> xLevProp( xLevel
, uno::UNO_QUERY
);
545 OSL_ENSURE( xLevProp
.is(), "no properties at level" );
548 if ( !bSubTotalDefault
)
550 uno::Sequence
<sal_Int16
> aSeq(maSubTotalFuncs
.size());
551 for(size_t i
= 0; i
< maSubTotalFuncs
.size(); ++i
)
552 aSeq
.getArray()[i
] = static_cast<sal_Int16
>(maSubTotalFuncs
[i
]);
553 xLevProp
->setPropertyValue( SC_UNO_DP_SUBTOTAL2
, uno::Any(aSeq
) );
555 if ( nShowEmptyMode
!= SC_DPSAVEMODE_DONTKNOW
)
556 lcl_SetBoolProperty( xLevProp
,
557 SC_UNO_DP_SHOWEMPTY
, static_cast<bool>(nShowEmptyMode
) );
559 lcl_SetBoolProperty( xLevProp
,
560 SC_UNO_DP_REPEATITEMLABELS
, bRepeatItemLabels
);
563 ScUnoHelpFunctions::SetOptionalPropertyValue(xLevProp
, SC_UNO_DP_SORTING
, *pSortInfo
);
566 ScUnoHelpFunctions::SetOptionalPropertyValue(xLevProp
, SC_UNO_DP_AUTOSHOW
, *pAutoShowInfo
);
569 ScUnoHelpFunctions::SetOptionalPropertyValue(xLevProp
, SC_UNO_DP_LAYOUT
, *pLayoutInfo
);
571 // exceptions are caught at ScDPSaveData::WriteToSource
576 uno::Reference
<sheet::XMembersSupplier
> xMembSupp( xLevel
, uno::UNO_QUERY
);
577 if ( xMembSupp
.is() )
579 uno::Reference
<sheet::XMembersAccess
> xMembers
= xMembSupp
->getMembers();
582 sal_Int32 nPosition
= -1; // set position only in manual mode
583 if ( !pSortInfo
|| pSortInfo
->Mode
== sheet::DataPilotFieldSortMode::MANUAL
)
586 for (ScDPSaveMember
* pMember
: maMemberList
)
588 if (!pMember
->GetIsVisible())
589 bHasHiddenMember
= true;
590 OUString aMemberName
= pMember
->GetName();
591 if ( xMembers
->hasByName( aMemberName
) )
593 uno::Reference
<uno::XInterface
> xMemberInt(
594 xMembers
->getByName(aMemberName
), uno::UNO_QUERY
);
595 pMember
->WriteToSource( xMemberInt
, nPosition
);
597 if ( nPosition
>= 0 )
598 ++nPosition
; // increase if initialized
600 // missing member is no error
609 ScUnoHelpFunctions::SetOptionalPropertyValue(xDimProp
, SC_UNO_DP_HAS_HIDDEN_MEMBER
, bHasHiddenMember
);
612 void ScDPSaveDimension::UpdateMemberVisibility(const std::unordered_map
<OUString
, bool>& rData
)
614 for (ScDPSaveMember
* pMem
: maMemberList
)
616 const OUString
& rMemName
= pMem
->GetName();
617 auto itr
= rData
.find(rMemName
);
618 if (itr
!= rData
.end())
619 pMem
->SetIsVisible(itr
->second
);
623 bool ScDPSaveDimension::HasInvisibleMember() const
625 return std::any_of(maMemberList
.begin(), maMemberList
.end(),
626 [](const ScDPSaveMember
* pMem
) { return !pMem
->GetIsVisible(); });
629 void ScDPSaveDimension::RemoveObsoleteMembers(const MemberSetType
& rMembers
)
632 for (ScDPSaveMember
* pMem
: maMemberList
)
634 if (rMembers
.count(pMem
->GetName()))
636 // This member still exists.
637 aNew
.push_back(pMem
);
641 maMemberHash
.erase(pMem
->GetName());
645 maMemberList
.swap(aNew
);
650 void ScDPSaveDimension::Dump(int nIndent
) const
652 static const char* pOrientNames
[] = { "hidden", "column", "row", "page", "data" };
653 std::string
aIndent(nIndent
*4, ' ');
655 cout
<< aIndent
<< "* dimension name: '" << aName
<< "'" << endl
;
657 cout
<< aIndent
<< " + orientation: ";
658 if (nOrientation
<= DataPilotFieldOrientation_DATA
)
659 cout
<< pOrientNames
[static_cast<int>(nOrientation
)];
664 cout
<< aIndent
<< " + layout name: ";
666 cout
<< "'" << *mpLayoutName
<< "'";
671 cout
<< aIndent
<< " + subtotal name: ";
673 cout
<< "'" << *mpSubtotalName
<< "'";
678 cout
<< aIndent
<< " + is data layout: " << (bIsDataLayout
? "yes" : "no") << endl
;
679 cout
<< aIndent
<< " + is duplicate: " << (bDupFlag
? "yes" : "no") << endl
;
681 for (ScDPSaveMember
* pMem
: maMemberList
)
683 pMem
->Dump(nIndent
+1);
686 cout
<< endl
; // blank line
691 ScDPSaveData::ScDPSaveData() :
692 nColumnGrandMode( SC_DPSAVEMODE_DONTKNOW
),
693 nRowGrandMode( SC_DPSAVEMODE_DONTKNOW
),
694 nIgnoreEmptyMode( SC_DPSAVEMODE_DONTKNOW
),
695 nRepeatEmptyMode( SC_DPSAVEMODE_DONTKNOW
),
696 bFilterButton( true ),
698 bExpandCollapse( false ),
699 mbDimensionMembersBuilt(false)
703 ScDPSaveData::ScDPSaveData(const ScDPSaveData
& r
) :
704 nColumnGrandMode( r
.nColumnGrandMode
),
705 nRowGrandMode( r
.nRowGrandMode
),
706 nIgnoreEmptyMode( r
.nIgnoreEmptyMode
),
707 nRepeatEmptyMode( r
.nRepeatEmptyMode
),
708 bFilterButton( r
.bFilterButton
),
709 bDrillDown( r
.bDrillDown
),
710 bExpandCollapse( r
.bExpandCollapse
),
711 mbDimensionMembersBuilt(r
.mbDimensionMembersBuilt
),
712 mpGrandTotalName(r
.mpGrandTotalName
)
714 if ( r
.pDimensionData
)
715 pDimensionData
.reset( new ScDPDimensionSaveData( *r
.pDimensionData
) );
717 for (auto const& it
: r
.m_DimList
)
719 m_DimList
.push_back(std::make_unique
<ScDPSaveDimension
>(*it
));
723 ScDPSaveData
& ScDPSaveData::operator= ( const ScDPSaveData
& r
)
727 this->~ScDPSaveData();
728 new( this ) ScDPSaveData ( r
);
733 bool ScDPSaveData::operator== ( const ScDPSaveData
& r
) const
735 if ( nColumnGrandMode
!= r
.nColumnGrandMode
||
736 nRowGrandMode
!= r
.nRowGrandMode
||
737 nIgnoreEmptyMode
!= r
.nIgnoreEmptyMode
||
738 nRepeatEmptyMode
!= r
.nRepeatEmptyMode
||
739 bFilterButton
!= r
.bFilterButton
||
740 bDrillDown
!= r
.bDrillDown
||
741 mbDimensionMembersBuilt
!= r
.mbDimensionMembersBuilt
)
744 if ( pDimensionData
|| r
.pDimensionData
)
745 if ( !pDimensionData
|| !r
.pDimensionData
|| !( *pDimensionData
== *r
.pDimensionData
) )
748 if (!(::comphelper::ContainerUniquePtrEquals(m_DimList
, r
.m_DimList
)))
751 if (mpGrandTotalName
)
753 if (!r
.mpGrandTotalName
)
755 if (*mpGrandTotalName
!= *r
.mpGrandTotalName
)
758 else if (r
.mpGrandTotalName
)
764 ScDPSaveData::~ScDPSaveData()
768 void ScDPSaveData::SetGrandTotalName(const OUString
& rName
)
770 mpGrandTotalName
= rName
;
773 const std::optional
<OUString
> & ScDPSaveData::GetGrandTotalName() const
775 return mpGrandTotalName
;
780 class DimOrderInserter
782 ScDPSaveData::DimOrderType
& mrNames
;
784 explicit DimOrderInserter(ScDPSaveData::DimOrderType
& rNames
) : mrNames(rNames
) {}
786 void operator() (const ScDPSaveDimension
* pDim
)
788 size_t nRank
= mrNames
.size();
789 mrNames
.emplace(ScGlobal::getCharClass().uppercase(pDim
->GetName()), nRank
);
795 const ScDPSaveData::DimOrderType
& ScDPSaveData::GetDimensionSortOrder() const
799 mpDimOrder
.reset(new DimOrderType
);
800 std::vector
<const ScDPSaveDimension
*> aRowDims
, aColDims
;
801 GetAllDimensionsByOrientation(sheet::DataPilotFieldOrientation_ROW
, aRowDims
);
802 GetAllDimensionsByOrientation(sheet::DataPilotFieldOrientation_COLUMN
, aColDims
);
804 std::for_each(aRowDims
.begin(), aRowDims
.end(), DimOrderInserter(*mpDimOrder
));
805 std::for_each(aColDims
.begin(), aColDims
.end(), DimOrderInserter(*mpDimOrder
));
810 void ScDPSaveData::GetAllDimensionsByOrientation(
811 sheet::DataPilotFieldOrientation eOrientation
, std::vector
<const ScDPSaveDimension
*>& rDims
) const
813 std::vector
<const ScDPSaveDimension
*> aDims
;
814 for (auto const& it
: m_DimList
)
816 const ScDPSaveDimension
& rDim
= *it
;
817 if (rDim
.GetOrientation() != eOrientation
)
820 aDims
.push_back(&rDim
);
826 void ScDPSaveData::AddDimension(ScDPSaveDimension
* pDim
)
831 CheckDuplicateName(*pDim
);
832 m_DimList
.push_back(std::unique_ptr
<ScDPSaveDimension
>(pDim
));
837 ScDPSaveDimension
* ScDPSaveData::GetDimensionByName(const OUString
& rName
)
839 for (auto const& iter
: m_DimList
)
841 if (iter
->GetName() == rName
&& !iter
->IsDataLayout() )
845 return AppendNewDimension(rName
, false);
848 ScDPSaveDimension
* ScDPSaveData::GetExistingDimensionByName(std::u16string_view rName
) const
850 for (auto const& iter
: m_DimList
)
852 if (iter
->GetName() == rName
&& !iter
->IsDataLayout() )
855 return nullptr; // don't create new
858 ScDPSaveDimension
* ScDPSaveData::GetNewDimensionByName(const OUString
& rName
)
860 for (auto const& iter
: m_DimList
)
862 if (iter
->GetName() == rName
&& !iter
->IsDataLayout() )
863 return DuplicateDimension(rName
);
866 return AppendNewDimension(rName
, false);
869 ScDPSaveDimension
* ScDPSaveData::GetDataLayoutDimension()
871 ScDPSaveDimension
* pDim
= GetExistingDataLayoutDimension();
875 return AppendNewDimension(OUString(), true);
878 ScDPSaveDimension
* ScDPSaveData::GetExistingDataLayoutDimension() const
880 for (auto const& iter
: m_DimList
)
882 if ( iter
->IsDataLayout() )
888 ScDPSaveDimension
* ScDPSaveData::DuplicateDimension(std::u16string_view rName
)
892 ScDPSaveDimension
* pOld
= GetExistingDimensionByName(rName
);
896 ScDPSaveDimension
* pNew
= new ScDPSaveDimension( *pOld
);
901 void ScDPSaveData::RemoveDimensionByName(const OUString
& rName
)
903 auto iter
= std::find_if(m_DimList
.begin(), m_DimList
.end(),
904 [&rName
](const std::unique_ptr
<ScDPSaveDimension
>& rxDim
) {
905 return rxDim
->GetName() == rName
&& !rxDim
->IsDataLayout(); });
906 if (iter
!= m_DimList
.end())
908 m_DimList
.erase(iter
);
909 RemoveDuplicateNameCount(rName
);
914 ScDPSaveDimension
& ScDPSaveData::DuplicateDimension( const ScDPSaveDimension
& rDim
)
916 ScDPSaveDimension
* pNew
= new ScDPSaveDimension( rDim
);
921 ScDPSaveDimension
* ScDPSaveData::GetInnermostDimension(DataPilotFieldOrientation nOrientation
)
923 // return the innermost dimension for the given orientation,
924 // excluding data layout dimension
926 auto iter
= std::find_if(m_DimList
.rbegin(), m_DimList
.rend(),
927 [&nOrientation
](const std::unique_ptr
<ScDPSaveDimension
>& rxDim
) {
928 return rxDim
->GetOrientation() == nOrientation
&& !rxDim
->IsDataLayout(); });
929 if (iter
!= m_DimList
.rend())
935 ScDPSaveDimension
* ScDPSaveData::GetFirstDimension(sheet::DataPilotFieldOrientation eOrientation
)
937 for (auto const& iter
: m_DimList
)
939 if (iter
->GetOrientation() == eOrientation
&& !iter
->IsDataLayout())
945 tools::Long
ScDPSaveData::GetDataDimensionCount() const
947 tools::Long nDataCount
= 0;
949 for (auto const& iter
: m_DimList
)
951 if (iter
->GetOrientation() == sheet::DataPilotFieldOrientation_DATA
)
958 void ScDPSaveData::SetPosition( ScDPSaveDimension
* pDim
, tools::Long nNew
)
960 // position (nNew) is counted within dimensions of the same orientation
962 DataPilotFieldOrientation nOrient
= pDim
->GetOrientation();
964 auto it
= std::find_if(m_DimList
.begin(), m_DimList
.end(),
965 [&pDim
](const std::unique_ptr
<ScDPSaveDimension
>& rxDim
) { return pDim
== rxDim
.get(); });
966 if (it
!= m_DimList
.end())
968 // Tell vector<unique_ptr> to give up ownership of this element. Don't
969 // delete this instance as it is re-inserted into the container later.
970 // coverity[leaked_storage] - re-inserted into the container later
975 auto iterInsert
= std::find_if(m_DimList
.begin(), m_DimList
.end(),
976 [&nOrient
, &nNew
](const std::unique_ptr
<ScDPSaveDimension
>& rxDim
) {
977 if (rxDim
->GetOrientation() == nOrient
)
982 m_DimList
.insert(iterInsert
, std::unique_ptr
<ScDPSaveDimension
>(pDim
));
986 void ScDPSaveData::SetColumnGrand(bool bSet
)
988 nColumnGrandMode
= sal_uInt16(bSet
);
991 void ScDPSaveData::SetRowGrand(bool bSet
)
993 nRowGrandMode
= sal_uInt16(bSet
);
996 void ScDPSaveData::SetIgnoreEmptyRows(bool bSet
)
998 nIgnoreEmptyMode
= sal_uInt16(bSet
);
1001 void ScDPSaveData::SetRepeatIfEmpty(bool bSet
)
1003 nRepeatEmptyMode
= sal_uInt16(bSet
);
1006 void ScDPSaveData::SetFilterButton(bool bSet
)
1008 bFilterButton
= bSet
;
1011 void ScDPSaveData::SetDrillDown(bool bSet
)
1016 void ScDPSaveData::SetExpandCollapse(bool bSet
)
1018 bExpandCollapse
= bSet
;
1021 static void lcl_ResetOrient( const uno::Reference
<sheet::XDimensionsSupplier
>& xSource
)
1023 uno::Reference
<container::XNameAccess
> xDimsName
= xSource
->getDimensions();
1024 uno::Reference
<container::XIndexAccess
> xIntDims
= new ScNameToIndexAccess( xDimsName
);
1025 tools::Long nIntCount
= xIntDims
->getCount();
1026 for (tools::Long nIntDim
=0; nIntDim
<nIntCount
; nIntDim
++)
1028 uno::Reference
<beans::XPropertySet
> xDimProp(xIntDims
->getByIndex(nIntDim
), uno::UNO_QUERY
);
1031 xDimProp
->setPropertyValue( SC_UNO_DP_ORIENTATION
, uno::Any(sheet::DataPilotFieldOrientation_HIDDEN
) );
1036 void ScDPSaveData::WriteToSource( const uno::Reference
<sheet::XDimensionsSupplier
>& xSource
)
1041 // source options must be first!
1043 uno::Reference
<beans::XPropertySet
> xSourceProp( xSource
, uno::UNO_QUERY
);
1044 SAL_WARN_IF( !xSourceProp
.is(), "sc.core", "no properties at source" );
1045 if ( xSourceProp
.is() )
1047 // source options are not available for external sources
1048 //TODO: use XPropertySetInfo to test for availability?
1052 if ( nIgnoreEmptyMode
!= SC_DPSAVEMODE_DONTKNOW
)
1053 lcl_SetBoolProperty( xSourceProp
,
1054 SC_UNO_DP_IGNOREEMPTY
, static_cast<bool>(nIgnoreEmptyMode
) );
1055 if ( nRepeatEmptyMode
!= SC_DPSAVEMODE_DONTKNOW
)
1056 lcl_SetBoolProperty( xSourceProp
,
1057 SC_UNO_DP_REPEATEMPTY
, static_cast<bool>(nRepeatEmptyMode
) );
1059 catch(uno::Exception
&)
1064 const std::optional
<OUString
> & pGrandTotalName
= GetGrandTotalName();
1065 if (pGrandTotalName
)
1066 ScUnoHelpFunctions::SetOptionalPropertyValue(xSourceProp
, SC_UNO_DP_GRANDTOTAL_NAME
, *pGrandTotalName
);
1069 // exceptions in the other calls are errors
1072 // reset all orientations
1073 //TODO: "forgetSettings" or similar at source ?????
1074 //TODO: reset all duplicated dimensions, or reuse them below !!!
1075 SAL_INFO("sc.core", "ScDPSaveData::WriteToSource");
1077 lcl_ResetOrient( xSource
);
1079 uno::Reference
<container::XNameAccess
> xDimsName
= xSource
->getDimensions();
1080 uno::Reference
<container::XIndexAccess
> xIntDims
= new ScNameToIndexAccess( xDimsName
);
1081 tools::Long nIntCount
= xIntDims
->getCount();
1083 for (const auto& rxDim
: m_DimList
)
1085 OUString aName
= rxDim
->GetName();
1086 OUString aCoreName
= ScDPUtil::getSourceDimensionName(aName
);
1088 SAL_INFO("sc.core", aName
);
1090 bool bData
= rxDim
->IsDataLayout();
1092 //TODO: getByName for ScDPSource, including DataLayoutDimension !!!!!!!!
1094 bool bFound
= false;
1095 for (tools::Long nIntDim
=0; nIntDim
<nIntCount
&& !bFound
; nIntDim
++)
1097 uno::Reference
<uno::XInterface
> xIntDim(xIntDims
->getByIndex(nIntDim
),
1101 uno::Reference
<beans::XPropertySet
> xDimProp( xIntDim
, uno::UNO_QUERY
);
1102 if ( xDimProp
.is() )
1104 bFound
= ScUnoHelpFunctions::GetBoolProperty( xDimProp
,
1105 SC_UNO_DP_ISDATALAYOUT
);
1106 //TODO: error checking -- is "IsDataLayoutDimension" property required??
1111 uno::Reference
<container::XNamed
> xDimName( xIntDim
, uno::UNO_QUERY
);
1112 if (xDimName
.is() && xDimName
->getName() == aCoreName
)
1118 if (rxDim
->GetDupFlag())
1120 uno::Reference
<util::XCloneable
> xCloneable(xIntDim
, uno::UNO_QUERY
);
1121 SAL_WARN_IF(!xCloneable
.is(), "sc.core", "cannot clone dimension");
1122 if (xCloneable
.is())
1124 uno::Reference
<util::XCloneable
> xNew
= xCloneable
->createClone();
1125 uno::Reference
<container::XNamed
> xNewName(xNew
, uno::UNO_QUERY
);
1128 xNewName
->setName(aName
);
1129 rxDim
->WriteToSource(xNew
);
1134 rxDim
->WriteToSource( xIntDim
);
1137 SAL_WARN_IF(!bFound
, "sc.core", "WriteToSource: Dimension not found: " + aName
+ ".");
1140 if ( xSourceProp
.is() )
1142 if ( nColumnGrandMode
!= SC_DPSAVEMODE_DONTKNOW
)
1143 lcl_SetBoolProperty( xSourceProp
,
1144 SC_UNO_DP_COLGRAND
, static_cast<bool>(nColumnGrandMode
) );
1145 if ( nRowGrandMode
!= SC_DPSAVEMODE_DONTKNOW
)
1146 lcl_SetBoolProperty( xSourceProp
,
1147 SC_UNO_DP_ROWGRAND
, static_cast<bool>(nRowGrandMode
) );
1150 catch(uno::Exception
const &)
1152 TOOLS_WARN_EXCEPTION("sc.core", "WriteToSource");
1156 bool ScDPSaveData::IsEmpty() const
1158 for (auto const& iter
: m_DimList
)
1160 if (iter
->GetOrientation() != sheet::DataPilotFieldOrientation_HIDDEN
&& !iter
->IsDataLayout())
1163 return true; // no entries that are not hidden
1166 void ScDPSaveData::RemoveAllGroupDimensions( const OUString
& rSrcDimName
, std::vector
<OUString
>* pDeletedNames
)
1168 if (!pDimensionData
)
1169 // No group dimensions exist. Nothing to do.
1172 // Remove numeric group dimension (exists once at most). No need to delete
1173 // anything in save data (grouping was done inplace in an existing base
1175 pDimensionData
->RemoveNumGroupDimension(rSrcDimName
);
1177 // Remove named group dimension(s). Dimensions have to be removed from
1178 // dimension save data and from save data too.
1179 const ScDPSaveGroupDimension
* pExistingGroup
= pDimensionData
->GetGroupDimForBase(rSrcDimName
);
1180 while ( pExistingGroup
)
1182 OUString aGroupDimName
= pExistingGroup
->GetGroupDimName();
1183 pDimensionData
->RemoveGroupDimension(aGroupDimName
); // pExistingGroup is deleted
1185 // also remove SaveData settings for the dimension that no longer exists
1186 RemoveDimensionByName(aGroupDimName
);
1189 pDeletedNames
->push_back(aGroupDimName
);
1191 // see if there are more group dimensions
1192 pExistingGroup
= pDimensionData
->GetGroupDimForBase(rSrcDimName
);
1194 if ( pExistingGroup
&& pExistingGroup
->GetGroupDimName() == aGroupDimName
)
1196 // still get the same group dimension?
1197 OSL_FAIL("couldn't remove group dimension");
1198 pExistingGroup
= nullptr; // avoid endless loop
1203 ScDPDimensionSaveData
* ScDPSaveData::GetDimensionData()
1205 if (!pDimensionData
)
1206 pDimensionData
.reset( new ScDPDimensionSaveData
);
1207 return pDimensionData
.get();
1210 void ScDPSaveData::SetDimensionData( const ScDPDimensionSaveData
* pNew
)
1213 pDimensionData
.reset( new ScDPDimensionSaveData( *pNew
) );
1215 pDimensionData
.reset();
1218 void ScDPSaveData::BuildAllDimensionMembers(ScDPTableData
* pData
)
1220 if (mbDimensionMembersBuilt
)
1223 // First, build a dimension name-to-index map.
1224 typedef std::unordered_map
<OUString
, tools::Long
> NameIndexMap
;
1226 tools::Long nColCount
= pData
->GetColumnCount();
1227 for (tools::Long i
= 0; i
< nColCount
; ++i
)
1228 aMap
.emplace(pData
->getDimensionName(i
), i
);
1230 NameIndexMap::const_iterator itrEnd
= aMap
.end();
1232 for (auto const& iter
: m_DimList
)
1234 const OUString
& rDimName
= iter
->GetName();
1235 if (rDimName
.isEmpty())
1236 // empty dimension name. It must be data layout.
1239 NameIndexMap::const_iterator itr
= aMap
.find(rDimName
);
1241 // dimension name not in the data. This should never happen!
1244 tools::Long nDimIndex
= itr
->second
;
1245 const std::vector
<SCROW
>& rMembers
= pData
->GetColumnEntries(nDimIndex
);
1246 size_t nMemberCount
= rMembers
.size();
1247 for (size_t j
= 0; j
< nMemberCount
; ++j
)
1249 const ScDPItemData
* pMemberData
= pData
->GetMemberById( nDimIndex
, rMembers
[j
] );
1250 OUString aMemName
= pData
->GetFormattedString(nDimIndex
, *pMemberData
, false);
1251 if (iter
->GetExistingMemberByName(aMemName
))
1252 // this member instance already exists. nothing to do.
1255 unique_ptr
<ScDPSaveMember
> pNewMember(new ScDPSaveMember(aMemName
));
1256 pNewMember
->SetIsVisible(true);
1257 iter
->AddMember(std::move(pNewMember
));
1261 mbDimensionMembersBuilt
= true;
1264 void ScDPSaveData::SyncAllDimensionMembers(ScDPTableData
* pData
)
1266 typedef std::unordered_map
<OUString
, tools::Long
> NameIndexMap
;
1268 // First, build a dimension name-to-index map.
1270 tools::Long nColCount
= pData
->GetColumnCount();
1271 for (tools::Long i
= 0; i
< nColCount
; ++i
)
1272 aMap
.emplace(pData
->getDimensionName(i
), i
);
1274 NameIndexMap::const_iterator itMapEnd
= aMap
.end();
1276 for (auto const& it
: m_DimList
)
1278 const OUString
& rDimName
= it
->GetName();
1279 if (rDimName
.isEmpty())
1280 // empty dimension name. It must be data layout.
1283 NameIndexMap::const_iterator itMap
= aMap
.find(rDimName
);
1284 if (itMap
== itMapEnd
)
1285 // dimension name not in the data. This should never happen!
1288 ScDPSaveDimension::MemberSetType aMemNames
;
1289 tools::Long nDimIndex
= itMap
->second
;
1290 const std::vector
<SCROW
>& rMembers
= pData
->GetColumnEntries(nDimIndex
);
1291 size_t nMemberCount
= rMembers
.size();
1292 for (size_t j
= 0; j
< nMemberCount
; ++j
)
1294 const ScDPItemData
* pMemberData
= pData
->GetMemberById(nDimIndex
, rMembers
[j
]);
1295 // ScDPCache::GetItemDataById() (via
1296 // ScDPTableData::GetMemberById(),
1297 // ScDPGroupTableData::GetMemberById() through
1298 // GetCacheTable().getCache()) may return nullptr.
1301 OUString aMemName
= pData
->GetFormattedString(nDimIndex
, *pMemberData
, false);
1302 aMemNames
.insert(aMemName
);
1306 SAL_WARN("sc.core", "No pMemberData for nDimIndex " << nDimIndex
<< ", rMembers[j] " << rMembers
[j
]
1311 it
->RemoveObsoleteMembers(aMemNames
);
1315 bool ScDPSaveData::HasInvisibleMember(std::u16string_view rDimName
) const
1317 ScDPSaveDimension
* pDim
= GetExistingDimensionByName(rDimName
);
1321 return pDim
->HasInvisibleMember();
1324 #if DUMP_PIVOT_TABLE
1326 void ScDPSaveData::Dump() const
1328 for (auto const& itDim
: m_DimList
)
1330 const ScDPSaveDimension
& rDim
= *itDim
;
1337 void ScDPSaveData::CheckDuplicateName(ScDPSaveDimension
& rDim
)
1339 const OUString aName
= ScDPUtil::getSourceDimensionName(rDim
.GetName());
1340 DupNameCountType::iterator it
= maDupNameCounts
.find(aName
);
1341 if (it
!= maDupNameCounts
.end())
1343 rDim
.SetName(ScDPUtil::createDuplicateDimensionName(aName
, ++it
->second
));
1344 rDim
.SetDupFlag(true);
1348 maDupNameCounts
.emplace(aName
, 0);
1351 void ScDPSaveData::RemoveDuplicateNameCount(const OUString
& rName
)
1353 OUString aCoreName
= rName
;
1354 if (ScDPUtil::isDuplicateDimension(rName
))
1355 aCoreName
= ScDPUtil::getSourceDimensionName(rName
);
1357 DupNameCountType::iterator it
= maDupNameCounts
.find(aCoreName
);
1358 if (it
== maDupNameCounts
.end())
1363 maDupNameCounts
.erase(it
);
1370 ScDPSaveDimension
* ScDPSaveData::AppendNewDimension(const OUString
& rName
, bool bDataLayout
)
1372 if (ScDPUtil::isDuplicateDimension(rName
))
1373 // This call is for original dimensions only.
1376 ScDPSaveDimension
* pNew
= new ScDPSaveDimension(rName
, bDataLayout
);
1377 m_DimList
.push_back(std::unique_ptr
<ScDPSaveDimension
>(pNew
));
1378 if (!maDupNameCounts
.count(rName
))
1379 maDupNameCounts
.emplace(rName
, 0);
1381 DimensionsChanged();
1385 void ScDPSaveData::DimensionsChanged()
1390 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */