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