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 boost::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     {
139         // exceptions are caught at ScDPSaveData::WriteToSource
140 
141         if ( nVisibleMode != SC_DPSAVEMODE_DONTKNOW )
142             lcl_SetBoolProperty( xMembProp,
143                     SC_UNO_DP_ISVISIBLE, static_cast<bool>(nVisibleMode) );
144 
145         if ( nShowDetailsMode != SC_DPSAVEMODE_DONTKNOW )
146             lcl_SetBoolProperty( xMembProp,
147                     SC_UNO_DP_SHOWDETAILS, static_cast<bool>(nShowDetailsMode) );
148 
149         if (mpLayoutName)
150             ScUnoHelpFunctions::SetOptionalPropertyValue(xMembProp, SC_UNO_DP_LAYOUTNAME, *mpLayoutName);
151 
152         if ( nPosition >= 0 )
153             ScUnoHelpFunctions::SetOptionalPropertyValue(xMembProp, SC_UNO_DP_POSITION, nPosition);
154     }
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(long nNew)348 void ScDPSaveDimension::SetUsedHierarchy(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 boost::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 boost::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 boost::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 boost::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     long nCount = maMemberHash.size();
515 
516     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 (long nHier=0; nHier<nHierCount; nHier++)
529     {
530         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 (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     typedef std::unordered_map<OUString, bool> DataMap;
614     for (ScDPSaveMember* pMem : maMemberList)
615     {
616         const OUString& rMemName = pMem->GetName();
617         DataMap::const_iterator itr = rData.find(rMemName);
618         if (itr != rData.end())
619             pMem->SetIsVisible(itr->second);
620     }
621 }
622 
HasInvisibleMember() const623 bool ScDPSaveDimension::HasInvisibleMember() const
624 {
625     return std::any_of(maMemberList.begin(), maMemberList.end(),
626         [](const ScDPSaveMember* pMem) { return !pMem->GetIsVisible(); });
627 }
628 
RemoveObsoleteMembers(const MemberSetType & rMembers)629 void ScDPSaveDimension::RemoveObsoleteMembers(const MemberSetType& rMembers)
630 {
631     MemberList aNew;
632     for (ScDPSaveMember* pMem : maMemberList)
633     {
634         if (rMembers.count(pMem->GetName()))
635         {
636             // This member still exists.
637             aNew.push_back(pMem);
638         }
639         else
640         {
641             maMemberHash.erase(pMem->GetName());
642         }
643     }
644 
645     maMemberList.swap(aNew);
646 }
647 
648 #if DUMP_PIVOT_TABLE
649 
Dump(int nIndent) const650 void ScDPSaveDimension::Dump(int nIndent) const
651 {
652     static const char* pOrientNames[] = { "hidden", "column", "row", "page", "data" };
653     std::string aIndent(nIndent*4, ' ');
654 
655     cout << aIndent << "* dimension name: '" << aName << "'" << endl;
656 
657     cout << aIndent << "    + orientation: ";
658     if (nOrientation <= DataPilotFieldOrientation_DATA)
659         cout << pOrientNames[static_cast<int>(nOrientation)];
660     else
661         cout << "(invalid)";
662     cout << endl;
663 
664     cout << aIndent << "    + layout name: ";
665     if (mpLayoutName)
666         cout << "'" << *mpLayoutName << "'";
667     else
668         cout << "(none)";
669     cout << endl;
670 
671     cout << aIndent << "    + subtotal name: ";
672     if (mpSubtotalName)
673         cout << "'" << *mpSubtotalName << "'";
674     else
675         cout << "(none)";
676     cout << endl;
677 
678     cout << aIndent << "    + is data layout: " << (bIsDataLayout ? "yes" : "no") << endl;
679     cout << aIndent << "    + is duplicate: " << (bDupFlag ? "yes" : "no") << endl;
680 
681     for (ScDPSaveMember* pMem : maMemberList)
682     {
683         pMem->Dump(nIndent+1);
684     }
685 
686     cout << endl; // blank line
687 }
688 
689 #endif
690 
ScDPSaveData()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 ),
697     bDrillDown( true ),
698     mbDimensionMembersBuilt(false)
699 {
700 }
701 
ScDPSaveData(const ScDPSaveData & r)702 ScDPSaveData::ScDPSaveData(const ScDPSaveData& r) :
703     nColumnGrandMode( r.nColumnGrandMode ),
704     nRowGrandMode( r.nRowGrandMode ),
705     nIgnoreEmptyMode( r.nIgnoreEmptyMode ),
706     nRepeatEmptyMode( r.nRepeatEmptyMode ),
707     bFilterButton( r.bFilterButton ),
708     bDrillDown( r.bDrillDown ),
709     mbDimensionMembersBuilt(r.mbDimensionMembersBuilt),
710     mpGrandTotalName(r.mpGrandTotalName)
711 {
712     if ( r.pDimensionData )
713         pDimensionData.reset( new ScDPDimensionSaveData( *r.pDimensionData ) );
714 
715     for (auto const& it : r.m_DimList)
716     {
717         m_DimList.push_back(std::make_unique<ScDPSaveDimension>(*it));
718     }
719 }
720 
operator =(const ScDPSaveData & r)721 ScDPSaveData& ScDPSaveData::operator= ( const ScDPSaveData& r )
722 {
723     if ( &r != this )
724     {
725         this->~ScDPSaveData();
726         new( this ) ScDPSaveData ( r );
727     }
728     return *this;
729 }
730 
operator ==(const ScDPSaveData & r) const731 bool ScDPSaveData::operator== ( const ScDPSaveData& r ) const
732 {
733     if ( nColumnGrandMode != r.nColumnGrandMode ||
734          nRowGrandMode    != r.nRowGrandMode    ||
735          nIgnoreEmptyMode != r.nIgnoreEmptyMode ||
736          nRepeatEmptyMode != r.nRepeatEmptyMode ||
737          bFilterButton    != r.bFilterButton    ||
738          bDrillDown       != r.bDrillDown       ||
739          mbDimensionMembersBuilt != r.mbDimensionMembersBuilt)
740         return false;
741 
742     if ( pDimensionData || r.pDimensionData )
743         if ( !pDimensionData || !r.pDimensionData || !( *pDimensionData == *r.pDimensionData ) )
744             return false;
745 
746     if (!(::comphelper::ContainerUniquePtrEquals(m_DimList, r.m_DimList)))
747         return false;
748 
749     if (mpGrandTotalName)
750     {
751         if (!r.mpGrandTotalName)
752             return false;
753         if (*mpGrandTotalName != *r.mpGrandTotalName)
754             return false;
755     }
756     else if (r.mpGrandTotalName)
757         return false;
758 
759     return true;
760 }
761 
~ScDPSaveData()762 ScDPSaveData::~ScDPSaveData()
763 {
764 }
765 
SetGrandTotalName(const OUString & rName)766 void ScDPSaveData::SetGrandTotalName(const OUString& rName)
767 {
768     mpGrandTotalName = rName;
769 }
770 
GetGrandTotalName() const771 const boost::optional<OUString> & ScDPSaveData::GetGrandTotalName() const
772 {
773     return mpGrandTotalName;
774 }
775 
776 namespace {
777 
778 class DimOrderInserter
779 {
780     ScDPSaveData::DimOrderType& mrNames;
781 public:
DimOrderInserter(ScDPSaveData::DimOrderType & rNames)782     explicit DimOrderInserter(ScDPSaveData::DimOrderType& rNames) : mrNames(rNames) {}
783 
operator ()(const ScDPSaveDimension * pDim)784     void operator() (const ScDPSaveDimension* pDim)
785     {
786         size_t nRank = mrNames.size();
787         mrNames.emplace(pDim->GetName(), nRank);
788     }
789 };
790 
791 }
792 
GetDimensionSortOrder() const793 const ScDPSaveData::DimOrderType& ScDPSaveData::GetDimensionSortOrder() const
794 {
795     if (!mpDimOrder)
796     {
797         mpDimOrder.reset(new DimOrderType);
798         std::vector<const ScDPSaveDimension*> aRowDims, aColDims;
799         GetAllDimensionsByOrientation(sheet::DataPilotFieldOrientation_ROW, aRowDims);
800         GetAllDimensionsByOrientation(sheet::DataPilotFieldOrientation_COLUMN, aColDims);
801 
802         std::for_each(aRowDims.begin(), aRowDims.end(), DimOrderInserter(*mpDimOrder));
803         std::for_each(aColDims.begin(), aColDims.end(), DimOrderInserter(*mpDimOrder));
804     }
805     return *mpDimOrder;
806 }
807 
GetAllDimensionsByOrientation(sheet::DataPilotFieldOrientation eOrientation,std::vector<const ScDPSaveDimension * > & rDims) const808 void ScDPSaveData::GetAllDimensionsByOrientation(
809     sheet::DataPilotFieldOrientation eOrientation, std::vector<const ScDPSaveDimension*>& rDims) const
810 {
811     std::vector<const ScDPSaveDimension*> aDims;
812     for (auto const& it : m_DimList)
813     {
814         const ScDPSaveDimension& rDim = *it;
815         if (rDim.GetOrientation() != eOrientation)
816             continue;
817 
818         aDims.push_back(&rDim);
819     }
820 
821     rDims.swap(aDims);
822 }
823 
AddDimension(ScDPSaveDimension * pDim)824 void ScDPSaveData::AddDimension(ScDPSaveDimension* pDim)
825 {
826     if (!pDim)
827         return;
828 
829     CheckDuplicateName(*pDim);
830     m_DimList.push_back(std::unique_ptr<ScDPSaveDimension>(pDim));
831 
832     DimensionsChanged();
833 }
834 
GetDimensionByName(const OUString & rName)835 ScDPSaveDimension* ScDPSaveData::GetDimensionByName(const OUString& rName)
836 {
837     for (auto const& iter : m_DimList)
838     {
839         if (iter->GetName() == rName && !iter->IsDataLayout() )
840             return &(*iter);
841     }
842 
843     return AppendNewDimension(rName, false);
844 }
845 
GetExistingDimensionByName(const OUString & rName) const846 ScDPSaveDimension* ScDPSaveData::GetExistingDimensionByName(const OUString& rName) const
847 {
848     for (auto const& iter : m_DimList)
849     {
850         if (iter->GetName() == rName && !iter->IsDataLayout() )
851             return &(*iter);
852     }
853     return nullptr; // don't create new
854 }
855 
GetNewDimensionByName(const OUString & rName)856 ScDPSaveDimension* ScDPSaveData::GetNewDimensionByName(const OUString& rName)
857 {
858     for (auto const& iter : m_DimList)
859     {
860         if (iter->GetName() == rName && !iter->IsDataLayout() )
861             return DuplicateDimension(rName);
862     }
863 
864     return AppendNewDimension(rName, false);
865 }
866 
GetDataLayoutDimension()867 ScDPSaveDimension* ScDPSaveData::GetDataLayoutDimension()
868 {
869     ScDPSaveDimension* pDim = GetExistingDataLayoutDimension();
870     if (pDim)
871         return pDim;
872 
873     return AppendNewDimension(OUString(), true);
874 }
875 
GetExistingDataLayoutDimension() const876 ScDPSaveDimension* ScDPSaveData::GetExistingDataLayoutDimension() const
877 {
878     for (auto const& iter : m_DimList)
879     {
880         if ( iter->IsDataLayout() )
881             return &(*iter);
882     }
883     return nullptr;
884 }
885 
DuplicateDimension(const OUString & rName)886 ScDPSaveDimension* ScDPSaveData::DuplicateDimension(const OUString& rName)
887 {
888     // always insert new
889 
890     ScDPSaveDimension* pOld = GetExistingDimensionByName(rName);
891     if (!pOld)
892         return nullptr;
893 
894     ScDPSaveDimension* pNew = new ScDPSaveDimension( *pOld );
895     AddDimension(pNew);
896     return pNew;
897 }
898 
RemoveDimensionByName(const OUString & rName)899 void ScDPSaveData::RemoveDimensionByName(const OUString& rName)
900 {
901     auto iter = std::find_if(m_DimList.begin(), m_DimList.end(),
902         [&rName](const std::unique_ptr<ScDPSaveDimension>& rxDim) {
903             return rxDim->GetName() == rName && !rxDim->IsDataLayout(); });
904     if (iter != m_DimList.end())
905     {
906         m_DimList.erase(iter);
907         RemoveDuplicateNameCount(rName);
908         DimensionsChanged();
909     }
910 }
911 
DuplicateDimension(const ScDPSaveDimension & rDim)912 ScDPSaveDimension& ScDPSaveData::DuplicateDimension( const ScDPSaveDimension& rDim )
913 {
914     ScDPSaveDimension* pNew = new ScDPSaveDimension( rDim );
915     AddDimension(pNew);
916     return *pNew;
917 }
918 
GetInnermostDimension(DataPilotFieldOrientation nOrientation)919 ScDPSaveDimension* ScDPSaveData::GetInnermostDimension(DataPilotFieldOrientation nOrientation)
920 {
921     // return the innermost dimension for the given orientation,
922     // excluding data layout dimension
923 
924     auto iter = std::find_if(m_DimList.rbegin(), m_DimList.rend(),
925         [&nOrientation](const std::unique_ptr<ScDPSaveDimension>& rxDim) {
926             return rxDim->GetOrientation() == nOrientation && !rxDim->IsDataLayout(); });
927     if (iter != m_DimList.rend())
928         return iter->get();
929 
930     return nullptr;
931 }
932 
GetFirstDimension(sheet::DataPilotFieldOrientation eOrientation)933 ScDPSaveDimension* ScDPSaveData::GetFirstDimension(sheet::DataPilotFieldOrientation eOrientation)
934 {
935     for (auto const& iter : m_DimList)
936     {
937         if (iter->GetOrientation() == eOrientation && !iter->IsDataLayout())
938             return &(*iter);
939     }
940     return nullptr;
941 }
942 
GetDataDimensionCount() const943 long ScDPSaveData::GetDataDimensionCount() const
944 {
945     long nDataCount = 0;
946 
947     for (auto const& iter : m_DimList)
948     {
949         if (iter->GetOrientation() == sheet::DataPilotFieldOrientation_DATA)
950             ++nDataCount;
951     }
952 
953     return nDataCount;
954 }
955 
SetPosition(ScDPSaveDimension * pDim,long nNew)956 void ScDPSaveData::SetPosition( ScDPSaveDimension* pDim, long nNew )
957 {
958     // position (nNew) is counted within dimensions of the same orientation
959 
960     DataPilotFieldOrientation nOrient = pDim->GetOrientation();
961 
962     auto it = std::find_if(m_DimList.begin(), m_DimList.end(),
963         [&pDim](const std::unique_ptr<ScDPSaveDimension>& rxDim) { return pDim == rxDim.get(); });
964     if (it != m_DimList.end())
965     {
966         // Tell vector<unique_ptr> to give up ownership of this element.
967         // Don't delete this instance as it is re-inserted into the
968         // container later.
969         it->release();
970         m_DimList.erase(it);
971     }
972 
973     auto iterInsert = std::find_if(m_DimList.begin(), m_DimList.end(),
974         [&nOrient, &nNew](const std::unique_ptr<ScDPSaveDimension>& rxDim) {
975             if (rxDim->GetOrientation() == nOrient )
976                 --nNew;
977             return nNew <= 0;
978         });
979 
980     m_DimList.insert(iterInsert, std::unique_ptr<ScDPSaveDimension>(pDim));
981     DimensionsChanged();
982 }
983 
SetColumnGrand(bool bSet)984 void ScDPSaveData::SetColumnGrand(bool bSet)
985 {
986     nColumnGrandMode = sal_uInt16(bSet);
987 }
988 
SetRowGrand(bool bSet)989 void ScDPSaveData::SetRowGrand(bool bSet)
990 {
991     nRowGrandMode = sal_uInt16(bSet);
992 }
993 
SetIgnoreEmptyRows(bool bSet)994 void ScDPSaveData::SetIgnoreEmptyRows(bool bSet)
995 {
996     nIgnoreEmptyMode = sal_uInt16(bSet);
997 }
998 
SetRepeatIfEmpty(bool bSet)999 void ScDPSaveData::SetRepeatIfEmpty(bool bSet)
1000 {
1001     nRepeatEmptyMode = sal_uInt16(bSet);
1002 }
1003 
SetFilterButton(bool bSet)1004 void ScDPSaveData::SetFilterButton(bool bSet)
1005 {
1006     bFilterButton = bSet;
1007 }
1008 
SetDrillDown(bool bSet)1009 void ScDPSaveData::SetDrillDown(bool bSet)
1010 {
1011     bDrillDown = bSet;
1012 }
1013 
lcl_ResetOrient(const uno::Reference<sheet::XDimensionsSupplier> & xSource)1014 static void lcl_ResetOrient( const uno::Reference<sheet::XDimensionsSupplier>& xSource )
1015 {
1016     uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions();
1017     uno::Reference<container::XIndexAccess> xIntDims = new ScNameToIndexAccess( xDimsName );
1018     long nIntCount = xIntDims->getCount();
1019     for (long nIntDim=0; nIntDim<nIntCount; nIntDim++)
1020     {
1021         uno::Reference<beans::XPropertySet> xDimProp(xIntDims->getByIndex(nIntDim), uno::UNO_QUERY);
1022         if (xDimProp.is())
1023         {
1024             xDimProp->setPropertyValue( SC_UNO_DP_ORIENTATION, uno::Any(sheet::DataPilotFieldOrientation_HIDDEN) );
1025         }
1026     }
1027 }
1028 
WriteToSource(const uno::Reference<sheet::XDimensionsSupplier> & xSource)1029 void ScDPSaveData::WriteToSource( const uno::Reference<sheet::XDimensionsSupplier>& xSource )
1030 {
1031     if (!xSource.is())
1032         return;
1033 
1034     // source options must be first!
1035 
1036     uno::Reference<beans::XPropertySet> xSourceProp( xSource, uno::UNO_QUERY );
1037     SAL_WARN_IF( !xSourceProp.is(), "sc.core", "no properties at source" );
1038     if ( xSourceProp.is() )
1039     {
1040         // source options are not available for external sources
1041         //TODO: use XPropertySetInfo to test for availability?
1042 
1043         try
1044         {
1045             if ( nIgnoreEmptyMode != SC_DPSAVEMODE_DONTKNOW )
1046                 lcl_SetBoolProperty( xSourceProp,
1047                     SC_UNO_DP_IGNOREEMPTY, static_cast<bool>(nIgnoreEmptyMode) );
1048             if ( nRepeatEmptyMode != SC_DPSAVEMODE_DONTKNOW )
1049                 lcl_SetBoolProperty( xSourceProp,
1050                     SC_UNO_DP_REPEATEMPTY, static_cast<bool>(nRepeatEmptyMode) );
1051         }
1052         catch(uno::Exception&)
1053         {
1054             // no error
1055         }
1056 
1057         const boost::optional<OUString> & pGrandTotalName = GetGrandTotalName();
1058         if (pGrandTotalName)
1059             ScUnoHelpFunctions::SetOptionalPropertyValue(xSourceProp, SC_UNO_DP_GRANDTOTAL_NAME, *pGrandTotalName);
1060     }
1061 
1062     // exceptions in the other calls are errors
1063     try
1064     {
1065         // reset all orientations
1066         //TODO: "forgetSettings" or similar at source ?????
1067         //TODO: reset all duplicated dimensions, or reuse them below !!!
1068         SAL_INFO("sc.core", "ScDPSaveData::WriteToSource");
1069 
1070         lcl_ResetOrient( xSource );
1071 
1072         uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions();
1073         uno::Reference<container::XIndexAccess> xIntDims = new ScNameToIndexAccess( xDimsName );
1074         long nIntCount = xIntDims->getCount();
1075 
1076         for (const auto& rxDim : m_DimList)
1077         {
1078             OUString aName = rxDim->GetName();
1079             OUString aCoreName = ScDPUtil::getSourceDimensionName(aName);
1080 
1081             SAL_INFO("sc.core", aName);
1082 
1083             bool bData = rxDim->IsDataLayout();
1084 
1085             //TODO: getByName for ScDPSource, including DataLayoutDimension !!!!!!!!
1086 
1087             bool bFound = false;
1088             for (long nIntDim=0; nIntDim<nIntCount && !bFound; nIntDim++)
1089             {
1090                 uno::Reference<uno::XInterface> xIntDim(xIntDims->getByIndex(nIntDim),
1091                                                         uno::UNO_QUERY);
1092                 if ( bData )
1093                 {
1094                     uno::Reference<beans::XPropertySet> xDimProp( xIntDim, uno::UNO_QUERY );
1095                     if ( xDimProp.is() )
1096                     {
1097                         bFound = ScUnoHelpFunctions::GetBoolProperty( xDimProp,
1098                                     SC_UNO_DP_ISDATALAYOUT );
1099                         //TODO: error checking -- is "IsDataLayoutDimension" property required??
1100                     }
1101                 }
1102                 else
1103                 {
1104                     uno::Reference<container::XNamed> xDimName( xIntDim, uno::UNO_QUERY );
1105                     if (xDimName.is() && xDimName->getName() == aCoreName)
1106                         bFound = true;
1107                 }
1108 
1109                 if (bFound)
1110                 {
1111                     if (rxDim->GetDupFlag())
1112                     {
1113                         uno::Reference<util::XCloneable> xCloneable(xIntDim, uno::UNO_QUERY);
1114                         SAL_WARN_IF(!xCloneable.is(), "sc.core", "cannot clone dimension");
1115                         if (xCloneable.is())
1116                         {
1117                             uno::Reference<util::XCloneable> xNew = xCloneable->createClone();
1118                             uno::Reference<container::XNamed> xNewName(xNew, uno::UNO_QUERY);
1119                             if (xNewName.is())
1120                             {
1121                                 xNewName->setName(aName);
1122                                 rxDim->WriteToSource(xNew);
1123                             }
1124                         }
1125                     }
1126                     else
1127                         rxDim->WriteToSource( xIntDim );
1128                 }
1129             }
1130             SAL_WARN_IF(!bFound, "sc.core", "WriteToSource: Dimension not found: " + aName + ".");
1131         }
1132 
1133         if ( xSourceProp.is() )
1134         {
1135             if ( nColumnGrandMode != SC_DPSAVEMODE_DONTKNOW )
1136                 lcl_SetBoolProperty( xSourceProp,
1137                     SC_UNO_DP_COLGRAND, static_cast<bool>(nColumnGrandMode) );
1138             if ( nRowGrandMode != SC_DPSAVEMODE_DONTKNOW )
1139                 lcl_SetBoolProperty( xSourceProp,
1140                     SC_UNO_DP_ROWGRAND, static_cast<bool>(nRowGrandMode) );
1141         }
1142     }
1143     catch(uno::Exception const &)
1144     {
1145         TOOLS_WARN_EXCEPTION("sc.core", "WriteToSource");
1146     }
1147 }
1148 
IsEmpty() const1149 bool ScDPSaveData::IsEmpty() const
1150 {
1151     for (auto const& iter : m_DimList)
1152     {
1153         if (iter->GetOrientation() != sheet::DataPilotFieldOrientation_HIDDEN && !iter->IsDataLayout())
1154             return false;
1155     }
1156     return true; // no entries that are not hidden
1157 }
1158 
RemoveAllGroupDimensions(const OUString & rSrcDimName,std::vector<OUString> * pDeletedNames)1159 void ScDPSaveData::RemoveAllGroupDimensions( const OUString& rSrcDimName, std::vector<OUString>* pDeletedNames )
1160 {
1161     if (!pDimensionData)
1162         // No group dimensions exist. Nothing to do.
1163         return;
1164 
1165     // Remove numeric group dimension (exists once at most). No need to delete
1166     // anything in save data (grouping was done inplace in an existing base
1167     // dimension).
1168     pDimensionData->RemoveNumGroupDimension(rSrcDimName);
1169 
1170     // Remove named group dimension(s). Dimensions have to be removed from
1171     // dimension save data and from save data too.
1172     const ScDPSaveGroupDimension* pExistingGroup = pDimensionData->GetGroupDimForBase(rSrcDimName);
1173     while ( pExistingGroup )
1174     {
1175         OUString aGroupDimName = pExistingGroup->GetGroupDimName();
1176         pDimensionData->RemoveGroupDimension(aGroupDimName);     // pExistingGroup is deleted
1177 
1178         // also remove SaveData settings for the dimension that no longer exists
1179         RemoveDimensionByName(aGroupDimName);
1180 
1181         if (pDeletedNames)
1182             pDeletedNames->push_back(aGroupDimName);
1183 
1184         // see if there are more group dimensions
1185         pExistingGroup = pDimensionData->GetGroupDimForBase(rSrcDimName);
1186 
1187         if ( pExistingGroup && pExistingGroup->GetGroupDimName() == aGroupDimName )
1188         {
1189             // still get the same group dimension?
1190             OSL_FAIL("couldn't remove group dimension");
1191             pExistingGroup = nullptr;      // avoid endless loop
1192         }
1193     }
1194 }
1195 
GetDimensionData()1196 ScDPDimensionSaveData* ScDPSaveData::GetDimensionData()
1197 {
1198     if (!pDimensionData)
1199         pDimensionData.reset( new ScDPDimensionSaveData );
1200     return pDimensionData.get();
1201 }
1202 
SetDimensionData(const ScDPDimensionSaveData * pNew)1203 void ScDPSaveData::SetDimensionData( const ScDPDimensionSaveData* pNew )
1204 {
1205     if ( pNew )
1206         pDimensionData.reset( new ScDPDimensionSaveData( *pNew ) );
1207     else
1208         pDimensionData.reset();
1209 }
1210 
BuildAllDimensionMembers(ScDPTableData * pData)1211 void ScDPSaveData::BuildAllDimensionMembers(ScDPTableData* pData)
1212 {
1213     if (mbDimensionMembersBuilt)
1214         return;
1215 
1216     // First, build a dimension name-to-index map.
1217     typedef std::unordered_map<OUString, long> NameIndexMap;
1218     NameIndexMap aMap;
1219     long nColCount = pData->GetColumnCount();
1220     for (long i = 0; i < nColCount; ++i)
1221         aMap.emplace(pData->getDimensionName(i), i);
1222 
1223     NameIndexMap::const_iterator itrEnd = aMap.end();
1224 
1225     for (auto const& iter : m_DimList)
1226     {
1227         const OUString& rDimName = iter->GetName();
1228         if (rDimName.isEmpty())
1229             // empty dimension name. It must be data layout.
1230             continue;
1231 
1232         NameIndexMap::const_iterator itr = aMap.find(rDimName);
1233         if (itr == itrEnd)
1234             // dimension name not in the data. This should never happen!
1235             continue;
1236 
1237         long nDimIndex = itr->second;
1238         const std::vector<SCROW>& rMembers = pData->GetColumnEntries(nDimIndex);
1239         size_t nMemberCount = rMembers.size();
1240         for (size_t j = 0; j < nMemberCount; ++j)
1241         {
1242             const ScDPItemData* pMemberData = pData->GetMemberById( nDimIndex, rMembers[j] );
1243             OUString aMemName = pData->GetFormattedString(nDimIndex, *pMemberData, false);
1244             if (iter->GetExistingMemberByName(aMemName))
1245                 // this member instance already exists. nothing to do.
1246                 continue;
1247 
1248             unique_ptr<ScDPSaveMember> pNewMember(new ScDPSaveMember(aMemName));
1249             pNewMember->SetIsVisible(true);
1250             iter->AddMember(std::move(pNewMember));
1251         }
1252     }
1253 
1254     mbDimensionMembersBuilt = true;
1255 }
1256 
SyncAllDimensionMembers(ScDPTableData * pData)1257 void ScDPSaveData::SyncAllDimensionMembers(ScDPTableData* pData)
1258 {
1259     typedef std::unordered_map<OUString, long> NameIndexMap;
1260 
1261     // First, build a dimension name-to-index map.
1262     NameIndexMap aMap;
1263     long nColCount = pData->GetColumnCount();
1264     for (long i = 0; i < nColCount; ++i)
1265         aMap.emplace(pData->getDimensionName(i), i);
1266 
1267     NameIndexMap::const_iterator itMapEnd = aMap.end();
1268 
1269     for (auto const& it : m_DimList)
1270     {
1271         const OUString& rDimName = it->GetName();
1272         if (rDimName.isEmpty())
1273             // empty dimension name. It must be data layout.
1274             continue;
1275 
1276         NameIndexMap::const_iterator itMap = aMap.find(rDimName);
1277         if (itMap == itMapEnd)
1278             // dimension name not in the data. This should never happen!
1279             continue;
1280 
1281         ScDPSaveDimension::MemberSetType aMemNames;
1282         long nDimIndex = itMap->second;
1283         const std::vector<SCROW>& rMembers = pData->GetColumnEntries(nDimIndex);
1284         size_t nMemberCount = rMembers.size();
1285         for (size_t j = 0; j < nMemberCount; ++j)
1286         {
1287             const ScDPItemData* pMemberData = pData->GetMemberById(nDimIndex, rMembers[j]);
1288             OUString aMemName = pData->GetFormattedString(nDimIndex, *pMemberData, false);
1289             aMemNames.insert(aMemName);
1290         }
1291 
1292         it->RemoveObsoleteMembers(aMemNames);
1293     }
1294 }
1295 
HasInvisibleMember(const OUString & rDimName) const1296 bool ScDPSaveData::HasInvisibleMember(const OUString& rDimName) const
1297 {
1298     ScDPSaveDimension* pDim = GetExistingDimensionByName(rDimName);
1299     if (!pDim)
1300         return false;
1301 
1302     return pDim->HasInvisibleMember();
1303 }
1304 
1305 #if DUMP_PIVOT_TABLE
1306 
Dump() const1307 void ScDPSaveData::Dump() const
1308 {
1309     for (auto const& itDim : m_DimList)
1310     {
1311         const ScDPSaveDimension& rDim = *itDim;
1312         rDim.Dump();
1313     }
1314 }
1315 
1316 #endif
1317 
CheckDuplicateName(ScDPSaveDimension & rDim)1318 void ScDPSaveData::CheckDuplicateName(ScDPSaveDimension& rDim)
1319 {
1320     const OUString aName = ScDPUtil::getSourceDimensionName(rDim.GetName());
1321     DupNameCountType::iterator it = maDupNameCounts.find(aName);
1322     if (it != maDupNameCounts.end())
1323     {
1324         rDim.SetName(ScDPUtil::createDuplicateDimensionName(aName, ++it->second));
1325         rDim.SetDupFlag(true);
1326     }
1327     else
1328         // New name.
1329         maDupNameCounts.emplace(aName, 0);
1330 }
1331 
RemoveDuplicateNameCount(const OUString & rName)1332 void ScDPSaveData::RemoveDuplicateNameCount(const OUString& rName)
1333 {
1334     OUString aCoreName = rName;
1335     if (ScDPUtil::isDuplicateDimension(rName))
1336         aCoreName = ScDPUtil::getSourceDimensionName(rName);
1337 
1338     DupNameCountType::iterator it = maDupNameCounts.find(aCoreName);
1339     if (it == maDupNameCounts.end())
1340         return;
1341 
1342     if (!it->second)
1343     {
1344         maDupNameCounts.erase(it);
1345         return;
1346     }
1347 
1348     --it->second;
1349 }
1350 
AppendNewDimension(const OUString & rName,bool bDataLayout)1351 ScDPSaveDimension* ScDPSaveData::AppendNewDimension(const OUString& rName, bool bDataLayout)
1352 {
1353     if (ScDPUtil::isDuplicateDimension(rName))
1354         // This call is for original dimensions only.
1355         return nullptr;
1356 
1357     ScDPSaveDimension* pNew = new ScDPSaveDimension(rName, bDataLayout);
1358     m_DimList.push_back(std::unique_ptr<ScDPSaveDimension>(pNew));
1359     if (!maDupNameCounts.count(rName))
1360         maDupNameCounts.emplace(rName, 0);
1361 
1362     DimensionsChanged();
1363     return pNew;
1364 }
1365 
DimensionsChanged()1366 void ScDPSaveData::DimensionsChanged()
1367 {
1368     mpDimOrder.reset();
1369 }
1370 
1371 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1372