1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <memory>
21 #include <dpsave.hxx>
22 #include <dpdimsave.hxx>
23 #include <miscuno.hxx>
24 #include <unonames.hxx>
25 #include <dputil.hxx>
26 #include <generalfunction.hxx>
27 #include <dptabdat.hxx>
28 
29 #include <sal/types.h>
30 #include <sal/log.hxx>
31 #include <osl/diagnose.h>
32 #include <comphelper/stl_types.hxx>
33 
34 #include <com/sun/star/sheet/XDimensionsSupplier.hpp>
35 #include <com/sun/star/sheet/DataPilotFieldAutoShowInfo.hpp>
36 #include <com/sun/star/sheet/DataPilotFieldLayoutInfo.hpp>
37 #include <com/sun/star/sheet/DataPilotFieldReference.hpp>
38 #include <com/sun/star/sheet/DataPilotFieldSortInfo.hpp>
39 #include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
40 #include <com/sun/star/sheet/XHierarchiesSupplier.hpp>
41 #include <com/sun/star/sheet/XLevelsSupplier.hpp>
42 #include <com/sun/star/sheet/XMembersSupplier.hpp>
43 #include <com/sun/star/container/XNamed.hpp>
44 #include <com/sun/star/util/XCloneable.hpp>
45 #include <tools/diagnose_ex.h>
46 
47 #include <unordered_map>
48 #include <algorithm>
49 
50 using namespace com::sun::star;
51 using namespace com::sun::star::sheet;
52 using ::std::unique_ptr;
53 
54 #define SC_DPSAVEMODE_DONTKNOW 2
55 
lcl_SetBoolProperty(const uno::Reference<beans::XPropertySet> & xProp,const OUString & rName,bool bValue)56 static void lcl_SetBoolProperty( const uno::Reference<beans::XPropertySet>& xProp,
57                             const OUString& rName, bool bValue )
58 {
59     //TODO: move to ScUnoHelpFunctions?
60 
61     xProp->setPropertyValue( rName, uno::Any( bValue ) );
62 }
63 
ScDPSaveMember(const OUString & rName)64 ScDPSaveMember::ScDPSaveMember(const OUString& rName) :
65     aName( rName ),
66     nVisibleMode( SC_DPSAVEMODE_DONTKNOW ),
67     nShowDetailsMode( SC_DPSAVEMODE_DONTKNOW )
68 {
69 }
70 
ScDPSaveMember(const ScDPSaveMember & r)71 ScDPSaveMember::ScDPSaveMember(const ScDPSaveMember& r) :
72     aName( r.aName ),
73     mpLayoutName( r.mpLayoutName ),
74     nVisibleMode( r.nVisibleMode ),
75     nShowDetailsMode( r.nShowDetailsMode )
76 {
77 }
78 
~ScDPSaveMember()79 ScDPSaveMember::~ScDPSaveMember()
80 {
81 }
82 
operator ==(const ScDPSaveMember & r) const83 bool ScDPSaveMember::operator== ( const ScDPSaveMember& r ) const
84 {
85     return aName            == r.aName           &&
86            nVisibleMode     == r.nVisibleMode    &&
87            nShowDetailsMode == r.nShowDetailsMode;
88 }
89 
HasIsVisible() const90 bool ScDPSaveMember::HasIsVisible() const
91 {
92     return nVisibleMode != SC_DPSAVEMODE_DONTKNOW;
93 }
94 
SetIsVisible(bool bSet)95 void ScDPSaveMember::SetIsVisible(bool bSet)
96 {
97     nVisibleMode = sal_uInt16(bSet);
98 }
99 
HasShowDetails() const100 bool ScDPSaveMember::HasShowDetails() const
101 {
102     return nShowDetailsMode != SC_DPSAVEMODE_DONTKNOW;
103 }
104 
SetShowDetails(bool bSet)105 void ScDPSaveMember::SetShowDetails(bool bSet)
106 {
107     nShowDetailsMode = sal_uInt16(bSet);
108 }
109 
SetName(const OUString & rNew)110 void ScDPSaveMember::SetName( const OUString& rNew )
111 {
112     // Used only if the source member was renamed (groups).
113     // For UI renaming of members, a layout name must be used.
114 
115     aName = rNew;
116 }
117 
SetLayoutName(const OUString & rName)118 void ScDPSaveMember::SetLayoutName( const OUString& rName )
119 {
120     mpLayoutName = rName;
121 }
122 
GetLayoutName() const123 const std::optional<OUString> & ScDPSaveMember::GetLayoutName() const
124 {
125     return mpLayoutName;
126 }
127 
RemoveLayoutName()128 void ScDPSaveMember::RemoveLayoutName()
129 {
130     mpLayoutName.reset();
131 }
132 
WriteToSource(const uno::Reference<uno::XInterface> & xMember,sal_Int32 nPosition)133 void ScDPSaveMember::WriteToSource( const uno::Reference<uno::XInterface>& xMember, sal_Int32 nPosition )
134 {
135     uno::Reference<beans::XPropertySet> xMembProp( xMember, uno::UNO_QUERY );
136     OSL_ENSURE( xMembProp.is(), "no properties at member" );
137     if ( !xMembProp.is() )
138         return;
139 
140     // exceptions are caught at ScDPSaveData::WriteToSource
141 
142     if ( nVisibleMode != SC_DPSAVEMODE_DONTKNOW )
143         lcl_SetBoolProperty( xMembProp,
144                 SC_UNO_DP_ISVISIBLE, static_cast<bool>(nVisibleMode) );
145 
146     if ( nShowDetailsMode != SC_DPSAVEMODE_DONTKNOW )
147         lcl_SetBoolProperty( xMembProp,
148                 SC_UNO_DP_SHOWDETAILS, static_cast<bool>(nShowDetailsMode) );
149 
150     if (mpLayoutName)
151         ScUnoHelpFunctions::SetOptionalPropertyValue(xMembProp, SC_UNO_DP_LAYOUTNAME, *mpLayoutName);
152 
153     if ( nPosition >= 0 )
154         ScUnoHelpFunctions::SetOptionalPropertyValue(xMembProp, SC_UNO_DP_POSITION, nPosition);
155 }
156 
157 #if DUMP_PIVOT_TABLE
158 
Dump(int nIndent) const159 void ScDPSaveMember::Dump(int nIndent) const
160 {
161     std::string aIndent(nIndent*4, ' ');
162     cout << aIndent << "* member name: '" << aName << "'" << endl;
163 
164     cout << aIndent << "    + layout name: ";
165     if (mpLayoutName)
166         cout << "'" << *mpLayoutName << "'";
167     else
168         cout << "(none)";
169     cout << endl;
170 
171     cout << aIndent << "    + visibility: ";
172     if (nVisibleMode == SC_DPSAVEMODE_DONTKNOW)
173         cout << "(unknown)";
174     else
175         cout << (nVisibleMode ? "visible" : "hidden");
176     cout << endl;
177 }
178 
179 #endif
180 
ScDPSaveDimension(const OUString & rName,bool bDataLayout)181 ScDPSaveDimension::ScDPSaveDimension(const OUString& rName, bool bDataLayout) :
182     aName( rName ),
183     bIsDataLayout( bDataLayout ),
184     bDupFlag( false ),
185     nOrientation( sheet::DataPilotFieldOrientation_HIDDEN ),
186     nFunction( ScGeneralFunction::AUTO ),
187     nUsedHierarchy( -1 ),
188     nShowEmptyMode( SC_DPSAVEMODE_DONTKNOW ),
189     bRepeatItemLabels( false ),
190     bSubTotalDefault( true )
191 {
192 }
193 
ScDPSaveDimension(const ScDPSaveDimension & r)194 ScDPSaveDimension::ScDPSaveDimension(const ScDPSaveDimension& r) :
195     aName( r.aName ),
196     mpLayoutName( r.mpLayoutName ),
197     mpSubtotalName( r.mpSubtotalName ),
198     bIsDataLayout( r.bIsDataLayout ),
199     bDupFlag( r.bDupFlag ),
200     nOrientation( r.nOrientation ),
201     nFunction( r.nFunction ),
202     nUsedHierarchy( r.nUsedHierarchy ),
203     nShowEmptyMode( r.nShowEmptyMode ),
204     bRepeatItemLabels( r.bRepeatItemLabels ),
205     bSubTotalDefault( r.bSubTotalDefault ),
206     maSubTotalFuncs( r.maSubTotalFuncs )
207 {
208     for (const ScDPSaveMember* pMem : r.maMemberList)
209     {
210         const OUString& rName = pMem->GetName();
211         std::unique_ptr<ScDPSaveMember> pNew(new ScDPSaveMember( *pMem ));
212         maMemberList.push_back( pNew.get() );
213         maMemberHash[rName] = std::move(pNew);
214     }
215     if (r.pReferenceValue)
216         pReferenceValue.reset( new sheet::DataPilotFieldReference( *(r.pReferenceValue) ) );
217     if (r.pSortInfo)
218         pSortInfo.reset( new sheet::DataPilotFieldSortInfo( *(r.pSortInfo) ) );
219     if (r.pAutoShowInfo)
220         pAutoShowInfo.reset( new sheet::DataPilotFieldAutoShowInfo( *(r.pAutoShowInfo) ) );
221     if (r.pLayoutInfo)
222         pLayoutInfo.reset(new sheet::DataPilotFieldLayoutInfo( *(r.pLayoutInfo) ));
223 }
224 
~ScDPSaveDimension()225 ScDPSaveDimension::~ScDPSaveDimension()
226 {
227     maMemberHash.clear();
228     pReferenceValue.reset();
229     pSortInfo.reset();
230     pAutoShowInfo.reset();
231     pLayoutInfo.reset();
232 }
233 
operator ==(const ScDPSaveDimension & r) const234 bool ScDPSaveDimension::operator== ( const ScDPSaveDimension& r ) const
235 {
236     if ( aName            != r.aName            ||
237          bIsDataLayout    != r.bIsDataLayout    ||
238          bDupFlag         != r.bDupFlag         ||
239          nOrientation     != r.nOrientation     ||
240          nFunction        != r.nFunction        ||
241          nUsedHierarchy   != r.nUsedHierarchy   ||
242          nShowEmptyMode   != r.nShowEmptyMode   ||
243          bRepeatItemLabels!= r.bRepeatItemLabels||
244          bSubTotalDefault != r.bSubTotalDefault ||
245          maSubTotalFuncs  != r.maSubTotalFuncs   )
246         return false;
247 
248     if (maMemberHash.size() != r.maMemberHash.size() )
249         return false;
250 
251     if (!std::equal(maMemberList.begin(), maMemberList.end(), r.maMemberList.begin(), r.maMemberList.end(),
252                     [](const ScDPSaveMember* a, const ScDPSaveMember* b) { return *a == *b; }))
253         return false;
254 
255     if( pReferenceValue && r.pReferenceValue )
256     {
257         if ( *pReferenceValue != *r.pReferenceValue )
258         {
259             return false;
260         }
261     }
262     else if ( pReferenceValue || r.pReferenceValue )
263     {
264         return false;
265     }
266     if( this->pSortInfo && r.pSortInfo )
267     {
268         if ( *this->pSortInfo != *r.pSortInfo )
269         {
270             return false;
271         }
272     }
273     else if ( this->pSortInfo || r.pSortInfo )
274     {
275         return false;
276     }
277     if( this->pAutoShowInfo && r.pAutoShowInfo )
278     {
279         if ( *this->pAutoShowInfo != *r.pAutoShowInfo )
280         {
281             return false;
282         }
283     }
284     else if ( this->pAutoShowInfo || r.pAutoShowInfo )
285     {
286         return false;
287     }
288 
289     return true;
290 }
291 
AddMember(std::unique_ptr<ScDPSaveMember> pMember)292 void ScDPSaveDimension::AddMember(std::unique_ptr<ScDPSaveMember> pMember)
293 {
294     const OUString & rName = pMember->GetName();
295     auto aExisting = maMemberHash.find( rName );
296     auto tmp = pMember.get();
297     if ( aExisting == maMemberHash.end() )
298     {
299         maMemberHash[rName] = std::move(pMember);
300     }
301     else
302     {
303         maMemberList.erase(std::remove(maMemberList.begin(), maMemberList.end(), aExisting->second.get()), maMemberList.end());
304         aExisting->second = std::move(pMember);
305     }
306     maMemberList.push_back( tmp );
307 }
308 
SetName(const OUString & rNew)309 void ScDPSaveDimension::SetName( const OUString& rNew )
310 {
311     // Used only if the source dim was renamed (groups).
312     // For UI renaming of dimensions, the layout name must be used.
313 
314     aName = rNew;
315 }
316 
SetOrientation(css::sheet::DataPilotFieldOrientation nNew)317 void ScDPSaveDimension::SetOrientation(css::sheet::DataPilotFieldOrientation nNew)
318 {
319     nOrientation = nNew;
320 }
321 
SetSubTotals(std::vector<ScGeneralFunction> const & rFuncs)322 void ScDPSaveDimension::SetSubTotals(std::vector<ScGeneralFunction> const & rFuncs)
323 {
324     maSubTotalFuncs = rFuncs;
325     bSubTotalDefault = false;
326 }
327 
HasShowEmpty() const328 bool ScDPSaveDimension::HasShowEmpty() const
329 {
330     return nShowEmptyMode != SC_DPSAVEMODE_DONTKNOW;
331 }
332 
SetShowEmpty(bool bSet)333 void ScDPSaveDimension::SetShowEmpty(bool bSet)
334 {
335     nShowEmptyMode = sal_uInt16(bSet);
336 }
337 
SetRepeatItemLabels(bool bSet)338 void ScDPSaveDimension::SetRepeatItemLabels(bool bSet)
339 {
340     bRepeatItemLabels = bSet;
341 }
342 
SetFunction(ScGeneralFunction nNew)343 void ScDPSaveDimension::SetFunction(ScGeneralFunction nNew)
344 {
345     nFunction = nNew;
346 }
347 
SetUsedHierarchy(tools::Long nNew)348 void ScDPSaveDimension::SetUsedHierarchy(tools::Long nNew)
349 {
350     nUsedHierarchy = nNew;
351 }
352 
SetSubtotalName(const OUString & rName)353 void ScDPSaveDimension::SetSubtotalName(const OUString& rName)
354 {
355     mpSubtotalName = rName;
356 }
357 
GetSubtotalName() const358 const std::optional<OUString> & ScDPSaveDimension::GetSubtotalName() const
359 {
360     return mpSubtotalName;
361 }
362 
RemoveSubtotalName()363 void ScDPSaveDimension::RemoveSubtotalName()
364 {
365     mpSubtotalName.reset();
366 }
367 
IsMemberNameInUse(const OUString & rName) const368 bool ScDPSaveDimension::IsMemberNameInUse(const OUString& rName) const
369 {
370     return std::any_of(maMemberList.begin(), maMemberList.end(), [&rName](const ScDPSaveMember* pMem) {
371         if (rName.equalsIgnoreAsciiCase(pMem->GetName()))
372             return true;
373 
374         const std::optional<OUString> & pLayoutName = pMem->GetLayoutName();
375         return pLayoutName && rName.equalsIgnoreAsciiCase(*pLayoutName);
376     });
377 }
378 
SetLayoutName(const OUString & rName)379 void ScDPSaveDimension::SetLayoutName(const OUString& rName)
380 {
381     mpLayoutName = rName;
382 }
383 
GetLayoutName() const384 const std::optional<OUString> & ScDPSaveDimension::GetLayoutName() const
385 {
386     return mpLayoutName;
387 }
388 
RemoveLayoutName()389 void ScDPSaveDimension::RemoveLayoutName()
390 {
391     mpLayoutName.reset();
392 }
393 
SetReferenceValue(const sheet::DataPilotFieldReference * pNew)394 void ScDPSaveDimension::SetReferenceValue(const sheet::DataPilotFieldReference* pNew)
395 {
396     if (pNew)
397         pReferenceValue.reset( new sheet::DataPilotFieldReference(*pNew) );
398     else
399         pReferenceValue.reset();
400 }
401 
SetSortInfo(const sheet::DataPilotFieldSortInfo * pNew)402 void ScDPSaveDimension::SetSortInfo(const sheet::DataPilotFieldSortInfo* pNew)
403 {
404     if (pNew)
405         pSortInfo.reset( new sheet::DataPilotFieldSortInfo(*pNew) );
406     else
407         pSortInfo.reset();
408 }
409 
SetAutoShowInfo(const sheet::DataPilotFieldAutoShowInfo * pNew)410 void ScDPSaveDimension::SetAutoShowInfo(const sheet::DataPilotFieldAutoShowInfo* pNew)
411 {
412     if (pNew)
413         pAutoShowInfo.reset( new sheet::DataPilotFieldAutoShowInfo(*pNew) );
414     else
415         pAutoShowInfo.reset();
416 }
417 
SetLayoutInfo(const sheet::DataPilotFieldLayoutInfo * pNew)418 void ScDPSaveDimension::SetLayoutInfo(const sheet::DataPilotFieldLayoutInfo* pNew)
419 {
420     if (pNew)
421         pLayoutInfo.reset( new sheet::DataPilotFieldLayoutInfo(*pNew) );
422     else
423         pLayoutInfo.reset();
424 }
425 
SetCurrentPage(const OUString * pPage)426 void ScDPSaveDimension::SetCurrentPage( const OUString* pPage )
427 {
428     // We use member's visibility attribute to filter by page dimension.
429 
430     // pPage == nullptr -> all members visible.
431     for (ScDPSaveMember* pMem : maMemberList)
432     {
433         bool bVisible = !pPage || pMem->GetName() == *pPage;
434         pMem->SetIsVisible(bVisible);
435     }
436 }
437 
GetCurrentPage() const438 OUString ScDPSaveDimension::GetCurrentPage() const
439 {
440     MemberList::const_iterator it = std::find_if(maMemberList.begin(), maMemberList.end(),
441         [](const ScDPSaveMember* pMem) { return pMem->GetIsVisible(); });
442     if (it != maMemberList.end())
443         return (*it)->GetName();
444 
445     return OUString();
446 }
447 
GetExistingMemberByName(const OUString & rName)448 ScDPSaveMember* ScDPSaveDimension::GetExistingMemberByName(const OUString& rName)
449 {
450     auto res = maMemberHash.find (rName);
451     if (res != maMemberHash.end())
452         return res->second.get();
453     return nullptr;
454 }
455 
GetMemberByName(const OUString & rName)456 ScDPSaveMember* ScDPSaveDimension::GetMemberByName(const OUString& rName)
457 {
458     auto res = maMemberHash.find (rName);
459     if (res != maMemberHash.end())
460         return res->second.get();
461 
462     ScDPSaveMember* pNew = new ScDPSaveMember( rName );
463     maMemberHash[rName] = std::unique_ptr<ScDPSaveMember>(pNew);
464     maMemberList.push_back( pNew );
465     return pNew;
466 }
467 
SetMemberPosition(const OUString & rName,sal_Int32 nNewPos)468 void ScDPSaveDimension::SetMemberPosition( const OUString& rName, sal_Int32 nNewPos )
469 {
470     ScDPSaveMember* pMember = GetMemberByName( rName ); // make sure it exists and is in the hash
471 
472     maMemberList.erase(std::remove( maMemberList.begin(), maMemberList.end(), pMember), maMemberList.end() );
473 
474     maMemberList.insert( maMemberList.begin() + nNewPos, pMember );
475 }
476 
WriteToSource(const uno::Reference<uno::XInterface> & xDim)477 void ScDPSaveDimension::WriteToSource( const uno::Reference<uno::XInterface>& xDim )
478 {
479     uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
480     OSL_ENSURE( xDimProp.is(), "no properties at dimension" );
481     if ( xDimProp.is() )
482     {
483         // exceptions are caught at ScDPSaveData::WriteToSource
484 
485         sheet::DataPilotFieldOrientation eOrient = nOrientation;
486         xDimProp->setPropertyValue( SC_UNO_DP_ORIENTATION, uno::Any(eOrient) );
487 
488         sal_Int16 eFunc = static_cast<sal_Int16>(nFunction);
489         xDimProp->setPropertyValue( SC_UNO_DP_FUNCTION2, uno::Any(eFunc) );
490 
491         if ( nUsedHierarchy >= 0 )
492         {
493             xDimProp->setPropertyValue( SC_UNO_DP_USEDHIERARCHY, uno::Any(static_cast<sal_Int32>(nUsedHierarchy)) );
494         }
495 
496         if ( pReferenceValue )
497         {
498             ;
499             xDimProp->setPropertyValue( SC_UNO_DP_REFVALUE, uno::Any(*pReferenceValue) );
500         }
501 
502         if (mpLayoutName)
503             ScUnoHelpFunctions::SetOptionalPropertyValue(xDimProp, SC_UNO_DP_LAYOUTNAME, *mpLayoutName);
504 
505         const std::optional<OUString> & pSubTotalName = GetSubtotalName();
506         if (pSubTotalName)
507             // Custom subtotal name, with '?' being replaced by the visible field name later.
508             ScUnoHelpFunctions::SetOptionalPropertyValue(xDimProp, SC_UNO_DP_FIELD_SUBTOTALNAME, *pSubTotalName);
509     }
510 
511     // Level loop outside of maMemberList loop
512     // because SubTotals have to be set independently of known members
513 
514     tools::Long nCount = maMemberHash.size();
515 
516     tools::Long nHierCount = 0;
517     uno::Reference<container::XIndexAccess> xHiers;
518     uno::Reference<sheet::XHierarchiesSupplier> xHierSupp( xDim, uno::UNO_QUERY );
519     if ( xHierSupp.is() )
520     {
521         uno::Reference<container::XNameAccess> xHiersName = xHierSupp->getHierarchies();
522         xHiers = new ScNameToIndexAccess( xHiersName );
523         nHierCount = xHiers->getCount();
524     }
525 
526     bool bHasHiddenMember = false;
527 
528     for (tools::Long nHier=0; nHier<nHierCount; nHier++)
529     {
530         tools::Long nLevCount = 0;
531         uno::Reference<container::XIndexAccess> xLevels;
532         uno::Reference<sheet::XLevelsSupplier> xLevSupp(xHiers->getByIndex(nHier), uno::UNO_QUERY);
533         if ( xLevSupp.is() )
534         {
535             uno::Reference<container::XNameAccess> xLevelsName = xLevSupp->getLevels();
536             xLevels = new ScNameToIndexAccess( xLevelsName );
537             nLevCount = xLevels->getCount();
538         }
539 
540         for (tools::Long nLev=0; nLev<nLevCount; nLev++)
541         {
542             uno::Reference<uno::XInterface> xLevel(xLevels->getByIndex(nLev), uno::UNO_QUERY);
543             uno::Reference<beans::XPropertySet> xLevProp( xLevel, uno::UNO_QUERY );
544             OSL_ENSURE( xLevProp.is(), "no properties at level" );
545             if ( xLevProp.is() )
546             {
547                 if ( !bSubTotalDefault )
548                 {
549                     uno::Sequence<sal_Int16> aSeq(maSubTotalFuncs.size());
550                     for(size_t i = 0; i < maSubTotalFuncs.size(); ++i)
551                         aSeq.getArray()[i] = static_cast<sal_Int16>(maSubTotalFuncs[i]);
552                     xLevProp->setPropertyValue( SC_UNO_DP_SUBTOTAL2, uno::Any(aSeq) );
553                 }
554                 if ( nShowEmptyMode != SC_DPSAVEMODE_DONTKNOW )
555                     lcl_SetBoolProperty( xLevProp,
556                         SC_UNO_DP_SHOWEMPTY, static_cast<bool>(nShowEmptyMode) );
557 
558                 lcl_SetBoolProperty( xLevProp,
559                     SC_UNO_DP_REPEATITEMLABELS, bRepeatItemLabels );
560 
561                 if ( pSortInfo )
562                     ScUnoHelpFunctions::SetOptionalPropertyValue(xLevProp, SC_UNO_DP_SORTING, *pSortInfo);
563 
564                 if ( pAutoShowInfo )
565                     ScUnoHelpFunctions::SetOptionalPropertyValue(xLevProp, SC_UNO_DP_AUTOSHOW, *pAutoShowInfo);
566 
567                 if ( pLayoutInfo )
568                     ScUnoHelpFunctions::SetOptionalPropertyValue(xLevProp, SC_UNO_DP_LAYOUT, *pLayoutInfo);
569 
570                 // exceptions are caught at ScDPSaveData::WriteToSource
571             }
572 
573             if ( nCount > 0 )
574             {
575                 uno::Reference<sheet::XMembersSupplier> xMembSupp( xLevel, uno::UNO_QUERY );
576                 if ( xMembSupp.is() )
577                 {
578                     uno::Reference<sheet::XMembersAccess> xMembers = xMembSupp->getMembers();
579                     if ( xMembers.is() )
580                     {
581                         sal_Int32 nPosition = -1; // set position only in manual mode
582                         if ( !pSortInfo || pSortInfo->Mode == sheet::DataPilotFieldSortMode::MANUAL )
583                             nPosition = 0;
584 
585                         for (ScDPSaveMember* pMember : maMemberList)
586                         {
587                             if (!pMember->GetIsVisible())
588                                 bHasHiddenMember = true;
589                             OUString aMemberName = pMember->GetName();
590                             if ( xMembers->hasByName( aMemberName ) )
591                             {
592                                 uno::Reference<uno::XInterface> xMemberInt(
593                                     xMembers->getByName(aMemberName), uno::UNO_QUERY);
594                                 pMember->WriteToSource( xMemberInt, nPosition );
595 
596                                 if ( nPosition >= 0 )
597                                     ++nPosition; // increase if initialized
598                             }
599                             // missing member is no error
600                         }
601                     }
602                 }
603             }
604         }
605     }
606 
607     if (xDimProp.is())
608         ScUnoHelpFunctions::SetOptionalPropertyValue(xDimProp, SC_UNO_DP_HAS_HIDDEN_MEMBER, bHasHiddenMember);
609 }
610 
UpdateMemberVisibility(const std::unordered_map<OUString,bool> & rData)611 void ScDPSaveDimension::UpdateMemberVisibility(const std::unordered_map<OUString, bool>& rData)
612 {
613     for (ScDPSaveMember* pMem : maMemberList)
614     {
615         const OUString& rMemName = pMem->GetName();
616         auto itr = rData.find(rMemName);
617         if (itr != rData.end())
618             pMem->SetIsVisible(itr->second);
619     }
620 }
621 
HasInvisibleMember() const622 bool ScDPSaveDimension::HasInvisibleMember() const
623 {
624     return std::any_of(maMemberList.begin(), maMemberList.end(),
625         [](const ScDPSaveMember* pMem) { return !pMem->GetIsVisible(); });
626 }
627 
RemoveObsoleteMembers(const MemberSetType & rMembers)628 void ScDPSaveDimension::RemoveObsoleteMembers(const MemberSetType& rMembers)
629 {
630     MemberList aNew;
631     for (ScDPSaveMember* pMem : maMemberList)
632     {
633         if (rMembers.count(pMem->GetName()))
634         {
635             // This member still exists.
636             aNew.push_back(pMem);
637         }
638         else
639         {
640             maMemberHash.erase(pMem->GetName());
641         }
642     }
643 
644     maMemberList.swap(aNew);
645 }
646 
647 #if DUMP_PIVOT_TABLE
648 
Dump(int nIndent) const649 void ScDPSaveDimension::Dump(int nIndent) const
650 {
651     static const char* pOrientNames[] = { "hidden", "column", "row", "page", "data" };
652     std::string aIndent(nIndent*4, ' ');
653 
654     cout << aIndent << "* dimension name: '" << aName << "'" << endl;
655 
656     cout << aIndent << "    + orientation: ";
657     if (nOrientation <= DataPilotFieldOrientation_DATA)
658         cout << pOrientNames[static_cast<int>(nOrientation)];
659     else
660         cout << "(invalid)";
661     cout << endl;
662 
663     cout << aIndent << "    + layout name: ";
664     if (mpLayoutName)
665         cout << "'" << *mpLayoutName << "'";
666     else
667         cout << "(none)";
668     cout << endl;
669 
670     cout << aIndent << "    + subtotal name: ";
671     if (mpSubtotalName)
672         cout << "'" << *mpSubtotalName << "'";
673     else
674         cout << "(none)";
675     cout << endl;
676 
677     cout << aIndent << "    + is data layout: " << (bIsDataLayout ? "yes" : "no") << endl;
678     cout << aIndent << "    + is duplicate: " << (bDupFlag ? "yes" : "no") << endl;
679 
680     for (ScDPSaveMember* pMem : maMemberList)
681     {
682         pMem->Dump(nIndent+1);
683     }
684 
685     cout << endl; // blank line
686 }
687 
688 #endif
689 
ScDPSaveData()690 ScDPSaveData::ScDPSaveData() :
691     nColumnGrandMode( SC_DPSAVEMODE_DONTKNOW ),
692     nRowGrandMode( SC_DPSAVEMODE_DONTKNOW ),
693     nIgnoreEmptyMode( SC_DPSAVEMODE_DONTKNOW ),
694     nRepeatEmptyMode( SC_DPSAVEMODE_DONTKNOW ),
695     bFilterButton( true ),
696     bDrillDown( true ),
697     mbDimensionMembersBuilt(false)
698 {
699 }
700 
ScDPSaveData(const ScDPSaveData & r)701 ScDPSaveData::ScDPSaveData(const ScDPSaveData& r) :
702     nColumnGrandMode( r.nColumnGrandMode ),
703     nRowGrandMode( r.nRowGrandMode ),
704     nIgnoreEmptyMode( r.nIgnoreEmptyMode ),
705     nRepeatEmptyMode( r.nRepeatEmptyMode ),
706     bFilterButton( r.bFilterButton ),
707     bDrillDown( r.bDrillDown ),
708     mbDimensionMembersBuilt(r.mbDimensionMembersBuilt),
709     mpGrandTotalName(r.mpGrandTotalName)
710 {
711     if ( r.pDimensionData )
712         pDimensionData.reset( new ScDPDimensionSaveData( *r.pDimensionData ) );
713 
714     for (auto const& it : r.m_DimList)
715     {
716         m_DimList.push_back(std::make_unique<ScDPSaveDimension>(*it));
717     }
718 }
719 
operator =(const ScDPSaveData & r)720 ScDPSaveData& ScDPSaveData::operator= ( const ScDPSaveData& r )
721 {
722     if ( &r != this )
723     {
724         this->~ScDPSaveData();
725         new( this ) ScDPSaveData ( r );
726     }
727     return *this;
728 }
729 
operator ==(const ScDPSaveData & r) const730 bool ScDPSaveData::operator== ( const ScDPSaveData& r ) const
731 {
732     if ( nColumnGrandMode != r.nColumnGrandMode ||
733          nRowGrandMode    != r.nRowGrandMode    ||
734          nIgnoreEmptyMode != r.nIgnoreEmptyMode ||
735          nRepeatEmptyMode != r.nRepeatEmptyMode ||
736          bFilterButton    != r.bFilterButton    ||
737          bDrillDown       != r.bDrillDown       ||
738          mbDimensionMembersBuilt != r.mbDimensionMembersBuilt)
739         return false;
740 
741     if ( pDimensionData || r.pDimensionData )
742         if ( !pDimensionData || !r.pDimensionData || !( *pDimensionData == *r.pDimensionData ) )
743             return false;
744 
745     if (!(::comphelper::ContainerUniquePtrEquals(m_DimList, r.m_DimList)))
746         return false;
747 
748     if (mpGrandTotalName)
749     {
750         if (!r.mpGrandTotalName)
751             return false;
752         if (*mpGrandTotalName != *r.mpGrandTotalName)
753             return false;
754     }
755     else if (r.mpGrandTotalName)
756         return false;
757 
758     return true;
759 }
760 
~ScDPSaveData()761 ScDPSaveData::~ScDPSaveData()
762 {
763 }
764 
SetGrandTotalName(const OUString & rName)765 void ScDPSaveData::SetGrandTotalName(const OUString& rName)
766 {
767     mpGrandTotalName = rName;
768 }
769 
GetGrandTotalName() const770 const std::optional<OUString> & ScDPSaveData::GetGrandTotalName() const
771 {
772     return mpGrandTotalName;
773 }
774 
775 namespace {
776 
777 class DimOrderInserter
778 {
779     ScDPSaveData::DimOrderType& mrNames;
780 public:
DimOrderInserter(ScDPSaveData::DimOrderType & rNames)781     explicit DimOrderInserter(ScDPSaveData::DimOrderType& rNames) : mrNames(rNames) {}
782 
operator ()(const ScDPSaveDimension * pDim)783     void operator() (const ScDPSaveDimension* pDim)
784     {
785         size_t nRank = mrNames.size();
786         mrNames.emplace(pDim->GetName(), nRank);
787     }
788 };
789 
790 }
791 
GetDimensionSortOrder() const792 const ScDPSaveData::DimOrderType& ScDPSaveData::GetDimensionSortOrder() const
793 {
794     if (!mpDimOrder)
795     {
796         mpDimOrder.reset(new DimOrderType);
797         std::vector<const ScDPSaveDimension*> aRowDims, aColDims;
798         GetAllDimensionsByOrientation(sheet::DataPilotFieldOrientation_ROW, aRowDims);
799         GetAllDimensionsByOrientation(sheet::DataPilotFieldOrientation_COLUMN, aColDims);
800 
801         std::for_each(aRowDims.begin(), aRowDims.end(), DimOrderInserter(*mpDimOrder));
802         std::for_each(aColDims.begin(), aColDims.end(), DimOrderInserter(*mpDimOrder));
803     }
804     return *mpDimOrder;
805 }
806 
GetAllDimensionsByOrientation(sheet::DataPilotFieldOrientation eOrientation,std::vector<const ScDPSaveDimension * > & rDims) const807 void ScDPSaveData::GetAllDimensionsByOrientation(
808     sheet::DataPilotFieldOrientation eOrientation, std::vector<const ScDPSaveDimension*>& rDims) const
809 {
810     std::vector<const ScDPSaveDimension*> aDims;
811     for (auto const& it : m_DimList)
812     {
813         const ScDPSaveDimension& rDim = *it;
814         if (rDim.GetOrientation() != eOrientation)
815             continue;
816 
817         aDims.push_back(&rDim);
818     }
819 
820     rDims.swap(aDims);
821 }
822 
AddDimension(ScDPSaveDimension * pDim)823 void ScDPSaveData::AddDimension(ScDPSaveDimension* pDim)
824 {
825     if (!pDim)
826         return;
827 
828     CheckDuplicateName(*pDim);
829     m_DimList.push_back(std::unique_ptr<ScDPSaveDimension>(pDim));
830 
831     DimensionsChanged();
832 }
833 
GetDimensionByName(const OUString & rName)834 ScDPSaveDimension* ScDPSaveData::GetDimensionByName(const OUString& rName)
835 {
836     for (auto const& iter : m_DimList)
837     {
838         if (iter->GetName() == rName && !iter->IsDataLayout() )
839             return &(*iter);
840     }
841 
842     return AppendNewDimension(rName, false);
843 }
844 
GetExistingDimensionByName(std::u16string_view rName) const845 ScDPSaveDimension* ScDPSaveData::GetExistingDimensionByName(std::u16string_view rName) const
846 {
847     for (auto const& iter : m_DimList)
848     {
849         if (iter->GetName() == rName && !iter->IsDataLayout() )
850             return &(*iter);
851     }
852     return nullptr; // don't create new
853 }
854 
GetNewDimensionByName(const OUString & rName)855 ScDPSaveDimension* ScDPSaveData::GetNewDimensionByName(const OUString& rName)
856 {
857     for (auto const& iter : m_DimList)
858     {
859         if (iter->GetName() == rName && !iter->IsDataLayout() )
860             return DuplicateDimension(rName);
861     }
862 
863     return AppendNewDimension(rName, false);
864 }
865 
GetDataLayoutDimension()866 ScDPSaveDimension* ScDPSaveData::GetDataLayoutDimension()
867 {
868     ScDPSaveDimension* pDim = GetExistingDataLayoutDimension();
869     if (pDim)
870         return pDim;
871 
872     return AppendNewDimension(OUString(), true);
873 }
874 
GetExistingDataLayoutDimension() const875 ScDPSaveDimension* ScDPSaveData::GetExistingDataLayoutDimension() const
876 {
877     for (auto const& iter : m_DimList)
878     {
879         if ( iter->IsDataLayout() )
880             return &(*iter);
881     }
882     return nullptr;
883 }
884 
DuplicateDimension(std::u16string_view rName)885 ScDPSaveDimension* ScDPSaveData::DuplicateDimension(std::u16string_view rName)
886 {
887     // always insert new
888 
889     ScDPSaveDimension* pOld = GetExistingDimensionByName(rName);
890     if (!pOld)
891         return nullptr;
892 
893     ScDPSaveDimension* pNew = new ScDPSaveDimension( *pOld );
894     AddDimension(pNew);
895     return pNew;
896 }
897 
RemoveDimensionByName(const OUString & rName)898 void ScDPSaveData::RemoveDimensionByName(const OUString& rName)
899 {
900     auto iter = std::find_if(m_DimList.begin(), m_DimList.end(),
901         [&rName](const std::unique_ptr<ScDPSaveDimension>& rxDim) {
902             return rxDim->GetName() == rName && !rxDim->IsDataLayout(); });
903     if (iter != m_DimList.end())
904     {
905         m_DimList.erase(iter);
906         RemoveDuplicateNameCount(rName);
907         DimensionsChanged();
908     }
909 }
910 
DuplicateDimension(const ScDPSaveDimension & rDim)911 ScDPSaveDimension& ScDPSaveData::DuplicateDimension( const ScDPSaveDimension& rDim )
912 {
913     ScDPSaveDimension* pNew = new ScDPSaveDimension( rDim );
914     AddDimension(pNew);
915     return *pNew;
916 }
917 
GetInnermostDimension(DataPilotFieldOrientation nOrientation)918 ScDPSaveDimension* ScDPSaveData::GetInnermostDimension(DataPilotFieldOrientation nOrientation)
919 {
920     // return the innermost dimension for the given orientation,
921     // excluding data layout dimension
922 
923     auto iter = std::find_if(m_DimList.rbegin(), m_DimList.rend(),
924         [&nOrientation](const std::unique_ptr<ScDPSaveDimension>& rxDim) {
925             return rxDim->GetOrientation() == nOrientation && !rxDim->IsDataLayout(); });
926     if (iter != m_DimList.rend())
927         return iter->get();
928 
929     return nullptr;
930 }
931 
GetFirstDimension(sheet::DataPilotFieldOrientation eOrientation)932 ScDPSaveDimension* ScDPSaveData::GetFirstDimension(sheet::DataPilotFieldOrientation eOrientation)
933 {
934     for (auto const& iter : m_DimList)
935     {
936         if (iter->GetOrientation() == eOrientation && !iter->IsDataLayout())
937             return &(*iter);
938     }
939     return nullptr;
940 }
941 
GetDataDimensionCount() const942 tools::Long ScDPSaveData::GetDataDimensionCount() const
943 {
944     tools::Long nDataCount = 0;
945 
946     for (auto const& iter : m_DimList)
947     {
948         if (iter->GetOrientation() == sheet::DataPilotFieldOrientation_DATA)
949             ++nDataCount;
950     }
951 
952     return nDataCount;
953 }
954 
SetPosition(ScDPSaveDimension * pDim,tools::Long nNew)955 void ScDPSaveData::SetPosition( ScDPSaveDimension* pDim, tools::Long nNew )
956 {
957     // position (nNew) is counted within dimensions of the same orientation
958 
959     DataPilotFieldOrientation nOrient = pDim->GetOrientation();
960 
961     auto it = std::find_if(m_DimList.begin(), m_DimList.end(),
962         [&pDim](const std::unique_ptr<ScDPSaveDimension>& rxDim) { return pDim == rxDim.get(); });
963     if (it != m_DimList.end())
964     {
965         // Tell vector<unique_ptr> to give up ownership of this element.
966         // Don't delete this instance as it is re-inserted into the
967         // container later.
968         it->release();
969         m_DimList.erase(it);
970     }
971 
972     auto iterInsert = std::find_if(m_DimList.begin(), m_DimList.end(),
973         [&nOrient, &nNew](const std::unique_ptr<ScDPSaveDimension>& rxDim) {
974             if (rxDim->GetOrientation() == nOrient )
975                 --nNew;
976             return nNew <= 0;
977         });
978 
979     m_DimList.insert(iterInsert, std::unique_ptr<ScDPSaveDimension>(pDim));
980     DimensionsChanged();
981 }
982 
SetColumnGrand(bool bSet)983 void ScDPSaveData::SetColumnGrand(bool bSet)
984 {
985     nColumnGrandMode = sal_uInt16(bSet);
986 }
987 
SetRowGrand(bool bSet)988 void ScDPSaveData::SetRowGrand(bool bSet)
989 {
990     nRowGrandMode = sal_uInt16(bSet);
991 }
992 
SetIgnoreEmptyRows(bool bSet)993 void ScDPSaveData::SetIgnoreEmptyRows(bool bSet)
994 {
995     nIgnoreEmptyMode = sal_uInt16(bSet);
996 }
997 
SetRepeatIfEmpty(bool bSet)998 void ScDPSaveData::SetRepeatIfEmpty(bool bSet)
999 {
1000     nRepeatEmptyMode = sal_uInt16(bSet);
1001 }
1002 
SetFilterButton(bool bSet)1003 void ScDPSaveData::SetFilterButton(bool bSet)
1004 {
1005     bFilterButton = bSet;
1006 }
1007 
SetDrillDown(bool bSet)1008 void ScDPSaveData::SetDrillDown(bool bSet)
1009 {
1010     bDrillDown = bSet;
1011 }
1012 
lcl_ResetOrient(const uno::Reference<sheet::XDimensionsSupplier> & xSource)1013 static void lcl_ResetOrient( const uno::Reference<sheet::XDimensionsSupplier>& xSource )
1014 {
1015     uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions();
1016     uno::Reference<container::XIndexAccess> xIntDims = new ScNameToIndexAccess( xDimsName );
1017     tools::Long nIntCount = xIntDims->getCount();
1018     for (tools::Long nIntDim=0; nIntDim<nIntCount; nIntDim++)
1019     {
1020         uno::Reference<beans::XPropertySet> xDimProp(xIntDims->getByIndex(nIntDim), uno::UNO_QUERY);
1021         if (xDimProp.is())
1022         {
1023             xDimProp->setPropertyValue( SC_UNO_DP_ORIENTATION, uno::Any(sheet::DataPilotFieldOrientation_HIDDEN) );
1024         }
1025     }
1026 }
1027 
WriteToSource(const uno::Reference<sheet::XDimensionsSupplier> & xSource)1028 void ScDPSaveData::WriteToSource( const uno::Reference<sheet::XDimensionsSupplier>& xSource )
1029 {
1030     if (!xSource.is())
1031         return;
1032 
1033     // source options must be first!
1034 
1035     uno::Reference<beans::XPropertySet> xSourceProp( xSource, uno::UNO_QUERY );
1036     SAL_WARN_IF( !xSourceProp.is(), "sc.core", "no properties at source" );
1037     if ( xSourceProp.is() )
1038     {
1039         // source options are not available for external sources
1040         //TODO: use XPropertySetInfo to test for availability?
1041 
1042         try
1043         {
1044             if ( nIgnoreEmptyMode != SC_DPSAVEMODE_DONTKNOW )
1045                 lcl_SetBoolProperty( xSourceProp,
1046                     SC_UNO_DP_IGNOREEMPTY, static_cast<bool>(nIgnoreEmptyMode) );
1047             if ( nRepeatEmptyMode != SC_DPSAVEMODE_DONTKNOW )
1048                 lcl_SetBoolProperty( xSourceProp,
1049                     SC_UNO_DP_REPEATEMPTY, static_cast<bool>(nRepeatEmptyMode) );
1050         }
1051         catch(uno::Exception&)
1052         {
1053             // no error
1054         }
1055 
1056         const std::optional<OUString> & pGrandTotalName = GetGrandTotalName();
1057         if (pGrandTotalName)
1058             ScUnoHelpFunctions::SetOptionalPropertyValue(xSourceProp, SC_UNO_DP_GRANDTOTAL_NAME, *pGrandTotalName);
1059     }
1060 
1061     // exceptions in the other calls are errors
1062     try
1063     {
1064         // reset all orientations
1065         //TODO: "forgetSettings" or similar at source ?????
1066         //TODO: reset all duplicated dimensions, or reuse them below !!!
1067         SAL_INFO("sc.core", "ScDPSaveData::WriteToSource");
1068 
1069         lcl_ResetOrient( xSource );
1070 
1071         uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions();
1072         uno::Reference<container::XIndexAccess> xIntDims = new ScNameToIndexAccess( xDimsName );
1073         tools::Long nIntCount = xIntDims->getCount();
1074 
1075         for (const auto& rxDim : m_DimList)
1076         {
1077             OUString aName = rxDim->GetName();
1078             OUString aCoreName = ScDPUtil::getSourceDimensionName(aName);
1079 
1080             SAL_INFO("sc.core", aName);
1081 
1082             bool bData = rxDim->IsDataLayout();
1083 
1084             //TODO: getByName for ScDPSource, including DataLayoutDimension !!!!!!!!
1085 
1086             bool bFound = false;
1087             for (tools::Long nIntDim=0; nIntDim<nIntCount && !bFound; nIntDim++)
1088             {
1089                 uno::Reference<uno::XInterface> xIntDim(xIntDims->getByIndex(nIntDim),
1090                                                         uno::UNO_QUERY);
1091                 if ( bData )
1092                 {
1093                     uno::Reference<beans::XPropertySet> xDimProp( xIntDim, uno::UNO_QUERY );
1094                     if ( xDimProp.is() )
1095                     {
1096                         bFound = ScUnoHelpFunctions::GetBoolProperty( xDimProp,
1097                                     SC_UNO_DP_ISDATALAYOUT );
1098                         //TODO: error checking -- is "IsDataLayoutDimension" property required??
1099                     }
1100                 }
1101                 else
1102                 {
1103                     uno::Reference<container::XNamed> xDimName( xIntDim, uno::UNO_QUERY );
1104                     if (xDimName.is() && xDimName->getName() == aCoreName)
1105                         bFound = true;
1106                 }
1107 
1108                 if (bFound)
1109                 {
1110                     if (rxDim->GetDupFlag())
1111                     {
1112                         uno::Reference<util::XCloneable> xCloneable(xIntDim, uno::UNO_QUERY);
1113                         SAL_WARN_IF(!xCloneable.is(), "sc.core", "cannot clone dimension");
1114                         if (xCloneable.is())
1115                         {
1116                             uno::Reference<util::XCloneable> xNew = xCloneable->createClone();
1117                             uno::Reference<container::XNamed> xNewName(xNew, uno::UNO_QUERY);
1118                             if (xNewName.is())
1119                             {
1120                                 xNewName->setName(aName);
1121                                 rxDim->WriteToSource(xNew);
1122                             }
1123                         }
1124                     }
1125                     else
1126                         rxDim->WriteToSource( xIntDim );
1127                 }
1128             }
1129             SAL_WARN_IF(!bFound, "sc.core", "WriteToSource: Dimension not found: " + aName + ".");
1130         }
1131 
1132         if ( xSourceProp.is() )
1133         {
1134             if ( nColumnGrandMode != SC_DPSAVEMODE_DONTKNOW )
1135                 lcl_SetBoolProperty( xSourceProp,
1136                     SC_UNO_DP_COLGRAND, static_cast<bool>(nColumnGrandMode) );
1137             if ( nRowGrandMode != SC_DPSAVEMODE_DONTKNOW )
1138                 lcl_SetBoolProperty( xSourceProp,
1139                     SC_UNO_DP_ROWGRAND, static_cast<bool>(nRowGrandMode) );
1140         }
1141     }
1142     catch(uno::Exception const &)
1143     {
1144         TOOLS_WARN_EXCEPTION("sc.core", "WriteToSource");
1145     }
1146 }
1147 
IsEmpty() const1148 bool ScDPSaveData::IsEmpty() const
1149 {
1150     for (auto const& iter : m_DimList)
1151     {
1152         if (iter->GetOrientation() != sheet::DataPilotFieldOrientation_HIDDEN && !iter->IsDataLayout())
1153             return false;
1154     }
1155     return true; // no entries that are not hidden
1156 }
1157 
RemoveAllGroupDimensions(const OUString & rSrcDimName,std::vector<OUString> * pDeletedNames)1158 void ScDPSaveData::RemoveAllGroupDimensions( const OUString& rSrcDimName, std::vector<OUString>* pDeletedNames )
1159 {
1160     if (!pDimensionData)
1161         // No group dimensions exist. Nothing to do.
1162         return;
1163 
1164     // Remove numeric group dimension (exists once at most). No need to delete
1165     // anything in save data (grouping was done inplace in an existing base
1166     // dimension).
1167     pDimensionData->RemoveNumGroupDimension(rSrcDimName);
1168 
1169     // Remove named group dimension(s). Dimensions have to be removed from
1170     // dimension save data and from save data too.
1171     const ScDPSaveGroupDimension* pExistingGroup = pDimensionData->GetGroupDimForBase(rSrcDimName);
1172     while ( pExistingGroup )
1173     {
1174         OUString aGroupDimName = pExistingGroup->GetGroupDimName();
1175         pDimensionData->RemoveGroupDimension(aGroupDimName);     // pExistingGroup is deleted
1176 
1177         // also remove SaveData settings for the dimension that no longer exists
1178         RemoveDimensionByName(aGroupDimName);
1179 
1180         if (pDeletedNames)
1181             pDeletedNames->push_back(aGroupDimName);
1182 
1183         // see if there are more group dimensions
1184         pExistingGroup = pDimensionData->GetGroupDimForBase(rSrcDimName);
1185 
1186         if ( pExistingGroup && pExistingGroup->GetGroupDimName() == aGroupDimName )
1187         {
1188             // still get the same group dimension?
1189             OSL_FAIL("couldn't remove group dimension");
1190             pExistingGroup = nullptr;      // avoid endless loop
1191         }
1192     }
1193 }
1194 
GetDimensionData()1195 ScDPDimensionSaveData* ScDPSaveData::GetDimensionData()
1196 {
1197     if (!pDimensionData)
1198         pDimensionData.reset( new ScDPDimensionSaveData );
1199     return pDimensionData.get();
1200 }
1201 
SetDimensionData(const ScDPDimensionSaveData * pNew)1202 void ScDPSaveData::SetDimensionData( const ScDPDimensionSaveData* pNew )
1203 {
1204     if ( pNew )
1205         pDimensionData.reset( new ScDPDimensionSaveData( *pNew ) );
1206     else
1207         pDimensionData.reset();
1208 }
1209 
BuildAllDimensionMembers(ScDPTableData * pData)1210 void ScDPSaveData::BuildAllDimensionMembers(ScDPTableData* pData)
1211 {
1212     if (mbDimensionMembersBuilt)
1213         return;
1214 
1215     // First, build a dimension name-to-index map.
1216     typedef std::unordered_map<OUString, tools::Long> NameIndexMap;
1217     NameIndexMap aMap;
1218     tools::Long nColCount = pData->GetColumnCount();
1219     for (tools::Long i = 0; i < nColCount; ++i)
1220         aMap.emplace(pData->getDimensionName(i), i);
1221 
1222     NameIndexMap::const_iterator itrEnd = aMap.end();
1223 
1224     for (auto const& iter : m_DimList)
1225     {
1226         const OUString& rDimName = iter->GetName();
1227         if (rDimName.isEmpty())
1228             // empty dimension name. It must be data layout.
1229             continue;
1230 
1231         NameIndexMap::const_iterator itr = aMap.find(rDimName);
1232         if (itr == itrEnd)
1233             // dimension name not in the data. This should never happen!
1234             continue;
1235 
1236         tools::Long nDimIndex = itr->second;
1237         const std::vector<SCROW>& rMembers = pData->GetColumnEntries(nDimIndex);
1238         size_t nMemberCount = rMembers.size();
1239         for (size_t j = 0; j < nMemberCount; ++j)
1240         {
1241             const ScDPItemData* pMemberData = pData->GetMemberById( nDimIndex, rMembers[j] );
1242             OUString aMemName = pData->GetFormattedString(nDimIndex, *pMemberData, false);
1243             if (iter->GetExistingMemberByName(aMemName))
1244                 // this member instance already exists. nothing to do.
1245                 continue;
1246 
1247             unique_ptr<ScDPSaveMember> pNewMember(new ScDPSaveMember(aMemName));
1248             pNewMember->SetIsVisible(true);
1249             iter->AddMember(std::move(pNewMember));
1250         }
1251     }
1252 
1253     mbDimensionMembersBuilt = true;
1254 }
1255 
SyncAllDimensionMembers(ScDPTableData * pData)1256 void ScDPSaveData::SyncAllDimensionMembers(ScDPTableData* pData)
1257 {
1258     typedef std::unordered_map<OUString, tools::Long> NameIndexMap;
1259 
1260     // First, build a dimension name-to-index map.
1261     NameIndexMap aMap;
1262     tools::Long nColCount = pData->GetColumnCount();
1263     for (tools::Long i = 0; i < nColCount; ++i)
1264         aMap.emplace(pData->getDimensionName(i), i);
1265 
1266     NameIndexMap::const_iterator itMapEnd = aMap.end();
1267 
1268     for (auto const& it : m_DimList)
1269     {
1270         const OUString& rDimName = it->GetName();
1271         if (rDimName.isEmpty())
1272             // empty dimension name. It must be data layout.
1273             continue;
1274 
1275         NameIndexMap::const_iterator itMap = aMap.find(rDimName);
1276         if (itMap == itMapEnd)
1277             // dimension name not in the data. This should never happen!
1278             continue;
1279 
1280         ScDPSaveDimension::MemberSetType aMemNames;
1281         tools::Long nDimIndex = itMap->second;
1282         const std::vector<SCROW>& rMembers = pData->GetColumnEntries(nDimIndex);
1283         size_t nMemberCount = rMembers.size();
1284         for (size_t j = 0; j < nMemberCount; ++j)
1285         {
1286             const ScDPItemData* pMemberData = pData->GetMemberById(nDimIndex, rMembers[j]);
1287             OUString aMemName = pData->GetFormattedString(nDimIndex, *pMemberData, false);
1288             aMemNames.insert(aMemName);
1289         }
1290 
1291         it->RemoveObsoleteMembers(aMemNames);
1292     }
1293 }
1294 
HasInvisibleMember(std::u16string_view rDimName) const1295 bool ScDPSaveData::HasInvisibleMember(std::u16string_view rDimName) const
1296 {
1297     ScDPSaveDimension* pDim = GetExistingDimensionByName(rDimName);
1298     if (!pDim)
1299         return false;
1300 
1301     return pDim->HasInvisibleMember();
1302 }
1303 
1304 #if DUMP_PIVOT_TABLE
1305 
Dump() const1306 void ScDPSaveData::Dump() const
1307 {
1308     for (auto const& itDim : m_DimList)
1309     {
1310         const ScDPSaveDimension& rDim = *itDim;
1311         rDim.Dump();
1312     }
1313 }
1314 
1315 #endif
1316 
CheckDuplicateName(ScDPSaveDimension & rDim)1317 void ScDPSaveData::CheckDuplicateName(ScDPSaveDimension& rDim)
1318 {
1319     const OUString aName = ScDPUtil::getSourceDimensionName(rDim.GetName());
1320     DupNameCountType::iterator it = maDupNameCounts.find(aName);
1321     if (it != maDupNameCounts.end())
1322     {
1323         rDim.SetName(ScDPUtil::createDuplicateDimensionName(aName, ++it->second));
1324         rDim.SetDupFlag(true);
1325     }
1326     else
1327         // New name.
1328         maDupNameCounts.emplace(aName, 0);
1329 }
1330 
RemoveDuplicateNameCount(const OUString & rName)1331 void ScDPSaveData::RemoveDuplicateNameCount(const OUString& rName)
1332 {
1333     OUString aCoreName = rName;
1334     if (ScDPUtil::isDuplicateDimension(rName))
1335         aCoreName = ScDPUtil::getSourceDimensionName(rName);
1336 
1337     DupNameCountType::iterator it = maDupNameCounts.find(aCoreName);
1338     if (it == maDupNameCounts.end())
1339         return;
1340 
1341     if (!it->second)
1342     {
1343         maDupNameCounts.erase(it);
1344         return;
1345     }
1346 
1347     --it->second;
1348 }
1349 
AppendNewDimension(const OUString & rName,bool bDataLayout)1350 ScDPSaveDimension* ScDPSaveData::AppendNewDimension(const OUString& rName, bool bDataLayout)
1351 {
1352     if (ScDPUtil::isDuplicateDimension(rName))
1353         // This call is for original dimensions only.
1354         return nullptr;
1355 
1356     ScDPSaveDimension* pNew = new ScDPSaveDimension(rName, bDataLayout);
1357     m_DimList.push_back(std::unique_ptr<ScDPSaveDimension>(pNew));
1358     if (!maDupNameCounts.count(rName))
1359         maDupNameCounts.emplace(rName, 0);
1360 
1361     DimensionsChanged();
1362     return pNew;
1363 }
1364 
DimensionsChanged()1365 void ScDPSaveData::DimensionsChanged()
1366 {
1367     mpDimOrder.reset();
1368 }
1369 
1370 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1371