1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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>
37 #include <miscuno.hxx>
38 #include <unonames.hxx>
39 #include <dpitemdata.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
;
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
)
96 mpData
->SetEmptyFlags(mbIgnoreEmptyRows
, mbRepeatIfEmpty
);
99 ScDPSource::~ScDPSource()
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())
143 sal_Int32 nDimIndex
= maDataDims
[nIndex
];
144 return GetDimensionsObject()->getByIndex(nDimIndex
);
147 OUString
ScDPSource::GetDataDimName(sal_Int32 nIndex
)
150 ScDPDimension
* pDim
= GetDataDimension(nIndex
);
152 aRet
= pDim
->getName();
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
);
161 return std::distance(itBeg
, it
);
163 itBeg
= maRowDims
.begin();
164 itEnd
= maRowDims
.end();
165 it
= std::find(itBeg
, itEnd
, nColumn
);
167 return std::distance(itBeg
, it
);
169 itBeg
= maDataDims
.begin();
170 itEnd
= maDataDims
.end();
171 it
= std::find(itBeg
, itEnd
, nColumn
);
173 return std::distance(itBeg
, it
);
175 itBeg
= maPageDims
.begin();
176 itEnd
= maPageDims
.end();
177 it
= std::find(itBeg
, itEnd
, nColumn
);
179 return std::distance(itBeg
, it
);
186 bool testSubTotal( bool& rAllowed
, sal_Int32 nColumn
, const std::vector
<sal_Int32
>& rDims
, ScDPSource
* pSource
)
189 std::vector
<sal_Int32
>::const_iterator it
= rDims
.begin(), itEnd
= rDims
.end();
190 for (; it
!= itEnd
; ++it
)
195 if ( pSource
->IsDataLayoutDimension(nColumn
) )
197 // no subtotals for data layout dim, no matter where
202 // no subtotals if no other dim but data layout follows
204 if (it
!= itEnd
&& pSource
->IsDataLayoutDimension(*it
))
209 return true; // found
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())
224 bool ScDPSource::SubTotalAllowed(sal_Int32 nColumn
)
226 //TODO: cache this at ScDPResultData
227 bool bAllowed
= true;
228 if ( testSubTotal(bAllowed
, nColumn
, maColDims
, this) )
230 if ( testSubTotal(bAllowed
, nColumn
, maRowDims
, this) )
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
);
248 case sheet::DataPilotFieldOrientation_COLUMN
:
249 maColDims
.push_back(nColumn
);
251 case sheet::DataPilotFieldOrientation_ROW
:
252 maRowDims
.push_back(nColumn
);
254 case sheet::DataPilotFieldOrientation_DATA
:
255 maDataDims
.push_back(nColumn
);
257 case sheet::DataPilotFieldOrientation_PAGE
:
258 maPageDims
.push_back(nColumn
);
260 // DataPilot Migration - Cache&&Performance
261 case sheet::DataPilotFieldOrientation_HIDDEN
:
264 OSL_FAIL( "ScDPSource::SetOrientation: unexpected orientation" );
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
)
301 ScDPDimension
* ScDPSource::AddDuplicated(std::u16string_view rNewName
)
303 OSL_ENSURE(mpDimensions
.is(), "AddDuplicated without dimensions?");
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
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())
330 if (nDim
< mpDimensions
->getCount())
332 ScDPDimension
* pDimObj
= mpDimensions
->getByIndex( nDim
);
335 tools::Long nSource
= pDimObj
->GetSourceDim();
341 OSL_FAIL("GetSourceDim: wrong dim");
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.
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
))
401 return uno::Sequence
<double>();
404 void SAL_CALL
ScDPSource::refresh()
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
);
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
);
460 OUString
ScDPSource::getDataDescription()
462 CreateRes_Impl(); // create mpResultData
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
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();
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!
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
;
531 if ( nPos
+1 < nLevels
&& ppDim
[nPos
] == ppDim
[nPos
+1] )
533 OSL_FAIL("lcl_CountMinMembers: multiple levels from one dimension not implemented");
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 )
545 else if ( bWasShowAll
) // "show all" set for all following levels?
548 if ( !ppLevel
[nPos
]->getShowEmpty() )
550 // this level is counted, following ones are not
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?
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
;
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() )
588 ScDPLevels
* pLevels
= pDim
->GetHierarchiesObject()->getByIndex(nHierarchy
)->GetLevelsObject();
589 sal_Int32 nCount
= pLevels
->getCount();
592 if (pDim
->getIsDataLayoutDimension() && maDataDims
.size() < 2)
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
)
609 rInfo
.aRowLevelDims
.push_back(rDimIndex
);
610 rInfo
.aRowDims
.push_back(pDim
);
611 rInfo
.aRowLevels
.push_back(pLevel
);
615 rInfo
.aColLevelDims
.push_back(rDimIndex
);
616 rInfo
.aColDims
.push_back(pDim
);
617 rInfo
.aColLevels
.push_back(pLevel
);
620 pLevel
->GetMembersObject(); // initialize for groups
627 class CategoryDimInserter
629 ScDPSource
& mrSource
;
630 std::unordered_set
<sal_Int32
>& mrCatDims
;
632 CategoryDimInserter(ScDPSource
& rSource
, std::unordered_set
<sal_Int32
>& rCatDims
) :
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.
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())
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()
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
);
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.
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();
802 aInfo
.aDataSrcCols
.push_back(nSource
);
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() )
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;
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
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
927 ScDPRunningTotalState
aRunning(mpColumnResultRoot
.get(), mpRowResultRoot
.get());
928 ScDPRowTotals aTotals
;
929 mpRowResultRoot
->UpdateRunningTotals(mpColumnResultRoot
.get(), mpResultData
->GetRowStartMeasure(), aRunning
, aTotals
);
936 void ScDPSource::FillLevelList( sheet::DataPilotFieldOrientation nOrientation
, std::vector
<ScDPLevel
*> &rList
)
940 std::vector
<sal_Int32
>* pDimIndex
= nullptr;
941 switch (nOrientation
)
943 case sheet::DataPilotFieldOrientation_COLUMN
:
944 pDimIndex
= &maColDims
;
946 case sheet::DataPilotFieldOrientation_ROW
:
947 pDimIndex
= &maRowDims
;
949 case sheet::DataPilotFieldOrientation_DATA
:
950 pDimIndex
= &maDataDims
;
952 case sheet::DataPilotFieldOrientation_PAGE
:
953 pDimIndex
= &maPageDims
;
956 OSL_FAIL( "ScDPSource::FillLevelList: unexpected orientation" );
961 OSL_FAIL("invalid orientation");
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() )
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
)
993 if (mbResultOverflow
) // set in CreateRes_Impl
995 // no results available -> abort (leave empty)
996 // exception is thrown in ScDPSource::getResults
1000 FillLevelList(sheet::DataPilotFieldOrientation_COLUMN
, maColumnLevelList
);
1001 sal_Int32 nColLevelCount
= maColumnLevelList
.size();
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();
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();
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
];
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
);
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
)
1087 if (aValue
>>= aName
)
1088 mpGrandTotalName
= aName
;
1092 OSL_FAIL("unknown property");
1093 //TODO: THROW( UnknownPropertyException() );
1097 uno::Any SAL_CALL
ScDPSource::getPropertyValue( const OUString
& aPropertyName
)
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
;
1123 OSL_FAIL("unknown property");
1124 //TODO: THROW( UnknownPropertyException() );
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);
1139 SC_IMPL_DUMMY_PROPERTY_LISTENER( ScDPSource
)
1141 ScDPDimensions::ScDPDimensions( ScDPSource
* 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();
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
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
);
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();
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
)
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
1231 ScDPDimension
* ScDPDimensions::getByIndex(tools::Long nIndex
) const
1233 if ( nIndex
>= 0 && nIndex
< nDimCount
)
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
) :
1255 nFunction( ScGeneralFunction::SUM
), // sum is default
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())
1297 return pSource
->GetData()->getDimensionName( nDim
);
1300 void SAL_CALL
ScDPDimension::setName( const OUString
& rNewName
)
1302 // used after cloning
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
)
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?
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() )
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
;
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
);
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
)
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
)
1433 uno::Sequence
<sheet::TableFilterField
> aSeq
;
1434 if (aValue
>>= aSeq
)
1436 sal_Int32 nLength
= aSeq
.getLength();
1439 aSelectedPage
.clear();
1440 bHasSelectedPage
= false;
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;
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
)
1464 if (aValue
>>= aTmpName
)
1465 mpLayoutName
= aTmpName
;
1467 else if (aPropertyName
== SC_UNO_DP_FIELD_SUBTOTALNAME
)
1470 if (aValue
>>= aTmpName
)
1471 mpSubtotalName
= aTmpName
;
1473 else if (aPropertyName
== SC_UNO_DP_HAS_HIDDEN_MEMBER
)
1477 mbHasHiddenMember
= b
;
1481 OSL_FAIL("unknown property");
1482 //TODO: THROW( UnknownPropertyException() );
1486 uno::Any SAL_CALL
ScDPDimension::getPropertyValue( const OUString
& aPropertyName
)
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();
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
);
1534 case sheet::DataPilotFieldReferenceType::INDEX
:
1535 nFormat
= pSource
->GetData()->GetNumberFormatByIdx( NF_NUMBER_SYSTEM
);
1543 else if ( aPropertyName
== SC_UNO_DP_ORIGINAL
)
1545 uno::Reference
<container::XNamed
> xOriginal
;
1546 if (nSourceDim
>= 0)
1547 xOriginal
= pSource
->GetDimensionsObject()->getByIndex(nSourceDim
);
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 );
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
1578 OSL_FAIL("unknown property");
1579 //TODO: THROW( UnknownPropertyException() );
1584 SC_IMPL_DUMMY_PROPERTY_LISTENER( ScDPDimension
)
1586 ScDPHierarchies::ScDPHierarchies( ScDPSource
* pSrc
, tools::Long 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
);
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();
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
)
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()
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
)
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
) :
1680 //TODO: hold pSource
1683 ScDPHierarchy::~ScDPHierarchy()
1685 //TODO: release pSource
1688 ScDPLevels
* ScDPHierarchy::GetLevelsObject()
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 !!!!
1707 case SC_DAPI_HIERARCHY_FLAT
:
1709 break; //TODO: name ???????
1710 case SC_DAPI_HIERARCHY_QUARTER
:
1712 break; //TODO: name ???????
1713 case SC_DAPI_HIERARCHY_WEEK
:
1715 break; //TODO: name ???????
1717 OSL_FAIL( "ScDPHierarchy::getName: unexpected hierarchy" );
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
) :
1733 //TODO: hold pSource
1735 // text columns have only one level
1737 tools::Long nSrcDim
= pSource
->GetSourceDim( nDim
);
1738 if ( pSource
->IsDateDimension( nSrcDim
) )
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;
1746 OSL_FAIL("wrong hierarchy");
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
);
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();
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
)
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
1812 ScDPLevel
* ScDPLevels::getByIndex(sal_Int32 nIndex
) const
1814 if ( nIndex
>= 0 && nIndex
< nLevCount
)
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?
1835 class ScDPGlobalMembersOrder
1841 ScDPGlobalMembersOrder( ScDPLevel
& rLev
, bool 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
) :
1870 aSortInfo( OUString(), true, sheet::DataPilotFieldSortMode::NAME
), // default: sort by name
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
;
1904 //TODO: error if not found?
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
);
1925 if ( !aAutoShowInfo
.IsEnabled
)
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
;
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 );
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
)
1982 case SC_DAPI_LEVEL_YEAR
:
1985 case SC_DAPI_LEVEL_QUARTER
:
1988 case SC_DAPI_LEVEL_MONTH
:
1991 case SC_DAPI_LEVEL_DAY
:
1995 OSL_FAIL( "ScDPLevel::getName: unexpected level" );
1999 else if ( nHier
== SC_DAPI_HIERARCHY_WEEK
)
2003 case SC_DAPI_LEVEL_YEAR
:
2006 case SC_DAPI_LEVEL_WEEK
:
2009 case SC_DAPI_LEVEL_WEEKDAY
:
2013 OSL_FAIL( "ScDPLevel::getName: unexpected level" );
2017 if (!aRet
.isEmpty())
2021 ScDPDimension
* pDim
= pSource
->GetDimensionsObject()->getByIndex(nSrcDim
);
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
) )
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
);
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
;
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
;
2089 OSL_FAIL("unknown property");
2093 uno::Any SAL_CALL
ScDPLevel::getPropertyValue( const OUString
& aPropertyName
)
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
);
2113 else if ( aPropertyName
== SC_UNO_DP_SUBTOTAL2
)
2115 uno::Sequence
<sal_Int16
> aSeq
= getSubTotals(); //TODO: avoid extra copy?
2118 else if ( aPropertyName
== SC_UNO_DP_SORTING
)
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
);
2132 const std::optional
<OUString
> & pLayoutName
= pDim
->GetLayoutName();
2136 aRet
<<= *pLayoutName
;
2140 OSL_FAIL("unknown property");
2145 SC_IMPL_DUMMY_PROPERTY_LISTENER( ScDPLevel
)
2147 ScDPMembers::ScDPMembers( ScDPSource
* pSrc
, sal_Int32 nD
, sal_Int32 nH
, sal_Int32 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
) )
2161 if ( nHier
== SC_DAPI_HIERARCHY_QUARTER
)
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() )
2174 pLastNumData
= pData
;
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
)),
2186 tools::Long nLastYear
= pSource
->GetData()->GetDatePart(
2187 static_cast<tools::Long
>(::rtl::math::approxFloor( fLastVal
)),
2190 nMbrCount
= nLastYear
+ 1 - nFirstYear
;
2193 nMbrCount
= 0; // no values
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;
2200 OSL_FAIL( "ScDPMembers::ScDPMembers: unexpected level" );
2204 else if ( nHier
== SC_DAPI_HIERARCHY_WEEK
)
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;
2212 OSL_FAIL( "ScDPMembers::ScDPMembers: unexpected level" );
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
2242 return -1; // not found
2245 uno::Any SAL_CALL
ScDPMembers::getByName( const OUString
& aName
)
2247 sal_Int32 nIndex
= GetIndexFromName( aName
);
2250 uno::Reference
<container::XNamed
> xNamed
= getByIndex(nIndex
);
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
);
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()); });
2322 nVisCount
= nMbrCount
; // default for all
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;
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
)),
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 );
2378 nVal
= nIndex
+ 1; // Quarter, Day, Week are 1-based
2382 case SC_DAPI_LEVEL_YEAR
:
2383 nGroupBy
= sheet::DataPilotFieldGroupBy::YEARS
;
2385 case SC_DAPI_LEVEL_QUARTER
:
2386 case SC_DAPI_LEVEL_WEEK
:
2387 nGroupBy
= sheet::DataPilotFieldGroupBy::QUARTERS
;
2389 case SC_DAPI_LEVEL_MONTH
:
2390 case SC_DAPI_LEVEL_WEEKDAY
:
2391 nGroupBy
= sheet::DataPilotFieldGroupBy::MONTHS
;
2393 case SC_DAPI_LEVEL_DAY
:
2394 nGroupBy
= sheet::DataPilotFieldGroupBy::DAYS
;
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
));
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
) :
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() )),
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;
2470 // only this has a position - members with specified positions come before those without
2474 else if ( rOther
.nPosition
>= 0 )
2476 // only rOther has a position
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();
2501 return pSource
->GetData()->GetFormattedString(nDim
, *pData
, bLocaleIndependent
);
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?
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
);
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
)
2542 if (aValue
>>= aName
)
2543 mpLayoutName
= aName
;
2547 OSL_FAIL("unknown property");
2551 uno::Any SAL_CALL
ScDPMember::getPropertyValue( const OUString
& aPropertyName
)
2554 if ( aPropertyName
== SC_UNO_DP_ISVISIBLE
)
2556 else if ( aPropertyName
== SC_UNO_DP_SHOWDETAILS
)
2558 else if ( aPropertyName
== SC_UNO_DP_POSITION
)
2560 else if (aPropertyName
== SC_UNO_DP_LAYOUTNAME
)
2561 aRet
<<= mpLayoutName
? *mpLayoutName
: OUString();
2564 OSL_FAIL("unknown property");
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
);
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() )
2594 SCROW nId
= memberIds
[ nIndex
];
2595 return pSource
->GetItemDataById( nDim
, nId
);
2598 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */