tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / sc / source / core / data / dptabsrc.cxx
blobad8f2490f2bab9af9f6530532fe2ed5cc0ab47d3
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 <dptabsrc.hxx>
22 #include <algorithm>
23 #include <vector>
25 #include <comphelper/sequence.hxx>
26 #include <o3tl/any.hxx>
27 #include <o3tl/safeint.hxx>
28 #include <osl/diagnose.h>
29 #include <rtl/math.hxx>
30 #include <sal/log.hxx>
31 #include <svl/itemprop.hxx>
33 #include <dpcache.hxx>
34 #include <dptabres.hxx>
35 #include <dptabdat.hxx>
36 #include <global.hxx>
37 #include <miscuno.hxx>
38 #include <unonames.hxx>
39 #include <dpitemdata.hxx>
40 #include <dputil.hxx>
41 #include <dpresfilter.hxx>
42 #include <calcmacros.hxx>
43 #include <generalfunction.hxx>
45 #include <com/sun/star/beans/PropertyAttribute.hpp>
46 #include <com/sun/star/sheet/DataPilotFieldFilter.hpp>
47 #include <com/sun/star/sheet/DataPilotFieldReferenceType.hpp>
48 #include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
49 #include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
50 #include <com/sun/star/sheet/DataPilotFieldAutoShowInfo.hpp>
51 #include <com/sun/star/sheet/GeneralFunction2.hpp>
52 #include <com/sun/star/sheet/TableFilterField.hpp>
54 #include <unotools/calendarwrapper.hxx>
55 #include <com/sun/star/i18n/CalendarDisplayIndex.hpp>
57 using namespace com::sun::star;
58 using ::std::vector;
59 using ::com::sun::star::uno::Sequence;
60 using ::com::sun::star::uno::Any;
61 using ::com::sun::star::sheet::DataPilotFieldAutoShowInfo;
63 #define SC_MINCOUNT_LIMIT 1000000
65 SC_SIMPLE_SERVICE_INFO( ScDPSource, u"ScDPSource"_ustr, u"com.sun.star.sheet.DataPilotSource"_ustr )
66 SC_SIMPLE_SERVICE_INFO( ScDPDimensions, u"ScDPDimensions"_ustr, u"com.sun.star.sheet.DataPilotSourceDimensions"_ustr )
67 SC_SIMPLE_SERVICE_INFO( ScDPDimension, u"ScDPDimension"_ustr, u"com.sun.star.sheet.DataPilotSourceDimension"_ustr )
69 // Typos are on purpose here, quote from Eike Rathke (see https://gerrit.libreoffice.org/c/core/+/101116):
70 // "The typo is exactly why the SC_SIMPLE_SERVICE_INFO_COMPAT() lists both service names,
71 // the old with the typo and the new corrected one. Correcting the typo in the old name
72 // will make all extensions fail that use it. This is not to be changed."
73 SC_SIMPLE_SERVICE_INFO_COMPAT( ScDPHierarchies, u"ScDPHierarchies"_ustr,
74 u"com.sun.star.sheet.DataPilotSourceHierarchies"_ustr, u"com.sun.star.sheet.DataPilotSourceHierarcies"_ustr )
75 SC_SIMPLE_SERVICE_INFO_COMPAT( ScDPHierarchy, u"ScDPHierarchy"_ustr,
76 u"com.sun.star.sheet.DataPilotSourceHierarchy"_ustr, u"com.sun.star.sheet.DataPilotSourceHierarcy"_ustr )
78 SC_SIMPLE_SERVICE_INFO( ScDPLevels, u"ScDPLevels"_ustr, u"com.sun.star.sheet.DataPilotSourceLevels"_ustr )
79 SC_SIMPLE_SERVICE_INFO( ScDPLevel, u"ScDPLevel"_ustr, u"com.sun.star.sheet.DataPilotSourceLevel"_ustr )
80 SC_SIMPLE_SERVICE_INFO( ScDPMembers, u"ScDPMembers"_ustr, u"com.sun.star.sheet.DataPilotSourceMembers"_ustr )
81 SC_SIMPLE_SERVICE_INFO( ScDPMember, u"ScDPMember"_ustr, u"com.sun.star.sheet.DataPilotSourceMember"_ustr )
83 // property maps for PropertySetInfo
84 // DataDescription / NumberFormat are internal
86 //TODO: move to a header?
87 static bool lcl_GetBoolFromAny( const uno::Any& aAny )
89 auto b = o3tl::tryAccess<bool>(aAny);
90 return b.has_value() && *b;
93 ScDPSource::ScDPSource(ScDPTableData* pData)
94 : mpData(pData)
96 mpData->SetEmptyFlags(mbIgnoreEmptyRows, mbRepeatIfEmpty);
99 ScDPSource::~ScDPSource()
101 // free lists
103 mpColumnResults.reset();
104 mpRowResults.reset();
106 mpColumnResultRoot.reset();
107 mpRowResultRoot.reset();
108 mpResultData.reset();
111 const std::optional<OUString> & ScDPSource::GetGrandTotalName() const
113 return mpGrandTotalName;
116 sheet::DataPilotFieldOrientation ScDPSource::GetOrientation(sal_Int32 nColumn)
118 if (std::find(maColDims.begin(), maColDims.end(), nColumn) != maColDims.end())
119 return sheet::DataPilotFieldOrientation_COLUMN;
121 if (std::find(maRowDims.begin(), maRowDims.end(), nColumn) != maRowDims.end())
122 return sheet::DataPilotFieldOrientation_ROW;
124 if (std::find(maDataDims.begin(), maDataDims.end(), nColumn) != maDataDims.end())
125 return sheet::DataPilotFieldOrientation_DATA;
127 if (std::find(maPageDims.begin(), maPageDims.end(), nColumn) != maPageDims.end())
128 return sheet::DataPilotFieldOrientation_PAGE;
130 return sheet::DataPilotFieldOrientation_HIDDEN;
133 sal_Int32 ScDPSource::GetDataDimensionCount() const
135 return maDataDims.size();
138 ScDPDimension* ScDPSource::GetDataDimension(sal_Int32 nIndex)
140 if (nIndex < 0 || o3tl::make_unsigned(nIndex) >= maDataDims.size())
141 return nullptr;
143 sal_Int32 nDimIndex = maDataDims[nIndex];
144 return GetDimensionsObject()->getByIndex(nDimIndex);
147 OUString ScDPSource::GetDataDimName(sal_Int32 nIndex)
149 OUString aRet;
150 ScDPDimension* pDim = GetDataDimension(nIndex);
151 if (pDim)
152 aRet = pDim->getName();
153 return aRet;
156 sal_Int32 ScDPSource::GetPosition(sal_Int32 nColumn)
158 std::vector<sal_Int32>::const_iterator it, itBeg = maColDims.begin(), itEnd = maColDims.end();
159 it = std::find(itBeg, itEnd, nColumn);
160 if (it != itEnd)
161 return std::distance(itBeg, it);
163 itBeg = maRowDims.begin();
164 itEnd = maRowDims.end();
165 it = std::find(itBeg, itEnd, nColumn);
166 if (it != itEnd)
167 return std::distance(itBeg, it);
169 itBeg = maDataDims.begin();
170 itEnd = maDataDims.end();
171 it = std::find(itBeg, itEnd, nColumn);
172 if (it != itEnd)
173 return std::distance(itBeg, it);
175 itBeg = maPageDims.begin();
176 itEnd = maPageDims.end();
177 it = std::find(itBeg, itEnd, nColumn);
178 if (it != itEnd)
179 return std::distance(itBeg, it);
181 return 0;
184 namespace {
186 bool testSubTotal( bool& rAllowed, sal_Int32 nColumn, const std::vector<sal_Int32>& rDims, ScDPSource* pSource )
188 rAllowed = true;
189 std::vector<sal_Int32>::const_iterator it = rDims.begin(), itEnd = rDims.end();
190 for (; it != itEnd; ++it)
192 if (*it != nColumn)
193 continue;
195 if ( pSource->IsDataLayoutDimension(nColumn) )
197 // no subtotals for data layout dim, no matter where
198 rAllowed = false;
199 return true;
202 // no subtotals if no other dim but data layout follows
203 ++it;
204 if (it != itEnd && pSource->IsDataLayoutDimension(*it))
205 ++it;
206 if (it == itEnd)
207 rAllowed = false;
209 return true; // found
212 return false;
215 void removeDim( sal_Int32 nRemove, std::vector<sal_Int32>& rDims )
217 std::vector<sal_Int32>::iterator it = std::find(rDims.begin(), rDims.end(), nRemove);
218 if (it != rDims.end())
219 rDims.erase(it);
224 bool ScDPSource::SubTotalAllowed(sal_Int32 nColumn)
226 //TODO: cache this at ScDPResultData
227 bool bAllowed = true;
228 if ( testSubTotal(bAllowed, nColumn, maColDims, this) )
229 return bAllowed;
230 if ( testSubTotal(bAllowed, nColumn, maRowDims, this) )
231 return bAllowed;
232 return bAllowed;
235 void ScDPSource::SetOrientation(sal_Int32 nColumn, sheet::DataPilotFieldOrientation nNew)
237 //TODO: change to no-op if new orientation is equal to old?
239 // remove from old list
240 removeDim(nColumn, maColDims);
241 removeDim(nColumn, maRowDims);
242 removeDim(nColumn, maDataDims);
243 removeDim(nColumn, maPageDims);
245 // add to new list
246 switch (nNew)
248 case sheet::DataPilotFieldOrientation_COLUMN:
249 maColDims.push_back(nColumn);
250 break;
251 case sheet::DataPilotFieldOrientation_ROW:
252 maRowDims.push_back(nColumn);
253 break;
254 case sheet::DataPilotFieldOrientation_DATA:
255 maDataDims.push_back(nColumn);
256 break;
257 case sheet::DataPilotFieldOrientation_PAGE:
258 maPageDims.push_back(nColumn);
259 break;
260 // DataPilot Migration - Cache&&Performance
261 case sheet::DataPilotFieldOrientation_HIDDEN:
262 break;
263 default:
264 OSL_FAIL( "ScDPSource::SetOrientation: unexpected orientation" );
265 break;
269 bool ScDPSource::IsDataLayoutDimension(sal_Int32 nDim)
271 return nDim == mpData->GetColumnCount();
274 sheet::DataPilotFieldOrientation ScDPSource::GetDataLayoutOrientation()
276 return GetOrientation(mpData->GetColumnCount());
279 bool ScDPSource::IsDateDimension(sal_Int32 nDim)
281 return mpData->IsDateDimension(nDim);
284 ScDPDimensions* ScDPSource::GetDimensionsObject()
286 if (!mpDimensions.is())
287 mpDimensions = new ScDPDimensions(this);
288 return mpDimensions.get();
291 uno::Reference<container::XNameAccess> SAL_CALL ScDPSource::getDimensions()
293 return GetDimensionsObject();
296 void ScDPSource::SetDupCount( tools::Long nNew )
298 mnDupCount = nNew;
301 ScDPDimension* ScDPSource::AddDuplicated(std::u16string_view rNewName)
303 OSL_ENSURE(mpDimensions.is(), "AddDuplicated without dimensions?");
305 // re-use
307 tools::Long nOldDimCount = mpDimensions->getCount();
308 for (tools::Long i=0; i<nOldDimCount; i++)
310 ScDPDimension* pDim = mpDimensions->getByIndex(i);
311 if (pDim && pDim->getName() == rNewName)
313 //TODO: test if pDim is a duplicate of source
314 return pDim;
318 SetDupCount(mnDupCount + 1);
319 mpDimensions->CountChanged(); // uses mnDupCount
321 return mpDimensions->getByIndex(mpDimensions->getCount() - 1);
324 sal_Int32 ScDPSource::GetSourceDim(sal_Int32 nDim)
326 // original source dimension or data layout dimension?
327 if (nDim <= mpData->GetColumnCount())
328 return nDim;
330 if (nDim < mpDimensions->getCount())
332 ScDPDimension* pDimObj = mpDimensions->getByIndex( nDim );
333 if ( pDimObj )
335 tools::Long nSource = pDimObj->GetSourceDim();
336 if ( nSource >= 0 )
337 return nSource;
341 OSL_FAIL("GetSourceDim: wrong dim");
342 return nDim;
345 uno::Sequence< uno::Sequence<sheet::DataResult> > SAL_CALL ScDPSource::getResults()
347 CreateRes_Impl(); // create mpColumnResultRoot and mpRowResultRoot
349 if (mbResultOverflow) // set in CreateRes_Impl
351 // no results available
352 throw uno::RuntimeException();
355 sal_Int32 nColCount = mpColumnResultRoot->GetSize(mpResultData->GetColStartMeasure());
356 sal_Int32 nRowCount = mpRowResultRoot->GetSize(mpResultData->GetRowStartMeasure());
358 // allocate full sequence
359 //TODO: leave out empty rows???
361 uno::Sequence< uno::Sequence<sheet::DataResult> > aSeq( nRowCount );
362 uno::Sequence<sheet::DataResult>* pRowAry = aSeq.getArray();
363 for (sal_Int32 nRow = 0; nRow < nRowCount; nRow++)
365 // use default values of DataResult
366 pRowAry[nRow] = uno::Sequence<sheet::DataResult>(nColCount);
369 ScDPResultFilterContext aFilterCxt;
370 mpRowResultRoot->FillDataResults(
371 mpColumnResultRoot.get(), aFilterCxt, aSeq, mpResultData->GetRowStartMeasure());
373 maResFilterSet.swap(aFilterCxt.maFilterSet); // Keep this data for GETPIVOTDATA.
375 return aSeq;
378 uno::Sequence<double> ScDPSource::getFilteredResults(
379 const uno::Sequence<sheet::DataPilotFieldFilter>& aFilters )
381 if (maResFilterSet.empty())
382 getResults(); // Build result tree first.
384 // Get result values from the tree.
385 const ScDPResultTree::ValuesType* pVals = maResFilterSet.getResults(aFilters);
386 if (pVals && !pVals->empty())
388 return comphelper::containerToSequence(*pVals);
391 if (aFilters.getLength() == 1)
393 // Try to get result from the leaf nodes.
394 double fVal = maResFilterSet.getLeafResult(aFilters[0]);
395 if (!std::isnan(fVal))
397 return { fVal };
401 return uno::Sequence<double>();
404 void SAL_CALL ScDPSource::refresh()
406 disposeData();
409 void SAL_CALL ScDPSource::addRefreshListener( const uno::Reference<util::XRefreshListener >& )
411 OSL_FAIL("not implemented"); //TODO: exception?
414 void SAL_CALL ScDPSource::removeRefreshListener( const uno::Reference<util::XRefreshListener >& )
416 OSL_FAIL("not implemented"); //TODO: exception?
419 Sequence< Sequence<Any> > SAL_CALL ScDPSource::getDrillDownData(const Sequence<sheet::DataPilotFieldFilter>& aFilters)
421 sal_Int32 nColumnCount = GetData()->GetColumnCount();
423 vector<ScDPFilteredCache::Criterion> aFilterCriteria;
424 for (const sheet::DataPilotFieldFilter& rFilter : aFilters)
426 const OUString& aFieldName = rFilter.FieldName;
427 for (sal_Int32 nCol = 0; nCol < nColumnCount; ++nCol)
429 if (aFieldName == mpData->getDimensionName(nCol))
431 ScDPDimension* pDim = GetDimensionsObject()->getByIndex( nCol );
432 ScDPMembers* pMembers = pDim->GetHierarchiesObject()->getByIndex(0)->
433 GetLevelsObject()->getByIndex(0)->GetMembersObject();
434 sal_Int32 nIndex = pMembers->GetIndexFromName( rFilter.MatchValueName );
435 if ( nIndex >= 0 )
437 ScDPItemData aItem(pMembers->getByIndex(nIndex)->FillItemData());
438 aFilterCriteria.emplace_back( );
439 aFilterCriteria.back().mnFieldIndex = nCol;
440 aFilterCriteria.back().mpFilter =
441 std::make_shared<ScDPFilteredCache::SingleFilter>(aItem);
447 // Take into account the visibilities of field members.
448 ScDPResultVisibilityData aResVisData(this);
449 mpRowResultRoot->FillVisibilityData(aResVisData);
450 mpColumnResultRoot->FillVisibilityData(aResVisData);
451 aResVisData.fillFieldFilters(aFilterCriteria);
453 Sequence< Sequence<Any> > aTabData;
454 std::unordered_set<sal_Int32> aCatDims;
455 GetCategoryDimensionIndices(aCatDims);
456 mpData->GetDrillDownData(std::move(aFilterCriteria), std::move(aCatDims), aTabData);
457 return aTabData;
460 OUString ScDPSource::getDataDescription()
462 CreateRes_Impl(); // create mpResultData
464 OUString aRet;
465 if (mpResultData->GetMeasureCount() == 1)
467 bool bTotalResult = false;
468 aRet = mpResultData->GetMeasureString(0, true, SUBTOTAL_FUNC_NONE, bTotalResult);
471 // empty for more than one measure
473 return aRet;
476 void ScDPSource::setIgnoreEmptyRows(bool bSet)
478 mbIgnoreEmptyRows = bSet;
479 mpData->SetEmptyFlags(mbIgnoreEmptyRows, mbRepeatIfEmpty);
482 void ScDPSource::setRepeatIfEmpty(bool bSet)
484 mbRepeatIfEmpty = bSet;
485 mpData->SetEmptyFlags(mbIgnoreEmptyRows, mbRepeatIfEmpty);
488 void ScDPSource::disposeData()
490 maResFilterSet.clear();
492 if (mpResultData)
494 // reset all data...
496 mpColumnResultRoot.reset();
497 mpRowResultRoot.reset();
498 mpResultData.reset();
499 mpColumnResults.reset();
500 mpRowResults.reset();
501 maColumnLevelList.clear();
502 maRowLevelList.clear();
505 mpDimensions.clear(); // settings have to be applied (from SaveData) again!
506 SetDupCount( 0 );
508 maColDims.clear();
509 maRowDims.clear();
510 maDataDims.clear();
511 maPageDims.clear();
513 mpData->DisposeData(); // cached entries etc.
514 mbPageFiltered = false;
515 mbResultOverflow = false;
518 static tools::Long lcl_CountMinMembers(const vector<ScDPDimension*>& ppDim, const vector<ScDPLevel*>& ppLevel, tools::Long nLevels )
520 // Calculate the product of the member count for those consecutive levels that
521 // have the "show all" flag, one following level, and the data layout dimension.
523 tools::Long nTotal = 1;
524 tools::Long nDataCount = 1;
525 bool bWasShowAll = true;
526 tools::Long nPos = nLevels;
527 while ( nPos > 0 )
529 --nPos;
531 if ( nPos+1 < nLevels && ppDim[nPos] == ppDim[nPos+1] )
533 OSL_FAIL("lcl_CountMinMembers: multiple levels from one dimension not implemented");
534 return 0;
537 bool bDo = false;
538 if ( ppDim[nPos]->getIsDataLayoutDimension() )
540 // data layout dim doesn't interfere with "show all" flags
541 nDataCount = ppLevel[nPos]->GetMembersObject()->getCount();
542 if ( nDataCount == 0 )
543 nDataCount = 1;
545 else if ( bWasShowAll ) // "show all" set for all following levels?
547 bDo = true;
548 if ( !ppLevel[nPos]->getShowEmpty() )
550 // this level is counted, following ones are not
551 bWasShowAll = false;
554 if ( bDo )
556 tools::Long nThisCount = ppLevel[nPos]->GetMembersObject()->getMinMembers();
557 if ( nThisCount == 0 )
559 nTotal = 1; // empty level -> start counting from here
560 //TODO: start with visible elements in this level?
562 else
564 if ( nTotal >= LONG_MAX / nThisCount )
565 return LONG_MAX; // overflow
566 nTotal *= nThisCount;
571 // always include data layout dim, even after restarting
572 if ( nTotal >= LONG_MAX / nDataCount )
573 return LONG_MAX; // overflow
574 nTotal *= nDataCount;
576 return nTotal;
579 void ScDPSource::FillCalcInfo(bool bIsRow, ScDPTableData::CalcInfo& rInfo, bool &rHasAutoShow)
581 const std::vector<sal_Int32>& rDims = bIsRow ? maRowDims : maColDims;
582 for (const auto& rDimIndex : rDims)
584 ScDPDimension* pDim = GetDimensionsObject()->getByIndex(rDimIndex);
585 tools::Long nHierarchy = ScDPDimension::getUsedHierarchy();
586 if ( nHierarchy >= ScDPHierarchies::getCount() )
587 nHierarchy = 0;
588 ScDPLevels* pLevels = pDim->GetHierarchiesObject()->getByIndex(nHierarchy)->GetLevelsObject();
589 sal_Int32 nCount = pLevels->getCount();
591 //TODO: Test
592 if (pDim->getIsDataLayoutDimension() && maDataDims.size() < 2)
593 nCount = 0;
594 //TODO: Test
596 for (sal_Int32 j = 0; j < nCount; ++j)
598 ScDPLevel* pLevel = pLevels->getByIndex(j);
599 pLevel->EvaluateSortOrder();
601 // no layout flags for column fields, only for row fields
602 pLevel->SetEnableLayout( bIsRow );
604 if ( pLevel->GetAutoShow().IsEnabled )
605 rHasAutoShow = true;
607 if (bIsRow)
609 rInfo.aRowLevelDims.push_back(rDimIndex);
610 rInfo.aRowDims.push_back(pDim);
611 rInfo.aRowLevels.push_back(pLevel);
613 else
615 rInfo.aColLevelDims.push_back(rDimIndex);
616 rInfo.aColDims.push_back(pDim);
617 rInfo.aColLevels.push_back(pLevel);
620 pLevel->GetMembersObject(); // initialize for groups
625 namespace {
627 class CategoryDimInserter
629 ScDPSource& mrSource;
630 std::unordered_set<sal_Int32>& mrCatDims;
631 public:
632 CategoryDimInserter(ScDPSource& rSource, std::unordered_set<sal_Int32>& rCatDims) :
633 mrSource(rSource),
634 mrCatDims(rCatDims) {}
636 void operator() (tools::Long nDim)
638 if (!mrSource.IsDataLayoutDimension(nDim))
639 mrCatDims.insert(nDim);
645 void ScDPSource::GetCategoryDimensionIndices(std::unordered_set<sal_Int32>& rCatDims)
647 std::unordered_set<sal_Int32> aCatDims;
649 CategoryDimInserter aInserter(*this, aCatDims);
650 std::for_each(maColDims.begin(), maColDims.end(), aInserter);
651 std::for_each(maRowDims.begin(), maRowDims.end(), aInserter);
652 std::for_each(maPageDims.begin(), maPageDims.end(), aInserter);
654 rCatDims.swap(aCatDims);
657 void ScDPSource::FilterCacheByPageDimensions()
659 // #i117661# Repeated calls to ScDPFilteredCache::filterByPageDimension
660 // are invalid because rows are only hidden, never shown again. If
661 // FilterCacheByPageDimensions is called again, the cache table must
662 // be re-initialized. Currently, CreateRes_Impl always uses a fresh cache
663 // because ScDBDocFunc::DataPilotUpdate calls InvalidateData.
665 if (mbPageFiltered)
667 SAL_WARN( "sc.core","tried to apply page field filters several times");
669 mpData->DisposeData();
670 mpData->CreateCacheTable(); // re-initialize the cache table
671 mbPageFiltered = false;
674 // filter table by page dimensions.
675 vector<ScDPFilteredCache::Criterion> aCriteria;
676 for (const auto& rDimIndex : maPageDims)
678 ScDPDimension* pDim = GetDimensionsObject()->getByIndex(rDimIndex);
679 tools::Long nField = pDim->GetDimension();
681 ScDPMembers* pMems = pDim->GetHierarchiesObject()->getByIndex(0)->
682 GetLevelsObject()->getByIndex(0)->GetMembersObject();
684 tools::Long nMemCount = pMems->getCount();
685 ScDPFilteredCache::Criterion aFilter;
686 aFilter.mnFieldIndex = static_cast<sal_Int32>(nField);
687 aFilter.mpFilter = std::make_shared<ScDPFilteredCache::GroupFilter>();
688 ScDPFilteredCache::GroupFilter* pGrpFilter =
689 static_cast<ScDPFilteredCache::GroupFilter*>(aFilter.mpFilter.get());
690 for (tools::Long j = 0; j < nMemCount; ++j)
692 ScDPMember* pMem = pMems->getByIndex(j);
693 if (pMem->isVisible())
695 ScDPItemData aData(pMem->FillItemData());
696 pGrpFilter->addMatchItem(aData);
699 if (pGrpFilter->getMatchItemCount() < o3tl::make_unsigned(nMemCount))
700 // there is at least one invisible item. Add this filter criterion to the mix.
701 aCriteria.push_back(aFilter);
703 if (!pDim->HasSelectedPage())
704 continue;
706 const ScDPItemData& rData = pDim->GetSelectedData();
707 aCriteria.emplace_back();
708 ScDPFilteredCache::Criterion& r = aCriteria.back();
709 r.mnFieldIndex = static_cast<sal_Int32>(nField);
710 r.mpFilter = std::make_shared<ScDPFilteredCache::SingleFilter>(rData);
712 if (!aCriteria.empty())
714 std::unordered_set<sal_Int32> aCatDims;
715 GetCategoryDimensionIndices(aCatDims);
716 mpData->FilterCacheTable(std::move(aCriteria), std::move(aCatDims));
717 mbPageFiltered = true;
721 void ScDPSource::CreateRes_Impl()
723 if (mpResultData)
724 return;
726 sheet::DataPilotFieldOrientation nDataOrient = GetDataLayoutOrientation();
727 if (maDataDims.size() > 1 && ( nDataOrient != sheet::DataPilotFieldOrientation_COLUMN &&
728 nDataOrient != sheet::DataPilotFieldOrientation_ROW ) )
730 // if more than one data dimension, data layout orientation must be set
731 SetOrientation(mpData->GetColumnCount(), sheet::DataPilotFieldOrientation_ROW);
732 nDataOrient = sheet::DataPilotFieldOrientation_ROW;
735 // TODO: Aggregate pDataNames, pDataRefValues, nDataRefOrient, and
736 // eDataFunctions into a structure and use vector instead of static
737 // or pointer arrays.
738 vector<OUString> aDataNames;
739 vector<sheet::DataPilotFieldReference> aDataRefValues;
740 vector<ScSubTotalFunc> aDataFunctions;
741 vector<sheet::DataPilotFieldOrientation> aDataRefOrient;
743 ScDPTableData::CalcInfo aInfo;
745 // LateInit (initialize only those rows/children that are used) can be used unless
746 // any data dimension needs reference values from column/row dimensions
747 bool bLateInit = true;
749 // Go through all data dimensions (i.e. fields) and build their meta data
750 // so that they can be passed on to ScDPResultData instance later.
751 // TODO: aggregate all of data dimension info into a structure.
752 for (const tools::Long nDimIndex : maDataDims)
754 // Get function for each data field.
755 ScDPDimension* pDim = GetDimensionsObject()->getByIndex(nDimIndex);
756 ScGeneralFunction eUser = pDim->getFunction();
757 if (eUser == ScGeneralFunction::AUTO)
759 //TODO: test for numeric data
760 eUser = ScGeneralFunction::SUM;
763 // Map UNO's enum to internal enum ScSubTotalFunc.
764 aDataFunctions.push_back(ScDPUtil::toSubTotalFunc(eUser));
766 // Get reference field/item information.
767 aDataRefValues.push_back(pDim->GetReferenceValue());
768 sheet::DataPilotFieldOrientation nDataRefOrient = sheet::DataPilotFieldOrientation_HIDDEN; // default if not used
769 sal_Int32 eRefType = aDataRefValues.back().ReferenceType;
770 if ( eRefType == sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE ||
771 eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE ||
772 eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE ||
773 eRefType == sheet::DataPilotFieldReferenceType::RUNNING_TOTAL )
775 sal_Int32 nColumn = comphelper::findValue(
776 GetDimensionsObject()->getElementNames(), aDataRefValues.back().ReferenceField);
777 if ( nColumn >= 0 )
779 nDataRefOrient = GetOrientation(nColumn);
780 // need fully initialized results to find reference values
781 // (both in column or row dimensions), so updated values or
782 // differences to 0 can be displayed even for empty results.
783 bLateInit = false;
787 aDataRefOrient.push_back(nDataRefOrient);
789 aDataNames.push_back(pDim->getName());
791 //TODO: modify user visible strings as in ScDPResultData::GetMeasureString instead!
793 aDataNames.back() = ScDPUtil::getSourceDimensionName(aDataNames.back());
795 //TODO: if the name is overridden by user, a flag must be set
796 //TODO: so the user defined name replaces the function string and field name.
798 //TODO: the complete name (function and field) must be stored at the dimension
800 tools::Long nSource = pDim->GetSourceDim();
801 if (nSource >= 0)
802 aInfo.aDataSrcCols.push_back(nSource);
803 else
804 aInfo.aDataSrcCols.push_back(nDimIndex);
807 mpResultData.reset( new ScDPResultData(*this) );
808 mpResultData->SetMeasureData(aDataFunctions, aDataRefValues, aDataRefOrient, aDataNames);
809 mpResultData->SetDataLayoutOrientation(nDataOrient);
810 mpResultData->SetLateInit( bLateInit );
812 bool bHasAutoShow = false;
814 ScDPInitState aInitState;
816 // Page field selections restrict the members shown in related fields
817 // (both in column and row fields). aInitState is filled with the page
818 // field selections, they are kept across the data iterator loop.
820 for (const auto& rDimIndex : maPageDims)
822 ScDPDimension* pDim = GetDimensionsObject()->getByIndex(rDimIndex);
823 if ( pDim->HasSelectedPage() )
824 aInitState.AddMember(rDimIndex, GetCache()->GetIdByItemData(rDimIndex, pDim->GetSelectedData()));
827 // Show grand total columns only when the option is set *and* there is at
828 // least one column field. Same for the grand total rows.
829 sheet::DataPilotFieldOrientation nDataLayoutOrient = GetDataLayoutOrientation();
830 tools::Long nColDimCount2 = maColDims.size() - (nDataLayoutOrient == sheet::DataPilotFieldOrientation_COLUMN ? 1 : 0);
831 tools::Long nRowDimCount2 = maRowDims.size() - (nDataLayoutOrient == sheet::DataPilotFieldOrientation_ROW ? 1 : 0);
832 bool bShowColGrand = mbColumnGrand && nColDimCount2 > 0;
833 bool bShowRowGrand = mbRowGrand && nRowDimCount2 > 0;
834 mpColumnResultRoot.reset( new ScDPResultMember(mpResultData.get(), bShowColGrand) );
835 mpRowResultRoot.reset( new ScDPResultMember(mpResultData.get(), bShowRowGrand) );
837 FillCalcInfo(false, aInfo, bHasAutoShow);
838 tools::Long nColLevelCount = aInfo.aColLevels.size();
840 mpColumnResultRoot->InitFrom( aInfo.aColDims, aInfo.aColLevels, 0, aInitState );
841 mpColumnResultRoot->SetHasElements();
843 FillCalcInfo(true, aInfo, bHasAutoShow);
844 tools::Long nRowLevelCount = aInfo.aRowLevels.size();
846 if ( nRowLevelCount > 0 )
848 // disable layout flags for the innermost row field (level)
849 aInfo.aRowLevels[nRowLevelCount-1]->SetEnableLayout( false );
852 mpRowResultRoot->InitFrom( aInfo.aRowDims, aInfo.aRowLevels, 0, aInitState );
853 mpRowResultRoot->SetHasElements();
855 // initialize members object also for all page dimensions (needed for numeric groups)
856 for (const auto& rDimIndex : maPageDims)
858 ScDPDimension* pDim = GetDimensionsObject()->getByIndex(rDimIndex);
859 tools::Long nHierarchy = ScDPDimension::getUsedHierarchy();
860 if ( nHierarchy >= ScDPHierarchies::getCount() )
861 nHierarchy = 0;
863 ScDPLevels* pLevels = pDim->GetHierarchiesObject()->getByIndex(nHierarchy)->GetLevelsObject();
864 tools::Long nCount = pLevels->getCount();
865 for (tools::Long j=0; j<nCount; j++)
866 pLevels->getByIndex(j)->GetMembersObject(); // initialize for groups
869 // pre-check: calculate minimum number of result columns / rows from
870 // levels that have the "show all" flag set
872 tools::Long nMinColMembers = lcl_CountMinMembers( aInfo.aColDims, aInfo.aColLevels, nColLevelCount );
873 tools::Long nMinRowMembers = lcl_CountMinMembers( aInfo.aRowDims, aInfo.aRowLevels, nRowLevelCount );
875 if ( nMinColMembers > MAXCOLCOUNT/*SC_MINCOUNT_LIMIT*/ || nMinRowMembers > SC_MINCOUNT_LIMIT )
877 // resulting table is too big -> abort before calculating
878 // (this relies on late init, so no members are allocated in InitFrom above)
880 mbResultOverflow = true;
881 return;
884 FilterCacheByPageDimensions();
886 aInfo.aPageDims = maPageDims;
887 aInfo.pInitState = &aInitState;
888 aInfo.pColRoot = mpColumnResultRoot.get();
889 aInfo.pRowRoot = mpRowResultRoot.get();
890 mpData->CalcResults(aInfo, false);
892 mpColumnResultRoot->CheckShowEmpty();
893 mpRowResultRoot->CheckShowEmpty();
895 // With all data processed, calculate the final results:
897 // UpdateDataResults calculates all original results from the collected values,
898 // and stores them as reference values if needed.
899 mpRowResultRoot->UpdateDataResults(mpColumnResultRoot.get(), mpResultData->GetRowStartMeasure());
901 if ( bHasAutoShow ) // do the double calculation only if AutoShow is used
903 // Find the desired members and set bAutoHidden flag for the others
904 mpRowResultRoot->DoAutoShow(mpColumnResultRoot.get());
906 // Reset all results to empty, so they can be built again with data for the
907 // desired members only.
908 mpColumnResultRoot->ResetResults();
909 mpRowResultRoot->ResetResults();
910 mpData->CalcResults(aInfo, true);
912 // Call UpdateDataResults again, with the new (limited) values.
913 mpRowResultRoot->UpdateDataResults(mpColumnResultRoot.get(), mpResultData->GetRowStartMeasure());
916 // SortMembers does the sorting by a result dimension, using the original results,
917 // but not running totals etc.
918 mpRowResultRoot->SortMembers(mpColumnResultRoot.get());
920 // UpdateRunningTotals calculates running totals along column/row dimensions,
921 // differences from other members (named or relative), and column/row percentages
922 // or index values.
923 // Running totals and relative differences need to be done using the sorted values.
924 // Column/row percentages and index values must be done after sorting, because the
925 // results may no longer be in the right order (row total for percentage of row is
926 // always 1).
927 ScDPRunningTotalState aRunning(mpColumnResultRoot.get(), mpRowResultRoot.get());
928 ScDPRowTotals aTotals;
929 mpRowResultRoot->UpdateRunningTotals(mpColumnResultRoot.get(), mpResultData->GetRowStartMeasure(), aRunning, aTotals);
931 #if DUMP_PIVOT_TABLE
932 DumpResults();
933 #endif
936 void ScDPSource::FillLevelList( sheet::DataPilotFieldOrientation nOrientation, std::vector<ScDPLevel*> &rList )
938 rList.clear();
940 std::vector<sal_Int32>* pDimIndex = nullptr;
941 switch (nOrientation)
943 case sheet::DataPilotFieldOrientation_COLUMN:
944 pDimIndex = &maColDims;
945 break;
946 case sheet::DataPilotFieldOrientation_ROW:
947 pDimIndex = &maRowDims;
948 break;
949 case sheet::DataPilotFieldOrientation_DATA:
950 pDimIndex = &maDataDims;
951 break;
952 case sheet::DataPilotFieldOrientation_PAGE:
953 pDimIndex = &maPageDims;
954 break;
955 default:
956 OSL_FAIL( "ScDPSource::FillLevelList: unexpected orientation" );
957 break;
959 if (!pDimIndex)
961 OSL_FAIL("invalid orientation");
962 return;
965 ScDPDimensions* pDims = GetDimensionsObject();
966 for (const auto& rIndex : *pDimIndex)
968 ScDPDimension* pDim = pDims->getByIndex(rIndex);
969 OSL_ENSURE( pDim->getOrientation() == nOrientation, "orientations are wrong" );
971 ScDPHierarchies* pHiers = pDim->GetHierarchiesObject();
972 sal_Int32 nHierarchy = ScDPDimension::getUsedHierarchy();
973 if ( nHierarchy >= ScDPHierarchies::getCount() )
974 nHierarchy = 0;
975 ScDPHierarchy* pHier = pHiers->getByIndex(nHierarchy);
976 ScDPLevels* pLevels = pHier->GetLevelsObject();
977 sal_Int32 nLevCount = pLevels->getCount();
978 for (sal_Int32 nLev=0; nLev<nLevCount; nLev++)
980 ScDPLevel* pLevel = pLevels->getByIndex(nLev);
981 rList.push_back(pLevel);
986 void ScDPSource::FillMemberResults()
988 if (mpColumnResults || mpRowResults)
989 return;
991 CreateRes_Impl();
993 if (mbResultOverflow) // set in CreateRes_Impl
995 // no results available -> abort (leave empty)
996 // exception is thrown in ScDPSource::getResults
997 return;
1000 FillLevelList(sheet::DataPilotFieldOrientation_COLUMN, maColumnLevelList);
1001 sal_Int32 nColLevelCount = maColumnLevelList.size();
1002 if (nColLevelCount)
1004 tools::Long nColDimSize = mpColumnResultRoot->GetSize(mpResultData->GetColStartMeasure());
1005 mpColumnResults.reset(new uno::Sequence<sheet::MemberResult>[nColLevelCount]);
1006 for (tools::Long i=0; i<nColLevelCount; i++)
1007 mpColumnResults[i].realloc(nColDimSize);
1009 tools::Long nPos = 0;
1010 mpColumnResultRoot->FillMemberResults(mpColumnResults.get(), nPos, mpResultData->GetColStartMeasure(),
1011 true, nullptr, nullptr);
1014 FillLevelList(sheet::DataPilotFieldOrientation_ROW, maRowLevelList);
1015 tools::Long nRowLevelCount = maRowLevelList.size();
1016 if (nRowLevelCount)
1018 tools::Long nRowDimSize = mpRowResultRoot->GetSize(mpResultData->GetRowStartMeasure());
1019 mpRowResults.reset( new uno::Sequence<sheet::MemberResult>[nRowLevelCount] );
1020 for (tools::Long i=0; i<nRowLevelCount; i++)
1021 mpRowResults[i].realloc(nRowDimSize);
1023 tools::Long nPos = 0;
1024 mpRowResultRoot->FillMemberResults(mpRowResults.get(), nPos, mpResultData->GetRowStartMeasure(),
1025 true, nullptr, nullptr);
1029 const uno::Sequence<sheet::MemberResult>* ScDPSource::GetMemberResults( const ScDPLevel* pLevel )
1031 FillMemberResults();
1033 sal_Int32 i = 0;
1034 sal_Int32 nColCount = maColumnLevelList.size();
1035 for (i=0; i<nColCount; i++)
1037 ScDPLevel* pColLevel = maColumnLevelList[i];
1038 if ( pColLevel == pLevel )
1039 return &mpColumnResults[i];
1041 sal_Int32 nRowCount = maRowLevelList.size();
1042 for (i=0; i<nRowCount; i++)
1044 ScDPLevel* pRowLevel = maRowLevelList[i];
1045 if ( pRowLevel == pLevel )
1046 return &mpRowResults[i];
1048 return nullptr;
1051 // XPropertySet
1053 uno::Reference<beans::XPropertySetInfo> SAL_CALL ScDPSource::getPropertySetInfo()
1055 using beans::PropertyAttribute::READONLY;
1057 static const SfxItemPropertyMapEntry aDPSourceMap_Impl[] =
1059 { SC_UNO_DP_COLGRAND, 0, cppu::UnoType<bool>::get(), 0, 0 },
1060 { SC_UNO_DP_DATADESC, 0, cppu::UnoType<OUString>::get(), beans::PropertyAttribute::READONLY, 0 },
1061 { SC_UNO_DP_IGNOREEMPTY, 0, cppu::UnoType<bool>::get(), 0, 0 }, // for sheet data only
1062 { SC_UNO_DP_REPEATEMPTY, 0, cppu::UnoType<bool>::get(), 0, 0 }, // for sheet data only
1063 { SC_UNO_DP_ROWGRAND, 0, cppu::UnoType<bool>::get(), 0, 0 },
1064 { SC_UNO_DP_ROWFIELDCOUNT, 0, cppu::UnoType<sal_Int32>::get(), READONLY, 0 },
1065 { SC_UNO_DP_COLUMNFIELDCOUNT, 0, cppu::UnoType<sal_Int32>::get(), READONLY, 0 },
1066 { SC_UNO_DP_DATAFIELDCOUNT, 0, cppu::UnoType<sal_Int32>::get(), READONLY, 0 },
1067 { SC_UNO_DP_GRANDTOTAL_NAME, 0, cppu::UnoType<OUString>::get(), 0, 0 },
1069 static uno::Reference<beans::XPropertySetInfo> aRef =
1070 new SfxItemPropertySetInfo( aDPSourceMap_Impl );
1071 return aRef;
1074 void SAL_CALL ScDPSource::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue )
1076 if (aPropertyName == SC_UNO_DP_COLGRAND)
1077 mbColumnGrand = lcl_GetBoolFromAny(aValue);
1078 else if (aPropertyName == SC_UNO_DP_ROWGRAND)
1079 mbRowGrand = lcl_GetBoolFromAny(aValue);
1080 else if (aPropertyName == SC_UNO_DP_IGNOREEMPTY)
1081 setIgnoreEmptyRows( lcl_GetBoolFromAny( aValue ) );
1082 else if (aPropertyName == SC_UNO_DP_REPEATEMPTY)
1083 setRepeatIfEmpty( lcl_GetBoolFromAny( aValue ) );
1084 else if (aPropertyName == SC_UNO_DP_GRANDTOTAL_NAME)
1086 OUString aName;
1087 if (aValue >>= aName)
1088 mpGrandTotalName = aName;
1090 else
1092 OSL_FAIL("unknown property");
1093 //TODO: THROW( UnknownPropertyException() );
1097 uno::Any SAL_CALL ScDPSource::getPropertyValue( const OUString& aPropertyName )
1099 uno::Any aRet;
1100 if ( aPropertyName == SC_UNO_DP_COLGRAND )
1101 aRet <<= mbColumnGrand;
1102 else if ( aPropertyName == SC_UNO_DP_ROWGRAND )
1103 aRet <<= mbRowGrand;
1104 else if ( aPropertyName == SC_UNO_DP_IGNOREEMPTY )
1105 aRet <<= mbIgnoreEmptyRows;
1106 else if ( aPropertyName == SC_UNO_DP_REPEATEMPTY )
1107 aRet <<= mbRepeatIfEmpty;
1108 else if ( aPropertyName == SC_UNO_DP_DATADESC ) // read-only
1109 aRet <<= getDataDescription();
1110 else if ( aPropertyName == SC_UNO_DP_ROWFIELDCOUNT ) // read-only
1111 aRet <<= static_cast<sal_Int32>(maRowDims.size());
1112 else if ( aPropertyName == SC_UNO_DP_COLUMNFIELDCOUNT ) // read-only
1113 aRet <<= static_cast<sal_Int32>(maColDims.size());
1114 else if ( aPropertyName == SC_UNO_DP_DATAFIELDCOUNT ) // read-only
1115 aRet <<= static_cast<sal_Int32>(maDataDims.size());
1116 else if (aPropertyName == SC_UNO_DP_GRANDTOTAL_NAME)
1118 if (mpGrandTotalName)
1119 aRet <<= *mpGrandTotalName;
1121 else
1123 OSL_FAIL("unknown property");
1124 //TODO: THROW( UnknownPropertyException() );
1126 return aRet;
1129 #if DUMP_PIVOT_TABLE
1130 void ScDPSource::DumpResults() const
1132 std::cout << "+++++ column root" << std::endl;
1133 mpColumnResultRoot->Dump(1);
1134 std::cout << "+++++ row root" << std::endl;
1135 mpRowResultRoot->Dump(1);
1137 #endif
1139 SC_IMPL_DUMMY_PROPERTY_LISTENER( ScDPSource )
1141 ScDPDimensions::ScDPDimensions( ScDPSource* pSrc ) :
1142 pSource( pSrc )
1144 //TODO: hold pSource
1146 // include data layout dimension and duplicated dimensions
1147 nDimCount = pSource->GetData()->GetColumnCount() + 1 + pSource->GetDupCount();
1150 ScDPDimensions::~ScDPDimensions()
1152 //TODO: release pSource
1155 void ScDPDimensions::CountChanged()
1157 // include data layout dimension and duplicated dimensions
1158 sal_Int32 nNewCount = pSource->GetData()->GetColumnCount() + 1 + pSource->GetDupCount();
1159 if ( ppDims )
1161 sal_Int32 i;
1162 sal_Int32 nCopy = std::min( nNewCount, nDimCount );
1163 rtl::Reference<ScDPDimension>* ppNew = new rtl::Reference<ScDPDimension>[nNewCount];
1165 for (i=0; i<nCopy; i++) // copy existing dims
1166 ppNew[i] = ppDims[i];
1167 for (i=nCopy; i<nNewCount; i++) // clear additional pointers
1168 ppNew[i] = nullptr;
1170 ppDims.reset( ppNew );
1172 nDimCount = nNewCount;
1175 // very simple XNameAccess implementation using getCount/getByIndex
1177 uno::Any SAL_CALL ScDPDimensions::getByName( const OUString& aName )
1179 sal_Int32 nCount = getCount();
1180 for (sal_Int32 i=0; i<nCount; i++)
1181 if ( getByIndex(i)->getName() == aName )
1183 uno::Reference<container::XNamed> xNamed = getByIndex(i);
1184 uno::Any aRet;
1185 aRet <<= xNamed;
1186 return aRet;
1189 throw container::NoSuchElementException();
1190 // return uno::Any();
1193 uno::Sequence<OUString> SAL_CALL ScDPDimensions::getElementNames()
1195 tools::Long nCount = getCount();
1196 uno::Sequence<OUString> aSeq(nCount);
1197 OUString* pArr = aSeq.getArray();
1198 for (tools::Long i=0; i<nCount; i++)
1199 pArr[i] = getByIndex(i)->getName();
1200 return aSeq;
1203 sal_Bool SAL_CALL ScDPDimensions::hasByName( const OUString& aName )
1205 tools::Long nCount = getCount();
1206 for (tools::Long i=0; i<nCount; i++)
1207 if ( getByIndex(i)->getName() == aName )
1208 return true;
1209 return false;
1212 uno::Type SAL_CALL ScDPDimensions::getElementType()
1214 return cppu::UnoType<container::XNamed>::get();
1217 sal_Bool SAL_CALL ScDPDimensions::hasElements()
1219 return ( getCount() > 0 );
1222 // end of XNameAccess implementation
1224 tools::Long ScDPDimensions::getCount() const
1226 // in tabular data, every column of source data is a dimension
1228 return nDimCount;
1231 ScDPDimension* ScDPDimensions::getByIndex(tools::Long nIndex) const
1233 if ( nIndex >= 0 && nIndex < nDimCount )
1235 if ( !ppDims )
1237 const_cast<ScDPDimensions*>(this)->ppDims.reset(new rtl::Reference<ScDPDimension>[nDimCount] );
1238 for (tools::Long i=0; i<nDimCount; i++)
1239 ppDims[i] = nullptr;
1241 if ( !ppDims[nIndex].is() )
1243 ppDims[nIndex] = new ScDPDimension( pSource, nIndex );
1246 return ppDims[nIndex].get();
1249 return nullptr; //TODO: exception?
1252 ScDPDimension::ScDPDimension( ScDPSource* pSrc, tools::Long nD ) :
1253 pSource( pSrc ),
1254 nDim( nD ),
1255 nFunction( ScGeneralFunction::SUM ), // sum is default
1256 nSourceDim( -1 ),
1257 bHasSelectedPage( false ),
1258 mbHasHiddenMember(false)
1260 //TODO: hold pSource
1263 ScDPDimension::~ScDPDimension()
1265 //TODO: release pSource
1268 ScDPHierarchies* ScDPDimension::GetHierarchiesObject()
1270 if (!mxHierarchies.is())
1272 mxHierarchies = new ScDPHierarchies( pSource, nDim );
1274 return mxHierarchies.get();
1277 const std::optional<OUString> & ScDPDimension::GetLayoutName() const
1279 return mpLayoutName;
1282 const std::optional<OUString> & ScDPDimension::GetSubtotalName() const
1284 return mpSubtotalName;
1287 uno::Reference<container::XNameAccess> SAL_CALL ScDPDimension::getHierarchies()
1289 return GetHierarchiesObject();
1292 OUString SAL_CALL ScDPDimension::getName()
1294 if (!aName.isEmpty())
1295 return aName;
1296 else
1297 return pSource->GetData()->getDimensionName( nDim );
1300 void SAL_CALL ScDPDimension::setName( const OUString& rNewName )
1302 // used after cloning
1303 aName = rNewName;
1306 sheet::DataPilotFieldOrientation ScDPDimension::getOrientation() const
1308 return pSource->GetOrientation( nDim );
1311 bool ScDPDimension::getIsDataLayoutDimension() const
1313 return pSource->GetData()->getIsDataLayoutDimension( nDim );
1316 void ScDPDimension::setFunction(ScGeneralFunction nNew)
1318 nFunction = nNew;
1321 ScDPDimension* ScDPDimension::CreateCloneObject()
1323 OSL_ENSURE( nSourceDim < 0, "recursive duplicate - not implemented" );
1325 //TODO: set new name here, or temporary name ???
1326 OUString aNewName = aName;
1328 ScDPDimension* pNew = pSource->AddDuplicated( aNewName );
1330 pNew->aName = aNewName; //TODO: here or in source?
1331 pNew->nSourceDim = nDim; //TODO: recursive?
1333 return pNew;
1336 uno::Reference<util::XCloneable> SAL_CALL ScDPDimension::createClone()
1338 return CreateCloneObject();
1341 const ScDPItemData& ScDPDimension::GetSelectedData()
1343 if ( !pSelectedData )
1345 // find the named member to initialize pSelectedData from it, with name and value
1347 tools::Long nLevel = 0;
1349 tools::Long nHierarchy = getUsedHierarchy();
1350 if ( nHierarchy >= ScDPHierarchies::getCount() )
1351 nHierarchy = 0;
1352 ScDPLevels* pLevels = GetHierarchiesObject()->getByIndex(nHierarchy)->GetLevelsObject();
1353 tools::Long nLevCount = pLevels->getCount();
1354 if ( nLevel < nLevCount )
1356 ScDPMembers* pMembers = pLevels->getByIndex(nLevel)->GetMembersObject();
1358 //TODO: merge with ScDPMembers::getByName
1359 tools::Long nCount = pMembers->getCount();
1360 for (tools::Long i=0; i<nCount && !pSelectedData; i++)
1362 ScDPMember* pMember = pMembers->getByIndex(i);
1363 if (aSelectedPage == pMember->GetNameStr(false))
1365 pSelectedData.reset( new ScDPItemData(pMember->FillItemData()) );
1370 if ( !pSelectedData )
1371 pSelectedData.reset( new ScDPItemData(aSelectedPage) ); // default - name only
1374 return *pSelectedData;
1377 // XPropertySet
1379 uno::Reference<beans::XPropertySetInfo> SAL_CALL ScDPDimension::getPropertySetInfo()
1381 static const SfxItemPropertyMapEntry aDPDimensionMap_Impl[] =
1383 { SC_UNO_DP_FILTER, 0, cppu::UnoType<uno::Sequence<sheet::TableFilterField>>::get(), 0, 0 },
1384 { SC_UNO_DP_FLAGS, 0, cppu::UnoType<sal_Int32>::get(), beans::PropertyAttribute::READONLY, 0 },
1385 { SC_UNO_DP_FUNCTION, 0, cppu::UnoType<sheet::GeneralFunction>::get(), 0, 0 },
1386 { SC_UNO_DP_FUNCTION2, 0, cppu::UnoType<sal_Int16>::get(), 0, 0 },
1387 { SC_UNO_DP_ISDATALAYOUT, 0, cppu::UnoType<bool>::get(), beans::PropertyAttribute::READONLY, 0 },
1388 { SC_UNO_DP_NUMBERFO, 0, cppu::UnoType<sal_Int32>::get(), beans::PropertyAttribute::READONLY, 0 },
1389 { SC_UNO_DP_ORIENTATION, 0, cppu::UnoType<sheet::DataPilotFieldOrientation>::get(), 0, 0 },
1390 { SC_UNO_DP_ORIGINAL, 0, cppu::UnoType<container::XNamed>::get(), beans::PropertyAttribute::READONLY, 0 },
1391 { SC_UNO_DP_ORIGINAL_POS, 0, cppu::UnoType<sal_Int32>::get(), 0, 0 },
1392 { SC_UNO_DP_POSITION, 0, cppu::UnoType<sal_Int32>::get(), 0, 0 },
1393 { SC_UNO_DP_REFVALUE, 0, cppu::UnoType<sheet::DataPilotFieldReference>::get(), 0, 0 },
1394 { SC_UNO_DP_USEDHIERARCHY, 0, cppu::UnoType<sal_Int32>::get(), 0, 0 },
1395 { SC_UNO_DP_LAYOUTNAME, 0, cppu::UnoType<OUString>::get(), 0, 0 },
1396 { SC_UNO_DP_FIELD_SUBTOTALNAME, 0, cppu::UnoType<OUString>::get(), 0, 0 },
1397 { SC_UNO_DP_HAS_HIDDEN_MEMBER, 0, cppu::UnoType<bool>::get(), 0, 0 },
1399 static uno::Reference<beans::XPropertySetInfo> aRef =
1400 new SfxItemPropertySetInfo( aDPDimensionMap_Impl );
1401 return aRef;
1404 void SAL_CALL ScDPDimension::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue )
1406 if ( aPropertyName == SC_UNO_DP_USEDHIERARCHY )
1408 // #i52547# don't use the incomplete date hierarchy implementation - ignore the call
1410 else if ( aPropertyName == SC_UNO_DP_ORIENTATION )
1412 sheet::DataPilotFieldOrientation eEnum;
1413 if (aValue >>= eEnum)
1414 pSource->SetOrientation( nDim, eEnum );
1416 else if ( aPropertyName == SC_UNO_DP_FUNCTION )
1418 sheet::GeneralFunction eEnum;
1419 if (aValue >>= eEnum)
1420 setFunction( static_cast<ScGeneralFunction>(eEnum) );
1422 else if ( aPropertyName == SC_UNO_DP_FUNCTION2 )
1424 sal_Int16 eEnum;
1425 if (aValue >>= eEnum)
1426 setFunction( static_cast<ScGeneralFunction>(eEnum) );
1428 else if ( aPropertyName == SC_UNO_DP_REFVALUE )
1429 aValue >>= aReferenceValue;
1430 else if ( aPropertyName == SC_UNO_DP_FILTER )
1432 bool bDone = false;
1433 uno::Sequence<sheet::TableFilterField> aSeq;
1434 if (aValue >>= aSeq)
1436 sal_Int32 nLength = aSeq.getLength();
1437 if ( nLength == 0 )
1439 aSelectedPage.clear();
1440 bHasSelectedPage = false;
1441 bDone = true;
1443 else if ( nLength == 1 )
1445 const sheet::TableFilterField& rField = aSeq[0];
1446 if ( rField.Field == 0 && rField.Operator == sheet::FilterOperator_EQUAL && !rField.IsNumeric )
1448 aSelectedPage = rField.StringValue;
1449 bHasSelectedPage = true;
1450 bDone = true;
1454 if ( !bDone )
1456 OSL_FAIL("Filter property is not a single string");
1457 throw lang::IllegalArgumentException();
1459 pSelectedData.reset(); // invalid after changing aSelectedPage
1461 else if (aPropertyName == SC_UNO_DP_LAYOUTNAME)
1463 OUString aTmpName;
1464 if (aValue >>= aTmpName)
1465 mpLayoutName = aTmpName;
1467 else if (aPropertyName == SC_UNO_DP_FIELD_SUBTOTALNAME)
1469 OUString aTmpName;
1470 if (aValue >>= aTmpName)
1471 mpSubtotalName = aTmpName;
1473 else if (aPropertyName == SC_UNO_DP_HAS_HIDDEN_MEMBER)
1475 bool b = false;
1476 aValue >>= b;
1477 mbHasHiddenMember = b;
1479 else
1481 OSL_FAIL("unknown property");
1482 //TODO: THROW( UnknownPropertyException() );
1486 uno::Any SAL_CALL ScDPDimension::getPropertyValue( const OUString& aPropertyName )
1488 uno::Any aRet;
1489 if ( aPropertyName == SC_UNO_DP_POSITION )
1490 aRet <<= pSource->GetPosition( nDim );
1491 else if ( aPropertyName == SC_UNO_DP_USEDHIERARCHY )
1492 aRet <<= static_cast<sal_Int32>(getUsedHierarchy());
1493 else if ( aPropertyName == SC_UNO_DP_ORIENTATION )
1495 sheet::DataPilotFieldOrientation eVal = getOrientation();
1496 aRet <<= eVal;
1498 else if ( aPropertyName == SC_UNO_DP_FUNCTION )
1500 ScGeneralFunction nVal = getFunction();
1501 if (nVal == ScGeneralFunction::MEDIAN)
1502 nVal = ScGeneralFunction::NONE;
1503 const int nValAsInt = static_cast<int>(nVal);
1504 assert(nValAsInt >= int(css::sheet::GeneralFunction_NONE) &&
1505 nValAsInt <= int(css::sheet::GeneralFunction_VARP));
1506 aRet <<= static_cast<sheet::GeneralFunction>(nValAsInt);
1508 else if ( aPropertyName == SC_UNO_DP_FUNCTION2 )
1510 ScGeneralFunction eVal = getFunction();
1511 aRet <<= static_cast<sal_Int16>(eVal);
1513 else if ( aPropertyName == SC_UNO_DP_REFVALUE )
1514 aRet <<= aReferenceValue;
1515 else if ( aPropertyName == SC_UNO_DP_ISDATALAYOUT ) // read-only properties
1516 aRet <<= getIsDataLayoutDimension();
1517 else if ( aPropertyName == SC_UNO_DP_NUMBERFO )
1519 sal_Int32 nFormat = 0;
1520 ScGeneralFunction eFunc = getFunction();
1521 // #i63745# don't use source format for "count"
1522 if ( eFunc != ScGeneralFunction::COUNT && eFunc != ScGeneralFunction::COUNTNUMS )
1523 nFormat = pSource->GetData()->GetNumberFormat( ( nSourceDim >= 0 ) ? nSourceDim : nDim );
1525 switch ( aReferenceValue.ReferenceType )
1527 case sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE:
1528 case sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE:
1529 case sheet::DataPilotFieldReferenceType::ROW_PERCENTAGE:
1530 case sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE:
1531 case sheet::DataPilotFieldReferenceType::TOTAL_PERCENTAGE:
1532 nFormat = pSource->GetData()->GetNumberFormatByIdx( NF_PERCENT_DEC2 );
1533 break;
1534 case sheet::DataPilotFieldReferenceType::INDEX:
1535 nFormat = pSource->GetData()->GetNumberFormatByIdx( NF_NUMBER_SYSTEM );
1536 break;
1537 default:
1538 break;
1541 aRet <<= nFormat;
1543 else if ( aPropertyName == SC_UNO_DP_ORIGINAL )
1545 uno::Reference<container::XNamed> xOriginal;
1546 if (nSourceDim >= 0)
1547 xOriginal = pSource->GetDimensionsObject()->getByIndex(nSourceDim);
1548 aRet <<= xOriginal;
1550 else if (aPropertyName == SC_UNO_DP_ORIGINAL_POS)
1552 aRet <<= nSourceDim;
1554 else if ( aPropertyName == SC_UNO_DP_FILTER )
1556 if ( bHasSelectedPage )
1558 // single filter field: first field equal to selected string
1559 sheet::TableFilterField aField( sheet::FilterConnection_AND, 0,
1560 sheet::FilterOperator_EQUAL, false, 0.0, aSelectedPage );
1561 aRet <<= uno::Sequence<sheet::TableFilterField>( &aField, 1 );
1563 else
1564 aRet <<= uno::Sequence<sheet::TableFilterField>(0);
1566 else if (aPropertyName == SC_UNO_DP_LAYOUTNAME)
1567 aRet <<= mpLayoutName ? *mpLayoutName : OUString();
1568 else if (aPropertyName == SC_UNO_DP_FIELD_SUBTOTALNAME)
1569 aRet <<= mpSubtotalName ? *mpSubtotalName : OUString();
1570 else if (aPropertyName == SC_UNO_DP_HAS_HIDDEN_MEMBER)
1571 aRet <<= mbHasHiddenMember;
1572 else if (aPropertyName == SC_UNO_DP_FLAGS)
1574 aRet <<= sal_Int32(0); // tabular data: all orientations are possible
1576 else
1578 OSL_FAIL("unknown property");
1579 //TODO: THROW( UnknownPropertyException() );
1581 return aRet;
1584 SC_IMPL_DUMMY_PROPERTY_LISTENER( ScDPDimension )
1586 ScDPHierarchies::ScDPHierarchies( ScDPSource* pSrc, tools::Long nD ) :
1587 pSource( pSrc ),
1588 nDim( nD )
1590 //TODO: hold pSource
1593 ScDPHierarchies::~ScDPHierarchies()
1595 //TODO: release pSource
1598 // very simple XNameAccess implementation using getCount/getByIndex
1600 uno::Any SAL_CALL ScDPHierarchies::getByName( const OUString& aName )
1602 tools::Long nCount = getCount();
1603 for (tools::Long i=0; i<nCount; i++)
1604 if ( getByIndex(i)->getName() == aName )
1606 uno::Reference<container::XNamed> xNamed = getByIndex(i);
1607 uno::Any aRet;
1608 aRet <<= xNamed;
1609 return aRet;
1612 throw container::NoSuchElementException();
1615 uno::Sequence<OUString> SAL_CALL ScDPHierarchies::getElementNames()
1617 tools::Long nCount = getCount();
1618 uno::Sequence<OUString> aSeq(nCount);
1619 OUString* pArr = aSeq.getArray();
1620 for (tools::Long i=0; i<nCount; i++)
1621 pArr[i] = getByIndex(i)->getName();
1622 return aSeq;
1625 sal_Bool SAL_CALL ScDPHierarchies::hasByName( const OUString& aName )
1627 tools::Long nCount = getCount();
1628 for (tools::Long i=0; i<nCount; i++)
1629 if ( getByIndex(i)->getName() == aName )
1630 return true;
1631 return false;
1634 uno::Type SAL_CALL ScDPHierarchies::getElementType()
1636 return cppu::UnoType<container::XNamed>::get();
1639 sal_Bool SAL_CALL ScDPHierarchies::hasElements()
1641 return ( getCount() > 0 );
1644 // end of XNameAccess implementation
1646 sal_Int32 ScDPHierarchies::getCount()
1648 return nHierCount;
1651 ScDPHierarchy* ScDPHierarchies::getByIndex(tools::Long nIndex) const
1653 // pass hierarchy index to new object in case the implementation
1654 // will be extended to more than one hierarchy
1656 if ( nIndex >= 0 && nIndex < nHierCount )
1658 if ( !ppHiers )
1660 const_cast<ScDPHierarchies*>(this)->ppHiers.reset( new rtl::Reference<ScDPHierarchy>[nHierCount] );
1661 for (sal_Int32 i=0; i<nHierCount; i++)
1662 ppHiers[i] = nullptr;
1664 if ( !ppHiers[nIndex].is() )
1666 ppHiers[nIndex] = new ScDPHierarchy( pSource, nDim, nIndex );
1669 return ppHiers[nIndex].get();
1672 return nullptr; //TODO: exception?
1675 ScDPHierarchy::ScDPHierarchy( ScDPSource* pSrc, sal_Int32 nD, sal_Int32 nH ) :
1676 pSource( pSrc ),
1677 nDim( nD ),
1678 nHier( nH )
1680 //TODO: hold pSource
1683 ScDPHierarchy::~ScDPHierarchy()
1685 //TODO: release pSource
1688 ScDPLevels* ScDPHierarchy::GetLevelsObject()
1690 if (!mxLevels.is())
1692 mxLevels = new ScDPLevels( pSource, nDim, nHier );
1694 return mxLevels.get();
1697 uno::Reference<container::XNameAccess> SAL_CALL ScDPHierarchy::getLevels()
1699 return GetLevelsObject();
1702 OUString SAL_CALL ScDPHierarchy::getName()
1704 OUString aRet; //TODO: globstr-ID !!!!
1705 switch (nHier)
1707 case SC_DAPI_HIERARCHY_FLAT:
1708 aRet = "flat";
1709 break; //TODO: name ???????
1710 case SC_DAPI_HIERARCHY_QUARTER:
1711 aRet = "Quarter";
1712 break; //TODO: name ???????
1713 case SC_DAPI_HIERARCHY_WEEK:
1714 aRet = "Week";
1715 break; //TODO: name ???????
1716 default:
1717 OSL_FAIL( "ScDPHierarchy::getName: unexpected hierarchy" );
1718 break;
1720 return aRet;
1723 void SAL_CALL ScDPHierarchy::setName( const OUString& /* rNewName */ )
1725 OSL_FAIL("not implemented"); //TODO: exception?
1728 ScDPLevels::ScDPLevels( ScDPSource* pSrc, sal_Int32 nD, sal_Int32 nH ) :
1729 pSource( pSrc ),
1730 nDim( nD ),
1731 nHier( nH )
1733 //TODO: hold pSource
1735 // text columns have only one level
1737 tools::Long nSrcDim = pSource->GetSourceDim( nDim );
1738 if ( pSource->IsDateDimension( nSrcDim ) )
1740 switch ( nHier )
1742 case SC_DAPI_HIERARCHY_FLAT: nLevCount = SC_DAPI_FLAT_LEVELS; break;
1743 case SC_DAPI_HIERARCHY_QUARTER: nLevCount = SC_DAPI_QUARTER_LEVELS; break;
1744 case SC_DAPI_HIERARCHY_WEEK: nLevCount = SC_DAPI_WEEK_LEVELS; break;
1745 default:
1746 OSL_FAIL("wrong hierarchy");
1747 nLevCount = 0;
1750 else
1751 nLevCount = 1;
1754 ScDPLevels::~ScDPLevels()
1756 //TODO: release pSource
1759 // very simple XNameAccess implementation using getCount/getByIndex
1761 uno::Any SAL_CALL ScDPLevels::getByName( const OUString& aName )
1763 tools::Long nCount = getCount();
1764 for (tools::Long i=0; i<nCount; i++)
1765 if ( getByIndex(i)->getName() == aName )
1767 uno::Reference<container::XNamed> xNamed = getByIndex(i);
1768 uno::Any aRet;
1769 aRet <<= xNamed;
1770 return aRet;
1773 throw container::NoSuchElementException();
1776 uno::Sequence<OUString> SAL_CALL ScDPLevels::getElementNames()
1778 tools::Long nCount = getCount();
1779 uno::Sequence<OUString> aSeq(nCount);
1780 OUString* pArr = aSeq.getArray();
1781 for (tools::Long i=0; i<nCount; i++)
1782 pArr[i] = getByIndex(i)->getName();
1783 return aSeq;
1786 sal_Bool SAL_CALL ScDPLevels::hasByName( const OUString& aName )
1788 tools::Long nCount = getCount();
1789 for (tools::Long i=0; i<nCount; i++)
1790 if ( getByIndex(i)->getName() == aName )
1791 return true;
1792 return false;
1795 uno::Type SAL_CALL ScDPLevels::getElementType()
1797 return cppu::UnoType<container::XNamed>::get();
1800 sal_Bool SAL_CALL ScDPLevels::hasElements()
1802 return ( getCount() > 0 );
1805 // end of XNameAccess implementation
1807 sal_Int32 ScDPLevels::getCount() const
1809 return nLevCount;
1812 ScDPLevel* ScDPLevels::getByIndex(sal_Int32 nIndex) const
1814 if ( nIndex >= 0 && nIndex < nLevCount )
1816 if ( !ppLevs )
1818 const_cast<ScDPLevels*>(this)->ppLevs.reset(new rtl::Reference<ScDPLevel>[nLevCount] );
1819 for (tools::Long i=0; i<nLevCount; i++)
1820 ppLevs[i] = nullptr;
1822 if ( !ppLevs[nIndex].is() )
1824 ppLevs[nIndex] = new ScDPLevel( pSource, nDim, nHier, nIndex );
1827 return ppLevs[nIndex].get();
1830 return nullptr; //TODO: exception?
1833 namespace {
1835 class ScDPGlobalMembersOrder
1837 ScDPLevel& rLevel;
1838 bool bAscending;
1840 public:
1841 ScDPGlobalMembersOrder( ScDPLevel& rLev, bool bAsc ) :
1842 rLevel(rLev),
1843 bAscending(bAsc)
1846 bool operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const;
1851 bool ScDPGlobalMembersOrder::operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const
1853 sal_Int32 nCompare = 0;
1854 // seems that some ::std::sort() implementations pass the same index twice
1855 if( nIndex1 != nIndex2 )
1857 ScDPMembers* pMembers = rLevel.GetMembersObject();
1858 ScDPMember* pMember1 = pMembers->getByIndex(nIndex1);
1859 ScDPMember* pMember2 = pMembers->getByIndex(nIndex2);
1860 nCompare = pMember1->Compare( *pMember2 );
1862 return bAscending ? (nCompare < 0) : (nCompare > 0);
1865 ScDPLevel::ScDPLevel( ScDPSource* pSrc, sal_Int32 nD, sal_Int32 nH, sal_Int32 nL ) :
1866 pSource( pSrc ),
1867 nDim( nD ),
1868 nHier( nH ),
1869 nLev( nL ),
1870 aSortInfo( OUString(), true, sheet::DataPilotFieldSortMode::NAME ), // default: sort by name
1871 nSortMeasure( 0 ),
1872 nAutoMeasure( 0 ),
1873 bShowEmpty( false ),
1874 bEnableLayout( false ),
1875 bRepeatItemLabels( false )
1877 //TODO: hold pSource
1878 // aSubTotals is empty
1881 ScDPLevel::~ScDPLevel()
1883 //TODO: release pSource
1886 void ScDPLevel::EvaluateSortOrder()
1888 switch (aSortInfo.Mode)
1890 case sheet::DataPilotFieldSortMode::DATA:
1892 // find index of measure (index among data dimensions)
1894 tools::Long nMeasureCount = pSource->GetDataDimensionCount();
1895 for (tools::Long nMeasure=0; nMeasure<nMeasureCount; nMeasure++)
1897 if (pSource->GetDataDimName(nMeasure) == aSortInfo.Field)
1899 nSortMeasure = nMeasure;
1900 break;
1904 //TODO: error if not found?
1906 break;
1907 case sheet::DataPilotFieldSortMode::MANUAL:
1908 case sheet::DataPilotFieldSortMode::NAME:
1910 ScDPMembers* pLocalMembers = GetMembersObject();
1911 tools::Long nCount = pLocalMembers->getCount();
1913 aGlobalOrder.resize( nCount );
1914 for (tools::Long nPos=0; nPos<nCount; nPos++)
1915 aGlobalOrder[nPos] = nPos;
1917 // allow manual or name (manual is always ascending)
1918 bool bAscending = ( aSortInfo.Mode == sheet::DataPilotFieldSortMode::MANUAL || aSortInfo.IsAscending );
1919 ScDPGlobalMembersOrder aComp( *this, bAscending );
1920 ::std::sort( aGlobalOrder.begin(), aGlobalOrder.end(), aComp );
1922 break;
1925 if ( !aAutoShowInfo.IsEnabled )
1926 return;
1928 // find index of measure (index among data dimensions)
1930 tools::Long nMeasureCount = pSource->GetDataDimensionCount();
1931 for (tools::Long nMeasure=0; nMeasure<nMeasureCount; nMeasure++)
1933 if (pSource->GetDataDimName(nMeasure) == aAutoShowInfo.DataField)
1935 nAutoMeasure = nMeasure;
1936 break;
1940 //TODO: error if not found?
1943 void ScDPLevel::SetEnableLayout(bool bSet)
1945 bEnableLayout = bSet;
1948 ScDPMembers* ScDPLevel::GetMembersObject()
1950 if (!mxMembers.is())
1952 mxMembers = new ScDPMembers( pSource, nDim, nHier, nLev );
1954 return mxMembers.get();
1957 uno::Reference<sheet::XMembersAccess> SAL_CALL ScDPLevel::getMembers()
1959 return GetMembersObject();
1962 uno::Sequence<sheet::MemberResult> SAL_CALL ScDPLevel::getResults()
1964 const uno::Sequence<sheet::MemberResult>* pRes = pSource->GetMemberResults( this );
1965 if (pRes)
1966 return *pRes;
1968 return {}; //TODO: Error?
1971 OUString SAL_CALL ScDPLevel::getName()
1973 tools::Long nSrcDim = pSource->GetSourceDim( nDim );
1974 if ( pSource->IsDateDimension( nSrcDim ) )
1976 OUString aRet; //TODO: globstr-ID !!!!
1978 if ( nHier == SC_DAPI_HIERARCHY_QUARTER )
1980 switch ( nLev )
1982 case SC_DAPI_LEVEL_YEAR:
1983 aRet = "Year";
1984 break;
1985 case SC_DAPI_LEVEL_QUARTER:
1986 aRet = "Quarter";
1987 break;
1988 case SC_DAPI_LEVEL_MONTH:
1989 aRet = "Month";
1990 break;
1991 case SC_DAPI_LEVEL_DAY:
1992 aRet = "Day";
1993 break;
1994 default:
1995 OSL_FAIL( "ScDPLevel::getName: unexpected level" );
1996 break;
1999 else if ( nHier == SC_DAPI_HIERARCHY_WEEK )
2001 switch ( nLev )
2003 case SC_DAPI_LEVEL_YEAR:
2004 aRet = "Year";
2005 break;
2006 case SC_DAPI_LEVEL_WEEK:
2007 aRet = "Week";
2008 break;
2009 case SC_DAPI_LEVEL_WEEKDAY:
2010 aRet = "Weekday";
2011 break;
2012 default:
2013 OSL_FAIL( "ScDPLevel::getName: unexpected level" );
2014 break;
2017 if (!aRet.isEmpty())
2018 return aRet;
2021 ScDPDimension* pDim = pSource->GetDimensionsObject()->getByIndex(nSrcDim);
2022 if (!pDim)
2023 return OUString();
2025 return pDim->getName();
2028 void SAL_CALL ScDPLevel::setName( const OUString& /* rNewName */ )
2030 OSL_FAIL("not implemented"); //TODO: exception?
2033 uno::Sequence<sal_Int16> ScDPLevel::getSubTotals() const
2035 //TODO: separate functions for settings and evaluation?
2037 tools::Long nSrcDim = pSource->GetSourceDim( nDim );
2038 if ( !pSource->SubTotalAllowed( nSrcDim ) )
2039 return {};
2041 return aSubTotals;
2044 // XPropertySet
2046 uno::Reference<beans::XPropertySetInfo> SAL_CALL ScDPLevel::getPropertySetInfo()
2048 static const SfxItemPropertyMapEntry aDPLevelMap_Impl[] =
2050 //TODO: change type of AutoShow/Layout/Sorting to API struct when available
2051 { SC_UNO_DP_AUTOSHOW, 0, cppu::UnoType<sheet::DataPilotFieldAutoShowInfo>::get(), 0, 0 },
2052 { SC_UNO_DP_LAYOUT, 0, cppu::UnoType<sheet::DataPilotFieldLayoutInfo>::get(), 0, 0 },
2053 { SC_UNO_DP_SHOWEMPTY, 0, cppu::UnoType<bool>::get(), 0, 0 },
2054 { SC_UNO_DP_REPEATITEMLABELS, 0, cppu::UnoType<bool>::get(), 0, 0 },
2055 { SC_UNO_DP_SORTING, 0, cppu::UnoType<sheet::DataPilotFieldSortInfo>::get(), 0, 0 },
2056 { SC_UNO_DP_SUBTOTAL, 0, cppu::UnoType<uno::Sequence<sheet::GeneralFunction>>::get(), 0, 0 },
2057 { SC_UNO_DP_SUBTOTAL2, 0, cppu::UnoType<uno::Sequence<sal_Int16>>::get(), 0, 0 },
2059 static uno::Reference<beans::XPropertySetInfo> aRef =
2060 new SfxItemPropertySetInfo( aDPLevelMap_Impl );
2061 return aRef;
2064 void SAL_CALL ScDPLevel::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue )
2066 if ( aPropertyName == SC_UNO_DP_SHOWEMPTY )
2067 bShowEmpty = lcl_GetBoolFromAny(aValue);
2068 else if ( aPropertyName == SC_UNO_DP_REPEATITEMLABELS )
2069 bRepeatItemLabels = lcl_GetBoolFromAny(aValue);
2070 else if ( aPropertyName == SC_UNO_DP_SUBTOTAL )
2072 uno::Sequence<sheet::GeneralFunction> aSeq;
2073 aValue >>= aSeq;
2074 aSubTotals.realloc(aSeq.getLength());
2075 std::transform(std::cbegin(aSeq), std::cend(aSeq), aSubTotals.getArray(),
2076 [](const sheet::GeneralFunction& rFunc) -> sal_Int16 {
2077 return static_cast<sal_Int16>(rFunc); });
2079 else if ( aPropertyName == SC_UNO_DP_SUBTOTAL2 )
2080 aValue >>= aSubTotals;
2081 else if ( aPropertyName == SC_UNO_DP_SORTING )
2082 aValue >>= aSortInfo;
2083 else if ( aPropertyName == SC_UNO_DP_AUTOSHOW )
2084 aValue >>= aAutoShowInfo;
2085 else if ( aPropertyName == SC_UNO_DP_LAYOUT )
2086 aValue >>= aLayoutInfo;
2087 else
2089 OSL_FAIL("unknown property");
2093 uno::Any SAL_CALL ScDPLevel::getPropertyValue( const OUString& aPropertyName )
2095 uno::Any aRet;
2096 if ( aPropertyName == SC_UNO_DP_SHOWEMPTY )
2097 aRet <<= bShowEmpty;
2098 else if ( aPropertyName == SC_UNO_DP_REPEATITEMLABELS )
2099 aRet <<= bRepeatItemLabels;
2100 else if ( aPropertyName == SC_UNO_DP_SUBTOTAL )
2102 const uno::Sequence<sal_Int16> aSeq = getSubTotals();
2103 uno::Sequence<sheet::GeneralFunction> aNewSeq(aSeq.getLength());
2104 std::transform(aSeq.begin(), aSeq.end(), aNewSeq.getArray(),
2105 [](const sal_Int16 nFunc) -> sheet::GeneralFunction {
2106 if (nFunc == sheet::GeneralFunction2::MEDIAN)
2107 return sheet::GeneralFunction_NONE;
2108 return static_cast<sheet::GeneralFunction>(nFunc);
2111 aRet <<= aNewSeq;
2113 else if ( aPropertyName == SC_UNO_DP_SUBTOTAL2 )
2115 uno::Sequence<sal_Int16> aSeq = getSubTotals(); //TODO: avoid extra copy?
2116 aRet <<= aSeq;
2118 else if ( aPropertyName == SC_UNO_DP_SORTING )
2119 aRet <<= aSortInfo;
2120 else if ( aPropertyName == SC_UNO_DP_AUTOSHOW )
2121 aRet <<= aAutoShowInfo;
2122 else if ( aPropertyName == SC_UNO_DP_LAYOUT )
2123 aRet <<= aLayoutInfo;
2124 else if (aPropertyName == SC_UNO_DP_LAYOUTNAME)
2126 // read only property
2127 tools::Long nSrcDim = pSource->GetSourceDim(nDim);
2128 ScDPDimension* pDim = pSource->GetDimensionsObject()->getByIndex(nSrcDim);
2129 if (!pDim)
2130 return aRet;
2132 const std::optional<OUString> & pLayoutName = pDim->GetLayoutName();
2133 if (!pLayoutName)
2134 return aRet;
2136 aRet <<= *pLayoutName;
2138 else
2140 OSL_FAIL("unknown property");
2142 return aRet;
2145 SC_IMPL_DUMMY_PROPERTY_LISTENER( ScDPLevel )
2147 ScDPMembers::ScDPMembers( ScDPSource* pSrc, sal_Int32 nD, sal_Int32 nH, sal_Int32 nL ) :
2148 pSource( pSrc ),
2149 nDim( nD ),
2150 nHier( nH ),
2151 nLev( nL )
2153 //TODO: hold pSource
2155 tools::Long nSrcDim = pSource->GetSourceDim( nDim );
2156 if ( pSource->IsDataLayoutDimension(nSrcDim) )
2157 nMbrCount = pSource->GetDataDimensionCount();
2158 else if ( nHier != SC_DAPI_HIERARCHY_FLAT && pSource->IsDateDimension( nSrcDim ) )
2160 nMbrCount = 0;
2161 if ( nHier == SC_DAPI_HIERARCHY_QUARTER )
2163 switch (nLev)
2165 case SC_DAPI_LEVEL_YEAR:
2167 const ScDPItemData* pLastNumData = nullptr;
2168 for ( SCROW n = 0; n < static_cast<SCROW>(pSource->GetData()->GetColumnEntries(nDim).size()); n-- )
2170 const ScDPItemData* pData = GetSrcItemDataByIndex( n );
2171 if ( pData && pData->HasStringData() )
2172 break;
2173 else
2174 pLastNumData = pData;
2177 if ( pLastNumData )
2179 const ScDPItemData* pFirstData = GetSrcItemDataByIndex( 0 );
2180 double fFirstVal = pFirstData->GetValue();
2181 double fLastVal = pLastNumData->GetValue();
2183 tools::Long nFirstYear = pSource->GetData()->GetDatePart(
2184 static_cast<tools::Long>(::rtl::math::approxFloor( fFirstVal )),
2185 nHier, nLev );
2186 tools::Long nLastYear = pSource->GetData()->GetDatePart(
2187 static_cast<tools::Long>(::rtl::math::approxFloor( fLastVal )),
2188 nHier, nLev );
2190 nMbrCount = nLastYear + 1 - nFirstYear;
2192 else
2193 nMbrCount = 0; // no values
2195 break;
2196 case SC_DAPI_LEVEL_QUARTER: nMbrCount = 4; break;
2197 case SC_DAPI_LEVEL_MONTH: nMbrCount = 12; break;
2198 case SC_DAPI_LEVEL_DAY: nMbrCount = 31; break;
2199 default:
2200 OSL_FAIL( "ScDPMembers::ScDPMembers: unexpected level" );
2201 break;
2204 else if ( nHier == SC_DAPI_HIERARCHY_WEEK )
2206 switch (nLev)
2208 case SC_DAPI_LEVEL_YEAR: nMbrCount = 1; break; //TODO: get years from source
2209 case SC_DAPI_LEVEL_WEEK: nMbrCount = 53; break;
2210 case SC_DAPI_LEVEL_WEEKDAY: nMbrCount = 7; break;
2211 default:
2212 OSL_FAIL( "ScDPMembers::ScDPMembers: unexpected level" );
2213 break;
2217 else
2218 nMbrCount = pSource->GetData()->GetMembersCount( nSrcDim );
2221 ScDPMembers::~ScDPMembers()
2225 // XNameAccess implementation using getCount/getByIndex
2227 sal_Int32 ScDPMembers::GetIndexFromName( const OUString& rName ) const
2229 if ( aHashMap.empty() )
2231 // store the index for each name
2233 sal_Int32 nCount = getCount();
2234 for (sal_Int32 i=0; i<nCount; i++)
2235 aHashMap[ getByIndex(i)->getName() ] = i;
2238 ScDPMembersHashMap::const_iterator aIter = aHashMap.find( rName );
2239 if ( aIter != aHashMap.end() )
2240 return aIter->second; // found index
2241 else
2242 return -1; // not found
2245 uno::Any SAL_CALL ScDPMembers::getByName( const OUString& aName )
2247 sal_Int32 nIndex = GetIndexFromName( aName );
2248 if ( nIndex >= 0 )
2250 uno::Reference<container::XNamed> xNamed = getByIndex(nIndex);
2251 uno::Any aRet;
2252 aRet <<= xNamed;
2253 return aRet;
2256 throw container::NoSuchElementException();
2259 uno::Sequence<OUString> SAL_CALL ScDPMembers::getElementNames()
2261 return getElementNames( false );
2264 sal_Bool SAL_CALL ScDPMembers::hasByName( const OUString& aName )
2266 return ( GetIndexFromName( aName ) >= 0 );
2269 uno::Type SAL_CALL ScDPMembers::getElementType()
2271 return cppu::UnoType<container::XNamed>::get();
2274 sal_Bool SAL_CALL ScDPMembers::hasElements()
2276 return ( getCount() > 0 );
2279 // end of XNameAccess implementation
2281 // XMembersAccess implementation
2283 uno::Sequence<OUString> SAL_CALL ScDPMembers::getLocaleIndependentElementNames()
2285 return getElementNames( true );
2288 // end of XMembersAccess implementation
2290 uno::Sequence<OUString> ScDPMembers::getElementNames( bool bLocaleIndependent ) const
2292 // Return list of names in sorted order,
2293 // so it's displayed in that order in the field options dialog.
2294 // Sorting is done at the level object (parent of this).
2296 ScDPLevel* pLevel = pSource->GetDimensionsObject()->getByIndex(nDim)->
2297 GetHierarchiesObject()->getByIndex(nHier)->GetLevelsObject()->getByIndex(nLev);
2298 pLevel->EvaluateSortOrder();
2299 const std::vector<sal_Int32>& rGlobalOrder = pLevel->GetGlobalOrder();
2300 bool bSort = !rGlobalOrder.empty();
2302 tools::Long nCount = getCount();
2303 uno::Sequence<OUString> aSeq(nCount);
2304 OUString* pArr = aSeq.getArray();
2305 for (tools::Long i=0; i<nCount; i++)
2306 pArr[i] = getByIndex(bSort ? rGlobalOrder[i] : i)->GetNameStr( bLocaleIndependent);
2307 return aSeq;
2310 sal_Int32 ScDPMembers::getMinMembers() const
2312 // used in lcl_CountMinMembers
2314 sal_Int32 nVisCount = 0;
2315 if (!maMembers.empty())
2317 nVisCount = std::count_if(maMembers.begin(), maMembers.end(), [](const rtl::Reference<ScDPMember>& pMbr) {
2318 // count only visible with details (default is true for both)
2319 return !pMbr || (pMbr->isVisible() && pMbr->getShowDetails()); });
2321 else
2322 nVisCount = nMbrCount; // default for all
2324 return nVisCount;
2327 ScDPMember* ScDPMembers::getByIndex(sal_Int32 nIndex) const
2329 // result of GetColumnEntries must not change between ScDPMembers ctor
2330 // and all calls to getByIndex
2332 if ( nIndex >= 0 && nIndex < nMbrCount )
2334 if (maMembers.empty())
2335 maMembers.resize(nMbrCount);
2337 if (!maMembers[nIndex])
2339 rtl::Reference<ScDPMember> pNew;
2340 sal_Int32 nSrcDim = pSource->GetSourceDim( nDim );
2341 if ( pSource->IsDataLayoutDimension(nSrcDim) )
2343 // empty name (never shown, not used for lookup)
2344 pNew.set(new ScDPMember(pSource, nDim, nHier, nLev, 0));
2346 else if ( nHier != SC_DAPI_HIERARCHY_FLAT && pSource->IsDateDimension( nSrcDim ) )
2348 sal_Int32 nGroupBy = 0;
2349 sal_Int32 nVal = 0;
2350 OUString aName;
2352 if ( nLev == SC_DAPI_LEVEL_YEAR ) // YEAR is in both hierarchies
2354 //TODO: cache year range here!
2356 double fFirstVal = pSource->GetData()->GetMemberByIndex( nSrcDim, 0 )->GetValue();
2357 tools::Long nFirstYear = pSource->GetData()->GetDatePart(
2358 static_cast<tools::Long>(::rtl::math::approxFloor( fFirstVal )),
2359 nHier, nLev );
2361 nVal = nFirstYear + nIndex;
2363 else if ( nHier == SC_DAPI_HIERARCHY_WEEK && nLev == SC_DAPI_LEVEL_WEEKDAY )
2365 nVal = nIndex; // DayOfWeek is 0-based
2366 aName = ScGlobal::GetCalendar().getDisplayName(
2367 css::i18n::CalendarDisplayIndex::DAY,
2368 sal::static_int_cast<sal_Int16>(nVal), 0 );
2370 else if ( nHier == SC_DAPI_HIERARCHY_QUARTER && nLev == SC_DAPI_LEVEL_MONTH )
2372 nVal = nIndex; // Month is 0-based
2373 aName = ScGlobal::GetCalendar().getDisplayName(
2374 css::i18n::CalendarDisplayIndex::MONTH,
2375 sal::static_int_cast<sal_Int16>(nVal), 0 );
2377 else
2378 nVal = nIndex + 1; // Quarter, Day, Week are 1-based
2380 switch (nLev)
2382 case SC_DAPI_LEVEL_YEAR:
2383 nGroupBy = sheet::DataPilotFieldGroupBy::YEARS;
2384 break;
2385 case SC_DAPI_LEVEL_QUARTER:
2386 case SC_DAPI_LEVEL_WEEK:
2387 nGroupBy = sheet::DataPilotFieldGroupBy::QUARTERS;
2388 break;
2389 case SC_DAPI_LEVEL_MONTH:
2390 case SC_DAPI_LEVEL_WEEKDAY:
2391 nGroupBy = sheet::DataPilotFieldGroupBy::MONTHS;
2392 break;
2393 case SC_DAPI_LEVEL_DAY:
2394 nGroupBy = sheet::DataPilotFieldGroupBy::DAYS;
2395 break;
2396 default:
2399 if (aName.isEmpty())
2400 aName = OUString::number(nVal);
2402 ScDPItemData aData(nGroupBy, nVal);
2403 SCROW nId = pSource->GetCache()->GetIdByItemData(nDim, aData);
2404 pNew.set(new ScDPMember(pSource, nDim, nHier, nLev, nId));
2406 else
2408 const std::vector<SCROW>& memberIndexs = pSource->GetData()->GetColumnEntries(nSrcDim);
2409 pNew.set(new ScDPMember(pSource, nDim, nHier, nLev, memberIndexs[nIndex]));
2411 maMembers[nIndex] = std::move(pNew);
2414 return maMembers[nIndex].get();
2417 return nullptr; //TODO: exception?
2420 ScDPMember::ScDPMember(
2421 ScDPSource* pSrc, sal_Int32 nD, sal_Int32 nH, sal_Int32 nL, SCROW nIndex) :
2422 pSource( pSrc ),
2423 nDim( nD ),
2424 nHier( nH ),
2425 nLev( nL ),
2426 mnDataId( nIndex ),
2427 nPosition( -1 ),
2428 bVisible( true ),
2429 bShowDet( true )
2431 //TODO: hold pSource
2434 ScDPMember::~ScDPMember()
2436 //TODO: release pSource
2439 bool ScDPMember::IsNamedItem(SCROW nIndex) const
2441 sal_Int32 nSrcDim = pSource->GetSourceDim( nDim );
2442 if ( nHier != SC_DAPI_HIERARCHY_FLAT && pSource->IsDateDimension( nSrcDim ) )
2444 const ScDPItemData* pData = pSource->GetCache()->GetItemDataById(nDim, nIndex);
2445 if (pData->IsValue())
2447 tools::Long nComp = pSource->GetData()->GetDatePart(
2448 static_cast<tools::Long>(::rtl::math::approxFloor( pData->GetValue() )),
2449 nHier, nLev );
2450 // fValue is converted from integer, so simple comparison works
2451 const ScDPItemData* pData2 = GetItemData();
2452 return pData2 && nComp == pData2->GetValue();
2456 return nIndex == mnDataId;
2459 sal_Int32 ScDPMember::Compare( const ScDPMember& rOther ) const
2461 if ( nPosition >= 0 )
2463 if ( rOther.nPosition >= 0 )
2465 OSL_ENSURE( nPosition != rOther.nPosition, "same position for two members" );
2466 return ( nPosition < rOther.nPosition ) ? -1 : 1;
2468 else
2470 // only this has a position - members with specified positions come before those without
2471 return -1;
2474 else if ( rOther.nPosition >= 0 )
2476 // only rOther has a position
2477 return 1;
2480 // no positions set - compare names
2481 return pSource->GetData()->Compare( pSource->GetSourceDim(nDim),mnDataId,rOther.GetItemDataId());
2484 ScDPItemData ScDPMember::FillItemData() const
2486 //TODO: handle date hierarchy...
2488 const ScDPItemData* pData = GetItemData();
2489 return (pData ? *pData : ScDPItemData());
2492 const std::optional<OUString> & ScDPMember::GetLayoutName() const
2494 return mpLayoutName;
2497 OUString ScDPMember::GetNameStr( bool bLocaleIndependent ) const
2499 const ScDPItemData* pData = GetItemData();
2500 if (pData)
2501 return pSource->GetData()->GetFormattedString(nDim, *pData, bLocaleIndependent);
2502 return OUString();
2505 OUString SAL_CALL ScDPMember::getName()
2507 return GetNameStr( false );
2510 void SAL_CALL ScDPMember::setName( const OUString& /* rNewName */ )
2512 OSL_FAIL("not implemented"); //TODO: exception?
2515 // XPropertySet
2517 uno::Reference<beans::XPropertySetInfo> SAL_CALL ScDPMember::getPropertySetInfo()
2519 static const SfxItemPropertyMapEntry aDPMemberMap_Impl[] =
2521 { SC_UNO_DP_ISVISIBLE, 0, cppu::UnoType<bool>::get(), 0, 0 },
2522 { SC_UNO_DP_POSITION, 0, cppu::UnoType<sal_Int32>::get(), 0, 0 },
2523 { SC_UNO_DP_SHOWDETAILS, 0, cppu::UnoType<bool>::get(), 0, 0 },
2524 { SC_UNO_DP_LAYOUTNAME, 0, cppu::UnoType<OUString>::get(), 0, 0 },
2526 static uno::Reference<beans::XPropertySetInfo> aRef =
2527 new SfxItemPropertySetInfo( aDPMemberMap_Impl );
2528 return aRef;
2531 void SAL_CALL ScDPMember::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue )
2533 if ( aPropertyName == SC_UNO_DP_ISVISIBLE )
2534 bVisible = lcl_GetBoolFromAny(aValue);
2535 else if ( aPropertyName == SC_UNO_DP_SHOWDETAILS )
2536 bShowDet = lcl_GetBoolFromAny(aValue);
2537 else if ( aPropertyName == SC_UNO_DP_POSITION )
2538 aValue >>= nPosition;
2539 else if (aPropertyName == SC_UNO_DP_LAYOUTNAME)
2541 OUString aName;
2542 if (aValue >>= aName)
2543 mpLayoutName = aName;
2545 else
2547 OSL_FAIL("unknown property");
2551 uno::Any SAL_CALL ScDPMember::getPropertyValue( const OUString& aPropertyName )
2553 uno::Any aRet;
2554 if ( aPropertyName == SC_UNO_DP_ISVISIBLE )
2555 aRet <<= bVisible;
2556 else if ( aPropertyName == SC_UNO_DP_SHOWDETAILS )
2557 aRet <<= bShowDet;
2558 else if ( aPropertyName == SC_UNO_DP_POSITION )
2559 aRet <<= nPosition;
2560 else if (aPropertyName == SC_UNO_DP_LAYOUTNAME)
2561 aRet <<= mpLayoutName ? *mpLayoutName : OUString();
2562 else
2564 OSL_FAIL("unknown property");
2566 return aRet;
2569 SC_IMPL_DUMMY_PROPERTY_LISTENER( ScDPMember )
2571 const ScDPCache* ScDPSource::GetCache()
2573 OSL_ENSURE( GetData() , "empty ScDPTableData pointer");
2574 return ( GetData()!=nullptr ) ? &GetData()->GetCacheTable().getCache() : nullptr ;
2577 const ScDPItemData* ScDPMember::GetItemData() const
2579 const ScDPItemData* pData = pSource->GetItemDataById(nDim, mnDataId);
2580 SAL_WARN_IF( !pData, "sc.core", "ScDPMember::GetItemData: what data? nDim " << nDim << ", mnDataId " << mnDataId);
2581 return pData;
2584 const ScDPItemData* ScDPSource::GetItemDataById(sal_Int32 nDim, sal_Int32 nId)
2586 return GetData()->GetMemberById(nDim, nId);
2589 const ScDPItemData* ScDPMembers::GetSrcItemDataByIndex(SCROW nIndex)
2591 const std::vector< SCROW >& memberIds = pSource->GetData()->GetColumnEntries( nDim );
2592 if ( nIndex < 0 || o3tl::make_unsigned(nIndex) >= memberIds.size() )
2593 return nullptr;
2594 SCROW nId = memberIds[ nIndex ];
2595 return pSource->GetItemDataById( nDim, nId );
2598 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */