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