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