summaryrefslogtreecommitdiffstats
path: root/sc/source/core/data/dptabsrc.cxx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /sc/source/core/data/dptabsrc.cxx
parentInitial commit. (diff)
downloadlibreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz
libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sc/source/core/data/dptabsrc.cxx')
-rw-r--r--sc/source/core/data/dptabsrc.cxx2613
1 files changed, 2613 insertions, 0 deletions
diff --git a/sc/source/core/data/dptabsrc.cxx b/sc/source/core/data/dptabsrc.cxx
new file mode 100644
index 000000000..cab605f69
--- /dev/null
+++ b/sc/source/core/data/dptabsrc.cxx
@@ -0,0 +1,2613 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dptabsrc.hxx>
+
+#include <algorithm>
+#include <vector>
+
+#include <comphelper/sequence.hxx>
+#include <o3tl/any.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <rtl/math.hxx>
+#include <sal/log.hxx>
+#include <svl/itemprop.hxx>
+#include <vcl/svapp.hxx>
+
+#include <dpcache.hxx>
+#include <dptabres.hxx>
+#include <dptabdat.hxx>
+#include <global.hxx>
+#include <miscuno.hxx>
+#include <unonames.hxx>
+#include <dpitemdata.hxx>
+#include <dputil.hxx>
+#include <dpresfilter.hxx>
+#include <calcmacros.hxx>
+#include <generalfunction.hxx>
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/sheet/DataPilotFieldFilter.hpp>
+#include <com/sun/star/sheet/DataPilotFieldReferenceType.hpp>
+#include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
+#include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
+#include <com/sun/star/sheet/DataPilotFieldAutoShowInfo.hpp>
+#include <com/sun/star/sheet/GeneralFunction2.hpp>
+#include <com/sun/star/sheet/TableFilterField.hpp>
+
+#include <unotools/calendarwrapper.hxx>
+#include <com/sun/star/i18n/CalendarDisplayIndex.hpp>
+
+using namespace com::sun::star;
+using ::std::vector;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::sheet::DataPilotFieldAutoShowInfo;
+
+#define SC_MINCOUNT_LIMIT 1000000
+
+SC_SIMPLE_SERVICE_INFO( ScDPSource, "ScDPSource", "com.sun.star.sheet.DataPilotSource" )
+SC_SIMPLE_SERVICE_INFO( ScDPDimensions, "ScDPDimensions", "com.sun.star.sheet.DataPilotSourceDimensions" )
+SC_SIMPLE_SERVICE_INFO( ScDPDimension, "ScDPDimension", "com.sun.star.sheet.DataPilotSourceDimension" )
+
+// Typos are on purpose here, quote from Eike Rathke (see https://gerrit.libreoffice.org/c/core/+/101116):
+// "The typo is exactly why the SC_SIMPLE_SERVICE_INFO_COMPAT() lists both service names,
+// the old with the typo and the new corrected one. Correcting the typo in the old name
+// will make all extensions fail that use it. This is not to be changed."
+SC_SIMPLE_SERVICE_INFO_COMPAT( ScDPHierarchies, "ScDPHierarchies",
+ "com.sun.star.sheet.DataPilotSourceHierarchies", "com.sun.star.sheet.DataPilotSourceHierarcies" )
+SC_SIMPLE_SERVICE_INFO_COMPAT( ScDPHierarchy, "ScDPHierarchy",
+ "com.sun.star.sheet.DataPilotSourceHierarchy", "com.sun.star.sheet.DataPilotSourceHierarcy" )
+
+SC_SIMPLE_SERVICE_INFO( ScDPLevels, "ScDPLevels", "com.sun.star.sheet.DataPilotSourceLevels" )
+SC_SIMPLE_SERVICE_INFO( ScDPLevel, "ScDPLevel", "com.sun.star.sheet.DataPilotSourceLevel" )
+SC_SIMPLE_SERVICE_INFO( ScDPMembers, "ScDPMembers", "com.sun.star.sheet.DataPilotSourceMembers" )
+SC_SIMPLE_SERVICE_INFO( ScDPMember, "ScDPMember", "com.sun.star.sheet.DataPilotSourceMember" )
+
+// property maps for PropertySetInfo
+// DataDescription / NumberFormat are internal
+
+//TODO: move to a header?
+static bool lcl_GetBoolFromAny( const uno::Any& aAny )
+{
+ auto b = o3tl::tryAccess<bool>(aAny);
+ return b && *b;
+}
+
+ScDPSource::ScDPSource( ScDPTableData* pD ) :
+ pData( pD ),
+ bColumnGrand( true ), // default is true
+ bRowGrand( true ),
+ bIgnoreEmptyRows( false ),
+ bRepeatIfEmpty( false ),
+ nDupCount( 0 ),
+ bResultOverflow( false ),
+ bPageFiltered( false )
+{
+ pData->SetEmptyFlags( bIgnoreEmptyRows, bRepeatIfEmpty );
+}
+
+ScDPSource::~ScDPSource()
+{
+ // free lists
+
+ pColResults.reset();
+ pRowResults.reset();
+
+ pColResRoot.reset();
+ pRowResRoot.reset();
+ pResData.reset();
+}
+
+const std::optional<OUString> & ScDPSource::GetGrandTotalName() const
+{
+ return mpGrandTotalName;
+}
+
+sheet::DataPilotFieldOrientation ScDPSource::GetOrientation(sal_Int32 nColumn)
+{
+ if (std::find(maColDims.begin(), maColDims.end(), nColumn) != maColDims.end())
+ return sheet::DataPilotFieldOrientation_COLUMN;
+
+ if (std::find(maRowDims.begin(), maRowDims.end(), nColumn) != maRowDims.end())
+ return sheet::DataPilotFieldOrientation_ROW;
+
+ if (std::find(maDataDims.begin(), maDataDims.end(), nColumn) != maDataDims.end())
+ return sheet::DataPilotFieldOrientation_DATA;
+
+ if (std::find(maPageDims.begin(), maPageDims.end(), nColumn) != maPageDims.end())
+ return sheet::DataPilotFieldOrientation_PAGE;
+
+ return sheet::DataPilotFieldOrientation_HIDDEN;
+}
+
+sal_Int32 ScDPSource::GetDataDimensionCount() const
+{
+ return maDataDims.size();
+}
+
+ScDPDimension* ScDPSource::GetDataDimension(sal_Int32 nIndex)
+{
+ if (nIndex < 0 || o3tl::make_unsigned(nIndex) >= maDataDims.size())
+ return nullptr;
+
+ sal_Int32 nDimIndex = maDataDims[nIndex];
+ return GetDimensionsObject()->getByIndex(nDimIndex);
+}
+
+OUString ScDPSource::GetDataDimName(sal_Int32 nIndex)
+{
+ OUString aRet;
+ ScDPDimension* pDim = GetDataDimension(nIndex);
+ if (pDim)
+ aRet = pDim->getName();
+ return aRet;
+}
+
+sal_Int32 ScDPSource::GetPosition(sal_Int32 nColumn)
+{
+ std::vector<sal_Int32>::const_iterator it, itBeg = maColDims.begin(), itEnd = maColDims.end();
+ it = std::find(itBeg, itEnd, nColumn);
+ if (it != itEnd)
+ return std::distance(itBeg, it);
+
+ itBeg = maRowDims.begin();
+ itEnd = maRowDims.end();
+ it = std::find(itBeg, itEnd, nColumn);
+ if (it != itEnd)
+ return std::distance(itBeg, it);
+
+ itBeg = maDataDims.begin();
+ itEnd = maDataDims.end();
+ it = std::find(itBeg, itEnd, nColumn);
+ if (it != itEnd)
+ return std::distance(itBeg, it);
+
+ itBeg = maPageDims.begin();
+ itEnd = maPageDims.end();
+ it = std::find(itBeg, itEnd, nColumn);
+ if (it != itEnd)
+ return std::distance(itBeg, it);
+
+ return 0;
+}
+
+namespace {
+
+bool testSubTotal( bool& rAllowed, sal_Int32 nColumn, const std::vector<sal_Int32>& rDims, ScDPSource* pSource )
+{
+ rAllowed = true;
+ std::vector<sal_Int32>::const_iterator it = rDims.begin(), itEnd = rDims.end();
+ for (; it != itEnd; ++it)
+ {
+ if (*it != nColumn)
+ continue;
+
+ if ( pSource->IsDataLayoutDimension(nColumn) )
+ {
+ // no subtotals for data layout dim, no matter where
+ rAllowed = false;
+ return true;
+ }
+
+ // no subtotals if no other dim but data layout follows
+ ++it;
+ if (it != itEnd && pSource->IsDataLayoutDimension(*it))
+ ++it;
+ if (it == itEnd)
+ rAllowed = false;
+
+ return true; // found
+ }
+
+ return false;
+}
+
+void removeDim( sal_Int32 nRemove, std::vector<sal_Int32>& rDims )
+{
+ std::vector<sal_Int32>::iterator it = std::find(rDims.begin(), rDims.end(), nRemove);
+ if (it != rDims.end())
+ rDims.erase(it);
+}
+
+}
+
+bool ScDPSource::SubTotalAllowed(sal_Int32 nColumn)
+{
+ //TODO: cache this at ScDPResultData
+ bool bAllowed = true;
+ if ( testSubTotal(bAllowed, nColumn, maColDims, this) )
+ return bAllowed;
+ if ( testSubTotal(bAllowed, nColumn, maRowDims, this) )
+ return bAllowed;
+ return bAllowed;
+}
+
+void ScDPSource::SetOrientation(sal_Int32 nColumn, sheet::DataPilotFieldOrientation nNew)
+{
+ //TODO: change to no-op if new orientation is equal to old?
+
+ // remove from old list
+ removeDim(nColumn, maColDims);
+ removeDim(nColumn, maRowDims);
+ removeDim(nColumn, maDataDims);
+ removeDim(nColumn, maPageDims);
+
+ // add to new list
+ switch (nNew)
+ {
+ case sheet::DataPilotFieldOrientation_COLUMN:
+ maColDims.push_back(nColumn);
+ break;
+ case sheet::DataPilotFieldOrientation_ROW:
+ maRowDims.push_back(nColumn);
+ break;
+ case sheet::DataPilotFieldOrientation_DATA:
+ maDataDims.push_back(nColumn);
+ break;
+ case sheet::DataPilotFieldOrientation_PAGE:
+ maPageDims.push_back(nColumn);
+ break;
+ // DataPilot Migration - Cache&&Performance
+ case sheet::DataPilotFieldOrientation_HIDDEN:
+ break;
+ default:
+ OSL_FAIL( "ScDPSource::SetOrientation: unexpected orientation" );
+ break;
+ }
+}
+
+bool ScDPSource::IsDataLayoutDimension(sal_Int32 nDim)
+{
+ return nDim == pData->GetColumnCount();
+}
+
+sheet::DataPilotFieldOrientation ScDPSource::GetDataLayoutOrientation()
+{
+ return GetOrientation(pData->GetColumnCount());
+}
+
+bool ScDPSource::IsDateDimension(sal_Int32 nDim)
+{
+ return pData->IsDateDimension(nDim);
+}
+
+ScDPDimensions* ScDPSource::GetDimensionsObject()
+{
+ if (!pDimensions.is())
+ {
+ pDimensions = new ScDPDimensions(this);
+ }
+ return pDimensions.get();
+}
+
+uno::Reference<container::XNameAccess> SAL_CALL ScDPSource::getDimensions()
+{
+ return GetDimensionsObject();
+}
+
+void ScDPSource::SetDupCount( tools::Long nNew )
+{
+ nDupCount = nNew;
+}
+
+ScDPDimension* ScDPSource::AddDuplicated(std::u16string_view rNewName)
+{
+ OSL_ENSURE( pDimensions.is(), "AddDuplicated without dimensions?" );
+
+ // re-use
+
+ tools::Long nOldDimCount = pDimensions->getCount();
+ for (tools::Long i=0; i<nOldDimCount; i++)
+ {
+ ScDPDimension* pDim = pDimensions->getByIndex(i);
+ if (pDim && pDim->getName() == rNewName)
+ {
+ //TODO: test if pDim is a duplicate of source
+ return pDim;
+ }
+ }
+
+ SetDupCount( nDupCount + 1 );
+ pDimensions->CountChanged(); // uses nDupCount
+
+ return pDimensions->getByIndex( pDimensions->getCount() - 1 );
+}
+
+sal_Int32 ScDPSource::GetSourceDim(sal_Int32 nDim)
+{
+ // original source dimension or data layout dimension?
+ if ( nDim <= pData->GetColumnCount() )
+ return nDim;
+
+ if ( nDim < pDimensions->getCount() )
+ {
+ ScDPDimension* pDimObj = pDimensions->getByIndex( nDim );
+ if ( pDimObj )
+ {
+ tools::Long nSource = pDimObj->GetSourceDim();
+ if ( nSource >= 0 )
+ return nSource;
+ }
+ }
+
+ OSL_FAIL("GetSourceDim: wrong dim");
+ return nDim;
+}
+
+uno::Sequence< uno::Sequence<sheet::DataResult> > SAL_CALL ScDPSource::getResults()
+{
+ CreateRes_Impl(); // create pColResRoot and pRowResRoot
+
+ if ( bResultOverflow ) // set in CreateRes_Impl
+ {
+ // no results available
+ throw uno::RuntimeException();
+ }
+
+ sal_Int32 nColCount = pColResRoot->GetSize(pResData->GetColStartMeasure());
+ sal_Int32 nRowCount = pRowResRoot->GetSize(pResData->GetRowStartMeasure());
+
+ // allocate full sequence
+ //TODO: leave out empty rows???
+
+ uno::Sequence< uno::Sequence<sheet::DataResult> > aSeq( nRowCount );
+ uno::Sequence<sheet::DataResult>* pRowAry = aSeq.getArray();
+ for (sal_Int32 nRow = 0; nRow < nRowCount; nRow++)
+ {
+ uno::Sequence<sheet::DataResult> aColSeq( nColCount );
+ // use default values of DataResult
+ pRowAry[nRow] = aColSeq;
+ }
+
+ ScDPResultFilterContext aFilterCxt;
+ pRowResRoot->FillDataResults(
+ pColResRoot.get(), aFilterCxt, aSeq, pResData->GetRowStartMeasure());
+
+ maResFilterSet.swap(aFilterCxt.maFilterSet); // Keep this data for GETPIVOTDATA.
+
+ return aSeq;
+}
+
+uno::Sequence<double> ScDPSource::getFilteredResults(
+ const uno::Sequence<sheet::DataPilotFieldFilter>& aFilters )
+{
+ if (maResFilterSet.empty())
+ getResults(); // Build result tree first.
+
+ // Get result values from the tree.
+ const ScDPResultTree::ValuesType* pVals = maResFilterSet.getResults(aFilters);
+ if (pVals && !pVals->empty())
+ {
+ return comphelper::containerToSequence(*pVals);
+ }
+
+ if (aFilters.getLength() == 1)
+ {
+ // Try to get result from the leaf nodes.
+ double fVal = maResFilterSet.getLeafResult(aFilters[0]);
+ if (!std::isnan(fVal))
+ {
+ return { fVal };
+ }
+ }
+
+ return uno::Sequence<double>();
+}
+
+void SAL_CALL ScDPSource::refresh()
+{
+ disposeData();
+}
+
+void SAL_CALL ScDPSource::addRefreshListener( const uno::Reference<util::XRefreshListener >& )
+{
+ OSL_FAIL("not implemented"); //TODO: exception?
+}
+
+void SAL_CALL ScDPSource::removeRefreshListener( const uno::Reference<util::XRefreshListener >& )
+{
+ OSL_FAIL("not implemented"); //TODO: exception?
+}
+
+Sequence< Sequence<Any> > SAL_CALL ScDPSource::getDrillDownData(const Sequence<sheet::DataPilotFieldFilter>& aFilters)
+{
+ sal_Int32 nColumnCount = GetData()->GetColumnCount();
+
+ vector<ScDPFilteredCache::Criterion> aFilterCriteria;
+ for (const sheet::DataPilotFieldFilter& rFilter : aFilters)
+ {
+ const OUString& aFieldName = rFilter.FieldName;
+ for (sal_Int32 nCol = 0; nCol < nColumnCount; ++nCol)
+ {
+ if (aFieldName == pData->getDimensionName(nCol))
+ {
+ ScDPDimension* pDim = GetDimensionsObject()->getByIndex( nCol );
+ ScDPMembers* pMembers = pDim->GetHierarchiesObject()->getByIndex(0)->
+ GetLevelsObject()->getByIndex(0)->GetMembersObject();
+ sal_Int32 nIndex = pMembers->GetIndexFromName( rFilter.MatchValueName );
+ if ( nIndex >= 0 )
+ {
+ ScDPItemData aItem(pMembers->getByIndex(nIndex)->FillItemData());
+ aFilterCriteria.emplace_back( );
+ aFilterCriteria.back().mnFieldIndex = nCol;
+ aFilterCriteria.back().mpFilter =
+ std::make_shared<ScDPFilteredCache::SingleFilter>(aItem);
+ }
+ }
+ }
+ }
+
+ // Take into account the visibilities of field members.
+ ScDPResultVisibilityData aResVisData(this);
+ pRowResRoot->FillVisibilityData(aResVisData);
+ pColResRoot->FillVisibilityData(aResVisData);
+ aResVisData.fillFieldFilters(aFilterCriteria);
+
+ Sequence< Sequence<Any> > aTabData;
+ std::unordered_set<sal_Int32> aCatDims;
+ GetCategoryDimensionIndices(aCatDims);
+ pData->GetDrillDownData(std::move(aFilterCriteria), std::move(aCatDims), aTabData);
+ return aTabData;
+}
+
+OUString ScDPSource::getDataDescription()
+{
+ CreateRes_Impl(); // create pResData
+
+ OUString aRet;
+ if ( pResData->GetMeasureCount() == 1 )
+ {
+ bool bTotalResult = false;
+ aRet = pResData->GetMeasureString(0, true, SUBTOTAL_FUNC_NONE, bTotalResult);
+ }
+
+ // empty for more than one measure
+
+ return aRet;
+}
+
+void ScDPSource::setIgnoreEmptyRows(bool bSet)
+{
+ bIgnoreEmptyRows = bSet;
+ pData->SetEmptyFlags( bIgnoreEmptyRows, bRepeatIfEmpty );
+}
+
+void ScDPSource::setRepeatIfEmpty(bool bSet)
+{
+ bRepeatIfEmpty = bSet;
+ pData->SetEmptyFlags( bIgnoreEmptyRows, bRepeatIfEmpty );
+}
+
+void ScDPSource::disposeData()
+{
+ maResFilterSet.clear();
+
+ if ( pResData )
+ {
+ // reset all data...
+
+ pColResRoot.reset();
+ pRowResRoot.reset();
+ pResData.reset();
+ pColResults.reset();
+ pRowResults.reset();
+ aColLevelList.clear();
+ aRowLevelList.clear();
+ }
+
+ pDimensions.clear(); // settings have to be applied (from SaveData) again!
+ SetDupCount( 0 );
+
+ maColDims.clear();
+ maRowDims.clear();
+ maDataDims.clear();
+ maPageDims.clear();
+
+ pData->DisposeData(); // cached entries etc.
+ bPageFiltered = false;
+ bResultOverflow = false;
+}
+
+static tools::Long lcl_CountMinMembers(const vector<ScDPDimension*>& ppDim, const vector<ScDPLevel*>& ppLevel, tools::Long nLevels )
+{
+ // Calculate the product of the member count for those consecutive levels that
+ // have the "show all" flag, one following level, and the data layout dimension.
+
+ tools::Long nTotal = 1;
+ tools::Long nDataCount = 1;
+ bool bWasShowAll = true;
+ tools::Long nPos = nLevels;
+ while ( nPos > 0 )
+ {
+ --nPos;
+
+ if ( nPos+1 < nLevels && ppDim[nPos] == ppDim[nPos+1] )
+ {
+ OSL_FAIL("lcl_CountMinMembers: multiple levels from one dimension not implemented");
+ return 0;
+ }
+
+ bool bDo = false;
+ if ( ppDim[nPos]->getIsDataLayoutDimension() )
+ {
+ // data layout dim doesn't interfere with "show all" flags
+ nDataCount = ppLevel[nPos]->GetMembersObject()->getCount();
+ if ( nDataCount == 0 )
+ nDataCount = 1;
+ }
+ else if ( bWasShowAll ) // "show all" set for all following levels?
+ {
+ bDo = true;
+ if ( !ppLevel[nPos]->getShowEmpty() )
+ {
+ // this level is counted, following ones are not
+ bWasShowAll = false;
+ }
+ }
+ if ( bDo )
+ {
+ tools::Long nThisCount = ppLevel[nPos]->GetMembersObject()->getMinMembers();
+ if ( nThisCount == 0 )
+ {
+ nTotal = 1; // empty level -> start counting from here
+ //TODO: start with visible elements in this level?
+ }
+ else
+ {
+ if ( nTotal >= LONG_MAX / nThisCount )
+ return LONG_MAX; // overflow
+ nTotal *= nThisCount;
+ }
+ }
+ }
+
+ // always include data layout dim, even after restarting
+ if ( nTotal >= LONG_MAX / nDataCount )
+ return LONG_MAX; // overflow
+ nTotal *= nDataCount;
+
+ return nTotal;
+}
+
+void ScDPSource::FillCalcInfo(bool bIsRow, ScDPTableData::CalcInfo& rInfo, bool &rHasAutoShow)
+{
+ const std::vector<sal_Int32>& rDims = bIsRow ? maRowDims : maColDims;
+ for (const auto& rDimIndex : rDims)
+ {
+ ScDPDimension* pDim = GetDimensionsObject()->getByIndex(rDimIndex);
+ tools::Long nHierarchy = ScDPDimension::getUsedHierarchy();
+ if ( nHierarchy >= ScDPHierarchies::getCount() )
+ nHierarchy = 0;
+ ScDPLevels* pLevels = pDim->GetHierarchiesObject()->getByIndex(nHierarchy)->GetLevelsObject();
+ sal_Int32 nCount = pLevels->getCount();
+
+ //TODO: Test
+ if (pDim->getIsDataLayoutDimension() && maDataDims.size() < 2)
+ nCount = 0;
+ //TODO: Test
+
+ for (sal_Int32 j = 0; j < nCount; ++j)
+ {
+ ScDPLevel* pLevel = pLevels->getByIndex(j);
+ pLevel->EvaluateSortOrder();
+
+ // no layout flags for column fields, only for row fields
+ pLevel->SetEnableLayout( bIsRow );
+
+ if ( pLevel->GetAutoShow().IsEnabled )
+ rHasAutoShow = true;
+
+ if (bIsRow)
+ {
+ rInfo.aRowLevelDims.push_back(rDimIndex);
+ rInfo.aRowDims.push_back(pDim);
+ rInfo.aRowLevels.push_back(pLevel);
+ }
+ else
+ {
+ rInfo.aColLevelDims.push_back(rDimIndex);
+ rInfo.aColDims.push_back(pDim);
+ rInfo.aColLevels.push_back(pLevel);
+ }
+
+ pLevel->GetMembersObject(); // initialize for groups
+ }
+ }
+}
+
+namespace {
+
+class CategoryDimInserter
+{
+ ScDPSource& mrSource;
+ std::unordered_set<sal_Int32>& mrCatDims;
+public:
+ CategoryDimInserter(ScDPSource& rSource, std::unordered_set<sal_Int32>& rCatDims) :
+ mrSource(rSource),
+ mrCatDims(rCatDims) {}
+
+ void operator() (tools::Long nDim)
+ {
+ if (!mrSource.IsDataLayoutDimension(nDim))
+ mrCatDims.insert(nDim);
+ }
+};
+
+}
+
+void ScDPSource::GetCategoryDimensionIndices(std::unordered_set<sal_Int32>& rCatDims)
+{
+ std::unordered_set<sal_Int32> aCatDims;
+
+ CategoryDimInserter aInserter(*this, aCatDims);
+ std::for_each(maColDims.begin(), maColDims.end(), aInserter);
+ std::for_each(maRowDims.begin(), maRowDims.end(), aInserter);
+ std::for_each(maPageDims.begin(), maPageDims.end(), aInserter);
+
+ rCatDims.swap(aCatDims);
+}
+
+void ScDPSource::FilterCacheByPageDimensions()
+{
+ // #i117661# Repeated calls to ScDPFilteredCache::filterByPageDimension
+ // are invalid because rows are only hidden, never shown again. If
+ // FilterCacheByPageDimensions is called again, the cache table must
+ // be re-initialized. Currently, CreateRes_Impl always uses a fresh cache
+ // because ScDBDocFunc::DataPilotUpdate calls InvalidateData.
+
+ if (bPageFiltered)
+ {
+ SAL_WARN( "sc.core","tried to apply page field filters several times");
+
+ pData->DisposeData();
+ pData->CreateCacheTable(); // re-initialize the cache table
+ bPageFiltered = false;
+ }
+
+ // filter table by page dimensions.
+ vector<ScDPFilteredCache::Criterion> aCriteria;
+ for (const auto& rDimIndex : maPageDims)
+ {
+ ScDPDimension* pDim = GetDimensionsObject()->getByIndex(rDimIndex);
+ tools::Long nField = pDim->GetDimension();
+
+ ScDPMembers* pMems = pDim->GetHierarchiesObject()->getByIndex(0)->
+ GetLevelsObject()->getByIndex(0)->GetMembersObject();
+
+ tools::Long nMemCount = pMems->getCount();
+ ScDPFilteredCache::Criterion aFilter;
+ aFilter.mnFieldIndex = static_cast<sal_Int32>(nField);
+ aFilter.mpFilter = std::make_shared<ScDPFilteredCache::GroupFilter>();
+ ScDPFilteredCache::GroupFilter* pGrpFilter =
+ static_cast<ScDPFilteredCache::GroupFilter*>(aFilter.mpFilter.get());
+ for (tools::Long j = 0; j < nMemCount; ++j)
+ {
+ ScDPMember* pMem = pMems->getByIndex(j);
+ if (pMem->isVisible())
+ {
+ ScDPItemData aData(pMem->FillItemData());
+ pGrpFilter->addMatchItem(aData);
+ }
+ }
+ if (pGrpFilter->getMatchItemCount() < o3tl::make_unsigned(nMemCount))
+ // there is at least one invisible item. Add this filter criterion to the mix.
+ aCriteria.push_back(aFilter);
+
+ if (!pDim->HasSelectedPage())
+ continue;
+
+ const ScDPItemData& rData = pDim->GetSelectedData();
+ aCriteria.emplace_back();
+ ScDPFilteredCache::Criterion& r = aCriteria.back();
+ r.mnFieldIndex = static_cast<sal_Int32>(nField);
+ r.mpFilter = std::make_shared<ScDPFilteredCache::SingleFilter>(rData);
+ }
+ if (!aCriteria.empty())
+ {
+ std::unordered_set<sal_Int32> aCatDims;
+ GetCategoryDimensionIndices(aCatDims);
+ pData->FilterCacheTable(std::move(aCriteria), std::move(aCatDims));
+ bPageFiltered = true;
+ }
+}
+
+void ScDPSource::CreateRes_Impl()
+{
+ if (pResData)
+ return;
+
+ sheet::DataPilotFieldOrientation nDataOrient = GetDataLayoutOrientation();
+ if (maDataDims.size() > 1 && ( nDataOrient != sheet::DataPilotFieldOrientation_COLUMN &&
+ nDataOrient != sheet::DataPilotFieldOrientation_ROW ) )
+ {
+ // if more than one data dimension, data layout orientation must be set
+ SetOrientation( pData->GetColumnCount(), sheet::DataPilotFieldOrientation_ROW );
+ nDataOrient = sheet::DataPilotFieldOrientation_ROW;
+ }
+
+ // TODO: Aggregate pDataNames, pDataRefValues, nDataRefOrient, and
+ // eDataFunctions into a structure and use vector instead of static
+ // or pointer arrays.
+ vector<OUString> aDataNames;
+ vector<sheet::DataPilotFieldReference> aDataRefValues;
+ vector<ScSubTotalFunc> aDataFunctions;
+ vector<sheet::DataPilotFieldOrientation> aDataRefOrient;
+
+ ScDPTableData::CalcInfo aInfo;
+
+ // LateInit (initialize only those rows/children that are used) can be used unless
+ // any data dimension needs reference values from column/row dimensions
+ bool bLateInit = true;
+
+ // Go through all data dimensions (i.e. fields) and build their meta data
+ // so that they can be passed on to ScDPResultData instance later.
+ // TODO: aggregate all of data dimension info into a structure.
+ for (const tools::Long nDimIndex : maDataDims)
+ {
+ // Get function for each data field.
+ ScDPDimension* pDim = GetDimensionsObject()->getByIndex(nDimIndex);
+ ScGeneralFunction eUser = pDim->getFunction();
+ if (eUser == ScGeneralFunction::AUTO)
+ {
+ //TODO: test for numeric data
+ eUser = ScGeneralFunction::SUM;
+ }
+
+ // Map UNO's enum to internal enum ScSubTotalFunc.
+ aDataFunctions.push_back(ScDPUtil::toSubTotalFunc(eUser));
+
+ // Get reference field/item information.
+ aDataRefValues.push_back(pDim->GetReferenceValue());
+ sheet::DataPilotFieldOrientation nDataRefOrient = sheet::DataPilotFieldOrientation_HIDDEN; // default if not used
+ sal_Int32 eRefType = aDataRefValues.back().ReferenceType;
+ if ( eRefType == sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE ||
+ eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE ||
+ eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE ||
+ eRefType == sheet::DataPilotFieldReferenceType::RUNNING_TOTAL )
+ {
+ sal_Int32 nColumn = comphelper::findValue(
+ GetDimensionsObject()->getElementNames(), aDataRefValues.back().ReferenceField);
+ if ( nColumn >= 0 )
+ {
+ nDataRefOrient = GetOrientation(nColumn);
+ // need fully initialized results to find reference values
+ // (both in column or row dimensions), so updated values or
+ // differences to 0 can be displayed even for empty results.
+ bLateInit = false;
+ }
+ }
+
+ aDataRefOrient.push_back(nDataRefOrient);
+
+ aDataNames.push_back(pDim->getName());
+
+ //TODO: modify user visible strings as in ScDPResultData::GetMeasureString instead!
+
+ aDataNames.back() = ScDPUtil::getSourceDimensionName(aDataNames.back());
+
+ //TODO: if the name is overridden by user, a flag must be set
+ //TODO: so the user defined name replaces the function string and field name.
+
+ //TODO: the complete name (function and field) must be stored at the dimension
+
+ tools::Long nSource = pDim->GetSourceDim();
+ if (nSource >= 0)
+ aInfo.aDataSrcCols.push_back(nSource);
+ else
+ aInfo.aDataSrcCols.push_back(nDimIndex);
+ }
+
+ pResData.reset( new ScDPResultData(*this) );
+ pResData->SetMeasureData(aDataFunctions, aDataRefValues, aDataRefOrient, aDataNames);
+ pResData->SetDataLayoutOrientation(nDataOrient);
+ pResData->SetLateInit( bLateInit );
+
+ bool bHasAutoShow = false;
+
+ ScDPInitState aInitState;
+
+ // Page field selections restrict the members shown in related fields
+ // (both in column and row fields). aInitState is filled with the page
+ // field selections, they are kept across the data iterator loop.
+
+ for (const auto& rDimIndex : maPageDims)
+ {
+ ScDPDimension* pDim = GetDimensionsObject()->getByIndex(rDimIndex);
+ if ( pDim->HasSelectedPage() )
+ aInitState.AddMember(rDimIndex, GetCache()->GetIdByItemData(rDimIndex, pDim->GetSelectedData()));
+ }
+
+ // Show grand total columns only when the option is set *and* there is at
+ // least one column field. Same for the grand total rows.
+ sheet::DataPilotFieldOrientation nDataLayoutOrient = GetDataLayoutOrientation();
+ tools::Long nColDimCount2 = maColDims.size() - (nDataLayoutOrient == sheet::DataPilotFieldOrientation_COLUMN ? 1 : 0);
+ tools::Long nRowDimCount2 = maRowDims.size() - (nDataLayoutOrient == sheet::DataPilotFieldOrientation_ROW ? 1 : 0);
+ bool bShowColGrand = bColumnGrand && nColDimCount2 > 0;
+ bool bShowRowGrand = bRowGrand && nRowDimCount2 > 0;
+ pColResRoot.reset( new ScDPResultMember(pResData.get(), bShowColGrand) );
+ pRowResRoot.reset( new ScDPResultMember(pResData.get(), bShowRowGrand) );
+
+ FillCalcInfo(false, aInfo, bHasAutoShow);
+ tools::Long nColLevelCount = aInfo.aColLevels.size();
+
+ pColResRoot->InitFrom( aInfo.aColDims, aInfo.aColLevels, 0, aInitState );
+ pColResRoot->SetHasElements();
+
+ FillCalcInfo(true, aInfo, bHasAutoShow);
+ tools::Long nRowLevelCount = aInfo.aRowLevels.size();
+
+ if ( nRowLevelCount > 0 )
+ {
+ // disable layout flags for the innermost row field (level)
+ aInfo.aRowLevels[nRowLevelCount-1]->SetEnableLayout( false );
+ }
+
+ pRowResRoot->InitFrom( aInfo.aRowDims, aInfo.aRowLevels, 0, aInitState );
+ pRowResRoot->SetHasElements();
+
+ // initialize members object also for all page dimensions (needed for numeric groups)
+ for (const auto& rDimIndex : maPageDims)
+ {
+ ScDPDimension* pDim = GetDimensionsObject()->getByIndex(rDimIndex);
+ tools::Long nHierarchy = ScDPDimension::getUsedHierarchy();
+ if ( nHierarchy >= ScDPHierarchies::getCount() )
+ nHierarchy = 0;
+
+ ScDPLevels* pLevels = pDim->GetHierarchiesObject()->getByIndex(nHierarchy)->GetLevelsObject();
+ tools::Long nCount = pLevels->getCount();
+ for (tools::Long j=0; j<nCount; j++)
+ pLevels->getByIndex(j)->GetMembersObject(); // initialize for groups
+ }
+
+ // pre-check: calculate minimum number of result columns / rows from
+ // levels that have the "show all" flag set
+
+ tools::Long nMinColMembers = lcl_CountMinMembers( aInfo.aColDims, aInfo.aColLevels, nColLevelCount );
+ tools::Long nMinRowMembers = lcl_CountMinMembers( aInfo.aRowDims, aInfo.aRowLevels, nRowLevelCount );
+
+ if ( nMinColMembers > MAXCOLCOUNT/*SC_MINCOUNT_LIMIT*/ || nMinRowMembers > SC_MINCOUNT_LIMIT )
+ {
+ // resulting table is too big -> abort before calculating
+ // (this relies on late init, so no members are allocated in InitFrom above)
+
+ bResultOverflow = true;
+ return;
+ }
+
+ FilterCacheByPageDimensions();
+
+ aInfo.aPageDims = maPageDims;
+ aInfo.pInitState = &aInitState;
+ aInfo.pColRoot = pColResRoot.get();
+ aInfo.pRowRoot = pRowResRoot.get();
+ pData->CalcResults(aInfo, false);
+
+ pColResRoot->CheckShowEmpty();
+ pRowResRoot->CheckShowEmpty();
+
+ // With all data processed, calculate the final results:
+
+ // UpdateDataResults calculates all original results from the collected values,
+ // and stores them as reference values if needed.
+ pRowResRoot->UpdateDataResults( pColResRoot.get(), pResData->GetRowStartMeasure() );
+
+ if ( bHasAutoShow ) // do the double calculation only if AutoShow is used
+ {
+ // Find the desired members and set bAutoHidden flag for the others
+ pRowResRoot->DoAutoShow( pColResRoot.get() );
+
+ // Reset all results to empty, so they can be built again with data for the
+ // desired members only.
+ pColResRoot->ResetResults();
+ pRowResRoot->ResetResults();
+ pData->CalcResults(aInfo, true);
+
+ // Call UpdateDataResults again, with the new (limited) values.
+ pRowResRoot->UpdateDataResults( pColResRoot.get(), pResData->GetRowStartMeasure() );
+ }
+
+ // SortMembers does the sorting by a result dimension, using the original results,
+ // but not running totals etc.
+ pRowResRoot->SortMembers( pColResRoot.get() );
+
+ // UpdateRunningTotals calculates running totals along column/row dimensions,
+ // differences from other members (named or relative), and column/row percentages
+ // or index values.
+ // Running totals and relative differences need to be done using the sorted values.
+ // Column/row percentages and index values must be done after sorting, because the
+ // results may no longer be in the right order (row total for percentage of row is
+ // always 1).
+ ScDPRunningTotalState aRunning( pColResRoot.get(), pRowResRoot.get() );
+ ScDPRowTotals aTotals;
+ pRowResRoot->UpdateRunningTotals( pColResRoot.get(), pResData->GetRowStartMeasure(), aRunning, aTotals );
+
+#if DUMP_PIVOT_TABLE
+ DumpResults();
+#endif
+}
+
+void ScDPSource::FillLevelList( sheet::DataPilotFieldOrientation nOrientation, std::vector<ScDPLevel*> &rList )
+{
+ rList.clear();
+
+ std::vector<sal_Int32>* pDimIndex = nullptr;
+ switch (nOrientation)
+ {
+ case sheet::DataPilotFieldOrientation_COLUMN:
+ pDimIndex = &maColDims;
+ break;
+ case sheet::DataPilotFieldOrientation_ROW:
+ pDimIndex = &maRowDims;
+ break;
+ case sheet::DataPilotFieldOrientation_DATA:
+ pDimIndex = &maDataDims;
+ break;
+ case sheet::DataPilotFieldOrientation_PAGE:
+ pDimIndex = &maPageDims;
+ break;
+ default:
+ OSL_FAIL( "ScDPSource::FillLevelList: unexpected orientation" );
+ break;
+ }
+ if (!pDimIndex)
+ {
+ OSL_FAIL("invalid orientation");
+ return;
+ }
+
+ ScDPDimensions* pDims = GetDimensionsObject();
+ for (const auto& rIndex : *pDimIndex)
+ {
+ ScDPDimension* pDim = pDims->getByIndex(rIndex);
+ OSL_ENSURE( pDim->getOrientation() == nOrientation, "orientations are wrong" );
+
+ ScDPHierarchies* pHiers = pDim->GetHierarchiesObject();
+ sal_Int32 nHierarchy = ScDPDimension::getUsedHierarchy();
+ if ( nHierarchy >= ScDPHierarchies::getCount() )
+ nHierarchy = 0;
+ ScDPHierarchy* pHier = pHiers->getByIndex(nHierarchy);
+ ScDPLevels* pLevels = pHier->GetLevelsObject();
+ sal_Int32 nLevCount = pLevels->getCount();
+ for (sal_Int32 nLev=0; nLev<nLevCount; nLev++)
+ {
+ ScDPLevel* pLevel = pLevels->getByIndex(nLev);
+ rList.push_back(pLevel);
+ }
+ }
+}
+
+void ScDPSource::FillMemberResults()
+{
+ if ( pColResults || pRowResults )
+ return;
+
+ CreateRes_Impl();
+
+ if ( bResultOverflow ) // set in CreateRes_Impl
+ {
+ // no results available -> abort (leave empty)
+ // exception is thrown in ScDPSource::getResults
+ return;
+ }
+
+ FillLevelList( sheet::DataPilotFieldOrientation_COLUMN, aColLevelList );
+ sal_Int32 nColLevelCount = aColLevelList.size();
+ if (nColLevelCount)
+ {
+ tools::Long nColDimSize = pColResRoot->GetSize(pResData->GetColStartMeasure());
+ pColResults.reset(new uno::Sequence<sheet::MemberResult>[nColLevelCount]);
+ for (tools::Long i=0; i<nColLevelCount; i++)
+ pColResults[i].realloc(nColDimSize);
+
+ tools::Long nPos = 0;
+ pColResRoot->FillMemberResults( pColResults.get(), nPos, pResData->GetColStartMeasure(),
+ true, nullptr, nullptr );
+ }
+
+ FillLevelList( sheet::DataPilotFieldOrientation_ROW, aRowLevelList );
+ tools::Long nRowLevelCount = aRowLevelList.size();
+ if (nRowLevelCount)
+ {
+ tools::Long nRowDimSize = pRowResRoot->GetSize(pResData->GetRowStartMeasure());
+ pRowResults.reset( new uno::Sequence<sheet::MemberResult>[nRowLevelCount] );
+ for (tools::Long i=0; i<nRowLevelCount; i++)
+ pRowResults[i].realloc(nRowDimSize);
+
+ tools::Long nPos = 0;
+ pRowResRoot->FillMemberResults( pRowResults.get(), nPos, pResData->GetRowStartMeasure(),
+ true, nullptr, nullptr );
+ }
+}
+
+const uno::Sequence<sheet::MemberResult>* ScDPSource::GetMemberResults( const ScDPLevel* pLevel )
+{
+ FillMemberResults();
+
+ sal_Int32 i = 0;
+ sal_Int32 nColCount = aColLevelList.size();
+ for (i=0; i<nColCount; i++)
+ {
+ ScDPLevel* pColLevel = aColLevelList[i];
+ if ( pColLevel == pLevel )
+ return &pColResults[i];
+ }
+ sal_Int32 nRowCount = aRowLevelList.size();
+ for (i=0; i<nRowCount; i++)
+ {
+ ScDPLevel* pRowLevel = aRowLevelList[i];
+ if ( pRowLevel == pLevel )
+ return &pRowResults[i];
+ }
+ return nullptr;
+}
+
+// XPropertySet
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScDPSource::getPropertySetInfo()
+{
+ using beans::PropertyAttribute::READONLY;
+
+ static const SfxItemPropertyMapEntry aDPSourceMap_Impl[] =
+ {
+ { SC_UNO_DP_COLGRAND, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNO_DP_DATADESC, 0, cppu::UnoType<OUString>::get(), beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNO_DP_IGNOREEMPTY, 0, cppu::UnoType<bool>::get(), 0, 0 }, // for sheet data only
+ { SC_UNO_DP_REPEATEMPTY, 0, cppu::UnoType<bool>::get(), 0, 0 }, // for sheet data only
+ { SC_UNO_DP_ROWGRAND, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNO_DP_ROWFIELDCOUNT, 0, cppu::UnoType<sal_Int32>::get(), READONLY, 0 },
+ { SC_UNO_DP_COLUMNFIELDCOUNT, 0, cppu::UnoType<sal_Int32>::get(), READONLY, 0 },
+ { SC_UNO_DP_DATAFIELDCOUNT, 0, cppu::UnoType<sal_Int32>::get(), READONLY, 0 },
+ { SC_UNO_DP_GRANDTOTAL_NAME, 0, cppu::UnoType<OUString>::get(), 0, 0 },
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ static uno::Reference<beans::XPropertySetInfo> aRef =
+ new SfxItemPropertySetInfo( aDPSourceMap_Impl );
+ return aRef;
+}
+
+void SAL_CALL ScDPSource::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue )
+{
+ if (aPropertyName == SC_UNO_DP_COLGRAND)
+ bColumnGrand = lcl_GetBoolFromAny(aValue);
+ else if (aPropertyName == SC_UNO_DP_ROWGRAND)
+ bRowGrand = lcl_GetBoolFromAny(aValue);
+ else if (aPropertyName == SC_UNO_DP_IGNOREEMPTY)
+ setIgnoreEmptyRows( lcl_GetBoolFromAny( aValue ) );
+ else if (aPropertyName == SC_UNO_DP_REPEATEMPTY)
+ setRepeatIfEmpty( lcl_GetBoolFromAny( aValue ) );
+ else if (aPropertyName == SC_UNO_DP_GRANDTOTAL_NAME)
+ {
+ OUString aName;
+ if (aValue >>= aName)
+ mpGrandTotalName = aName;
+ }
+ else
+ {
+ OSL_FAIL("unknown property");
+ //TODO: THROW( UnknownPropertyException() );
+ }
+}
+
+uno::Any SAL_CALL ScDPSource::getPropertyValue( const OUString& aPropertyName )
+{
+ uno::Any aRet;
+ if ( aPropertyName == SC_UNO_DP_COLGRAND )
+ aRet <<= bColumnGrand;
+ else if ( aPropertyName == SC_UNO_DP_ROWGRAND )
+ aRet <<= bRowGrand;
+ else if ( aPropertyName == SC_UNO_DP_IGNOREEMPTY )
+ aRet <<= bIgnoreEmptyRows;
+ else if ( aPropertyName == SC_UNO_DP_REPEATEMPTY )
+ aRet <<= bRepeatIfEmpty;
+ else if ( aPropertyName == SC_UNO_DP_DATADESC ) // read-only
+ aRet <<= getDataDescription();
+ else if ( aPropertyName == SC_UNO_DP_ROWFIELDCOUNT ) // read-only
+ aRet <<= static_cast<sal_Int32>(maRowDims.size());
+ else if ( aPropertyName == SC_UNO_DP_COLUMNFIELDCOUNT ) // read-only
+ aRet <<= static_cast<sal_Int32>(maColDims.size());
+ else if ( aPropertyName == SC_UNO_DP_DATAFIELDCOUNT ) // read-only
+ aRet <<= static_cast<sal_Int32>(maDataDims.size());
+ else if (aPropertyName == SC_UNO_DP_GRANDTOTAL_NAME)
+ {
+ if (mpGrandTotalName)
+ aRet <<= *mpGrandTotalName;
+ }
+ else
+ {
+ OSL_FAIL("unknown property");
+ //TODO: THROW( UnknownPropertyException() );
+ }
+ return aRet;
+}
+
+#if DUMP_PIVOT_TABLE
+void ScDPSource::DumpResults() const
+{
+ std::cout << "+++++ column root" << std::endl;
+ pColResRoot->Dump(1);
+ std::cout << "+++++ row root" << std::endl;
+ pRowResRoot->Dump(1);
+}
+#endif
+
+SC_IMPL_DUMMY_PROPERTY_LISTENER( ScDPSource )
+
+ScDPDimensions::ScDPDimensions( ScDPSource* pSrc ) :
+ pSource( pSrc )
+{
+ //TODO: hold pSource
+
+ // include data layout dimension and duplicated dimensions
+ nDimCount = pSource->GetData()->GetColumnCount() + 1 + pSource->GetDupCount();
+}
+
+ScDPDimensions::~ScDPDimensions()
+{
+ //TODO: release pSource
+}
+
+void ScDPDimensions::CountChanged()
+{
+ // include data layout dimension and duplicated dimensions
+ sal_Int32 nNewCount = pSource->GetData()->GetColumnCount() + 1 + pSource->GetDupCount();
+ if ( ppDims )
+ {
+ sal_Int32 i;
+ sal_Int32 nCopy = std::min( nNewCount, nDimCount );
+ rtl::Reference<ScDPDimension>* ppNew = new rtl::Reference<ScDPDimension>[nNewCount];
+
+ for (i=0; i<nCopy; i++) // copy existing dims
+ ppNew[i] = ppDims[i];
+ for (i=nCopy; i<nNewCount; i++) // clear additional pointers
+ ppNew[i] = nullptr;
+
+ ppDims.reset( ppNew );
+ }
+ nDimCount = nNewCount;
+}
+
+// very simple XNameAccess implementation using getCount/getByIndex
+
+uno::Any SAL_CALL ScDPDimensions::getByName( const OUString& aName )
+{
+ sal_Int32 nCount = getCount();
+ for (sal_Int32 i=0; i<nCount; i++)
+ if ( getByIndex(i)->getName() == aName )
+ {
+ uno::Reference<container::XNamed> xNamed = getByIndex(i);
+ uno::Any aRet;
+ aRet <<= xNamed;
+ return aRet;
+ }
+
+ throw container::NoSuchElementException();
+// return uno::Any();
+}
+
+uno::Sequence<OUString> SAL_CALL ScDPDimensions::getElementNames()
+{
+ tools::Long nCount = getCount();
+ uno::Sequence<OUString> aSeq(nCount);
+ OUString* pArr = aSeq.getArray();
+ for (tools::Long i=0; i<nCount; i++)
+ pArr[i] = getByIndex(i)->getName();
+ return aSeq;
+}
+
+sal_Bool SAL_CALL ScDPDimensions::hasByName( const OUString& aName )
+{
+ tools::Long nCount = getCount();
+ for (tools::Long i=0; i<nCount; i++)
+ if ( getByIndex(i)->getName() == aName )
+ return true;
+ return false;
+}
+
+uno::Type SAL_CALL ScDPDimensions::getElementType()
+{
+ return cppu::UnoType<container::XNamed>::get();
+}
+
+sal_Bool SAL_CALL ScDPDimensions::hasElements()
+{
+ return ( getCount() > 0 );
+}
+
+// end of XNameAccess implementation
+
+tools::Long ScDPDimensions::getCount() const
+{
+ // in tabular data, every column of source data is a dimension
+
+ return nDimCount;
+}
+
+ScDPDimension* ScDPDimensions::getByIndex(tools::Long nIndex) const
+{
+ if ( nIndex >= 0 && nIndex < nDimCount )
+ {
+ if ( !ppDims )
+ {
+ const_cast<ScDPDimensions*>(this)->ppDims.reset(new rtl::Reference<ScDPDimension>[nDimCount] );
+ for (tools::Long i=0; i<nDimCount; i++)
+ ppDims[i] = nullptr;
+ }
+ if ( !ppDims[nIndex].is() )
+ {
+ ppDims[nIndex] = new ScDPDimension( pSource, nIndex );
+ }
+
+ return ppDims[nIndex].get();
+ }
+
+ return nullptr; //TODO: exception?
+}
+
+ScDPDimension::ScDPDimension( ScDPSource* pSrc, tools::Long nD ) :
+ pSource( pSrc ),
+ nDim( nD ),
+ nFunction( ScGeneralFunction::SUM ), // sum is default
+ nSourceDim( -1 ),
+ bHasSelectedPage( false ),
+ mbHasHiddenMember(false)
+{
+ //TODO: hold pSource
+}
+
+ScDPDimension::~ScDPDimension()
+{
+ //TODO: release pSource
+}
+
+ScDPHierarchies* ScDPDimension::GetHierarchiesObject()
+{
+ if (!mxHierarchies.is())
+ {
+ mxHierarchies = new ScDPHierarchies( pSource, nDim );
+ }
+ return mxHierarchies.get();
+}
+
+const std::optional<OUString> & ScDPDimension::GetLayoutName() const
+{
+ return mpLayoutName;
+}
+
+const std::optional<OUString> & ScDPDimension::GetSubtotalName() const
+{
+ return mpSubtotalName;
+}
+
+uno::Reference<container::XNameAccess> SAL_CALL ScDPDimension::getHierarchies()
+{
+ return GetHierarchiesObject();
+}
+
+OUString SAL_CALL ScDPDimension::getName()
+{
+ if (!aName.isEmpty())
+ return aName;
+ else
+ return pSource->GetData()->getDimensionName( nDim );
+}
+
+void SAL_CALL ScDPDimension::setName( const OUString& rNewName )
+{
+ // used after cloning
+ aName = rNewName;
+}
+
+sheet::DataPilotFieldOrientation ScDPDimension::getOrientation() const
+{
+ return pSource->GetOrientation( nDim );
+}
+
+bool ScDPDimension::getIsDataLayoutDimension() const
+{
+ return pSource->GetData()->getIsDataLayoutDimension( nDim );
+}
+
+void ScDPDimension::setFunction(ScGeneralFunction nNew)
+{
+ nFunction = nNew;
+}
+
+ScDPDimension* ScDPDimension::CreateCloneObject()
+{
+ OSL_ENSURE( nSourceDim < 0, "recursive duplicate - not implemented" );
+
+ //TODO: set new name here, or temporary name ???
+ OUString aNewName = aName;
+
+ ScDPDimension* pNew = pSource->AddDuplicated( aNewName );
+
+ pNew->aName = aNewName; //TODO: here or in source?
+ pNew->nSourceDim = nDim; //TODO: recursive?
+
+ return pNew;
+}
+
+uno::Reference<util::XCloneable> SAL_CALL ScDPDimension::createClone()
+{
+ return CreateCloneObject();
+}
+
+const ScDPItemData& ScDPDimension::GetSelectedData()
+{
+ if ( !pSelectedData )
+ {
+ // find the named member to initialize pSelectedData from it, with name and value
+
+ tools::Long nLevel = 0;
+
+ tools::Long nHierarchy = getUsedHierarchy();
+ if ( nHierarchy >= ScDPHierarchies::getCount() )
+ nHierarchy = 0;
+ ScDPLevels* pLevels = GetHierarchiesObject()->getByIndex(nHierarchy)->GetLevelsObject();
+ tools::Long nLevCount = pLevels->getCount();
+ if ( nLevel < nLevCount )
+ {
+ ScDPMembers* pMembers = pLevels->getByIndex(nLevel)->GetMembersObject();
+
+ //TODO: merge with ScDPMembers::getByName
+ tools::Long nCount = pMembers->getCount();
+ for (tools::Long i=0; i<nCount && !pSelectedData; i++)
+ {
+ ScDPMember* pMember = pMembers->getByIndex(i);
+ if (aSelectedPage == pMember->GetNameStr(false))
+ {
+ pSelectedData.reset( new ScDPItemData(pMember->FillItemData()) );
+ }
+ }
+ }
+
+ if ( !pSelectedData )
+ pSelectedData.reset( new ScDPItemData(aSelectedPage) ); // default - name only
+ }
+
+ return *pSelectedData;
+}
+
+// XPropertySet
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScDPDimension::getPropertySetInfo()
+{
+ static const SfxItemPropertyMapEntry aDPDimensionMap_Impl[] =
+ {
+ { SC_UNO_DP_FILTER, 0, cppu::UnoType<uno::Sequence<sheet::TableFilterField>>::get(), 0, 0 },
+ { SC_UNO_DP_FLAGS, 0, cppu::UnoType<sal_Int32>::get(), beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNO_DP_FUNCTION, 0, cppu::UnoType<sheet::GeneralFunction>::get(), 0, 0 },
+ { SC_UNO_DP_FUNCTION2, 0, cppu::UnoType<sal_Int16>::get(), 0, 0 },
+ { SC_UNO_DP_ISDATALAYOUT, 0, cppu::UnoType<bool>::get(), beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNO_DP_NUMBERFO, 0, cppu::UnoType<sal_Int32>::get(), beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNO_DP_ORIENTATION, 0, cppu::UnoType<sheet::DataPilotFieldOrientation>::get(), 0, 0 },
+ { SC_UNO_DP_ORIGINAL, 0, cppu::UnoType<container::XNamed>::get(), beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNO_DP_ORIGINAL_POS, 0, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNO_DP_POSITION, 0, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNO_DP_REFVALUE, 0, cppu::UnoType<sheet::DataPilotFieldReference>::get(), 0, 0 },
+ { SC_UNO_DP_USEDHIERARCHY, 0, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNO_DP_LAYOUTNAME, 0, cppu::UnoType<OUString>::get(), 0, 0 },
+ { SC_UNO_DP_FIELD_SUBTOTALNAME, 0, cppu::UnoType<OUString>::get(), 0, 0 },
+ { SC_UNO_DP_HAS_HIDDEN_MEMBER, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ static uno::Reference<beans::XPropertySetInfo> aRef =
+ new SfxItemPropertySetInfo( aDPDimensionMap_Impl );
+ return aRef;
+}
+
+void SAL_CALL ScDPDimension::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue )
+{
+ if ( aPropertyName == SC_UNO_DP_USEDHIERARCHY )
+ {
+ // #i52547# don't use the incomplete date hierarchy implementation - ignore the call
+ }
+ else if ( aPropertyName == SC_UNO_DP_ORIENTATION )
+ {
+ sheet::DataPilotFieldOrientation eEnum;
+ if (aValue >>= eEnum)
+ pSource->SetOrientation( nDim, eEnum );
+ }
+ else if ( aPropertyName == SC_UNO_DP_FUNCTION )
+ {
+ sheet::GeneralFunction eEnum;
+ if (aValue >>= eEnum)
+ setFunction( static_cast<ScGeneralFunction>(eEnum) );
+ }
+ else if ( aPropertyName == SC_UNO_DP_FUNCTION2 )
+ {
+ sal_Int16 eEnum;
+ if (aValue >>= eEnum)
+ setFunction( static_cast<ScGeneralFunction>(eEnum) );
+ }
+ else if ( aPropertyName == SC_UNO_DP_REFVALUE )
+ aValue >>= aReferenceValue;
+ else if ( aPropertyName == SC_UNO_DP_FILTER )
+ {
+ bool bDone = false;
+ uno::Sequence<sheet::TableFilterField> aSeq;
+ if (aValue >>= aSeq)
+ {
+ sal_Int32 nLength = aSeq.getLength();
+ if ( nLength == 0 )
+ {
+ aSelectedPage.clear();
+ bHasSelectedPage = false;
+ bDone = true;
+ }
+ else if ( nLength == 1 )
+ {
+ const sheet::TableFilterField& rField = aSeq[0];
+ if ( rField.Field == 0 && rField.Operator == sheet::FilterOperator_EQUAL && !rField.IsNumeric )
+ {
+ aSelectedPage = rField.StringValue;
+ bHasSelectedPage = true;
+ bDone = true;
+ }
+ }
+ }
+ if ( !bDone )
+ {
+ OSL_FAIL("Filter property is not a single string");
+ throw lang::IllegalArgumentException();
+ }
+ pSelectedData.reset(); // invalid after changing aSelectedPage
+ }
+ else if (aPropertyName == SC_UNO_DP_LAYOUTNAME)
+ {
+ OUString aTmpName;
+ if (aValue >>= aTmpName)
+ mpLayoutName = aTmpName;
+ }
+ else if (aPropertyName == SC_UNO_DP_FIELD_SUBTOTALNAME)
+ {
+ OUString aTmpName;
+ if (aValue >>= aTmpName)
+ mpSubtotalName = aTmpName;
+ }
+ else if (aPropertyName == SC_UNO_DP_HAS_HIDDEN_MEMBER)
+ {
+ bool b = false;
+ aValue >>= b;
+ mbHasHiddenMember = b;
+ }
+ else
+ {
+ OSL_FAIL("unknown property");
+ //TODO: THROW( UnknownPropertyException() );
+ }
+}
+
+uno::Any SAL_CALL ScDPDimension::getPropertyValue( const OUString& aPropertyName )
+{
+ uno::Any aRet;
+ if ( aPropertyName == SC_UNO_DP_POSITION )
+ aRet <<= pSource->GetPosition( nDim );
+ else if ( aPropertyName == SC_UNO_DP_USEDHIERARCHY )
+ aRet <<= static_cast<sal_Int32>(getUsedHierarchy());
+ else if ( aPropertyName == SC_UNO_DP_ORIENTATION )
+ {
+ sheet::DataPilotFieldOrientation eVal = getOrientation();
+ aRet <<= eVal;
+ }
+ else if ( aPropertyName == SC_UNO_DP_FUNCTION )
+ {
+ ScGeneralFunction nVal = getFunction();
+ if (nVal == ScGeneralFunction::MEDIAN)
+ nVal = ScGeneralFunction::NONE;
+ const int nValAsInt = static_cast<int>(nVal);
+ assert(nValAsInt >= int(css::sheet::GeneralFunction_NONE) &&
+ nValAsInt <= int(css::sheet::GeneralFunction_VARP));
+ aRet <<= static_cast<sheet::GeneralFunction>(nValAsInt);
+ }
+ else if ( aPropertyName == SC_UNO_DP_FUNCTION2 )
+ {
+ ScGeneralFunction eVal = getFunction();
+ aRet <<= static_cast<sal_Int16>(eVal);
+ }
+ else if ( aPropertyName == SC_UNO_DP_REFVALUE )
+ aRet <<= aReferenceValue;
+ else if ( aPropertyName == SC_UNO_DP_ISDATALAYOUT ) // read-only properties
+ aRet <<= getIsDataLayoutDimension();
+ else if ( aPropertyName == SC_UNO_DP_NUMBERFO )
+ {
+ sal_Int32 nFormat = 0;
+ ScGeneralFunction eFunc = getFunction();
+ // #i63745# don't use source format for "count"
+ if ( eFunc != ScGeneralFunction::COUNT && eFunc != ScGeneralFunction::COUNTNUMS )
+ nFormat = pSource->GetData()->GetNumberFormat( ( nSourceDim >= 0 ) ? nSourceDim : nDim );
+
+ switch ( aReferenceValue.ReferenceType )
+ {
+ case sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE:
+ case sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE:
+ case sheet::DataPilotFieldReferenceType::ROW_PERCENTAGE:
+ case sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE:
+ case sheet::DataPilotFieldReferenceType::TOTAL_PERCENTAGE:
+ nFormat = pSource->GetData()->GetNumberFormatByIdx( NF_PERCENT_DEC2 );
+ break;
+ case sheet::DataPilotFieldReferenceType::INDEX:
+ nFormat = pSource->GetData()->GetNumberFormatByIdx( NF_NUMBER_SYSTEM );
+ break;
+ default:
+ break;
+ }
+
+ aRet <<= nFormat;
+ }
+ else if ( aPropertyName == SC_UNO_DP_ORIGINAL )
+ {
+ uno::Reference<container::XNamed> xOriginal;
+ if (nSourceDim >= 0)
+ xOriginal = pSource->GetDimensionsObject()->getByIndex(nSourceDim);
+ aRet <<= xOriginal;
+ }
+ else if (aPropertyName == SC_UNO_DP_ORIGINAL_POS)
+ {
+ aRet <<= nSourceDim;
+ }
+ else if ( aPropertyName == SC_UNO_DP_FILTER )
+ {
+ if ( bHasSelectedPage )
+ {
+ // single filter field: first field equal to selected string
+ sheet::TableFilterField aField( sheet::FilterConnection_AND, 0,
+ sheet::FilterOperator_EQUAL, false, 0.0, aSelectedPage );
+ aRet <<= uno::Sequence<sheet::TableFilterField>( &aField, 1 );
+ }
+ else
+ aRet <<= uno::Sequence<sheet::TableFilterField>(0);
+ }
+ else if (aPropertyName == SC_UNO_DP_LAYOUTNAME)
+ aRet <<= mpLayoutName ? *mpLayoutName : OUString();
+ else if (aPropertyName == SC_UNO_DP_FIELD_SUBTOTALNAME)
+ aRet <<= mpSubtotalName ? *mpSubtotalName : OUString();
+ else if (aPropertyName == SC_UNO_DP_HAS_HIDDEN_MEMBER)
+ aRet <<= mbHasHiddenMember;
+ else if (aPropertyName == SC_UNO_DP_FLAGS)
+ {
+ aRet <<= sal_Int32(0); // tabular data: all orientations are possible
+ }
+ else
+ {
+ OSL_FAIL("unknown property");
+ //TODO: THROW( UnknownPropertyException() );
+ }
+ return aRet;
+}
+
+SC_IMPL_DUMMY_PROPERTY_LISTENER( ScDPDimension )
+
+ScDPHierarchies::ScDPHierarchies( ScDPSource* pSrc, tools::Long nD ) :
+ pSource( pSrc ),
+ nDim( nD )
+{
+ //TODO: hold pSource
+}
+
+ScDPHierarchies::~ScDPHierarchies()
+{
+ //TODO: release pSource
+}
+
+// very simple XNameAccess implementation using getCount/getByIndex
+
+uno::Any SAL_CALL ScDPHierarchies::getByName( const OUString& aName )
+{
+ tools::Long nCount = getCount();
+ for (tools::Long i=0; i<nCount; i++)
+ if ( getByIndex(i)->getName() == aName )
+ {
+ uno::Reference<container::XNamed> xNamed = getByIndex(i);
+ uno::Any aRet;
+ aRet <<= xNamed;
+ return aRet;
+ }
+
+ throw container::NoSuchElementException();
+}
+
+uno::Sequence<OUString> SAL_CALL ScDPHierarchies::getElementNames()
+{
+ tools::Long nCount = getCount();
+ uno::Sequence<OUString> aSeq(nCount);
+ OUString* pArr = aSeq.getArray();
+ for (tools::Long i=0; i<nCount; i++)
+ pArr[i] = getByIndex(i)->getName();
+ return aSeq;
+}
+
+sal_Bool SAL_CALL ScDPHierarchies::hasByName( const OUString& aName )
+{
+ tools::Long nCount = getCount();
+ for (tools::Long i=0; i<nCount; i++)
+ if ( getByIndex(i)->getName() == aName )
+ return true;
+ return false;
+}
+
+uno::Type SAL_CALL ScDPHierarchies::getElementType()
+{
+ return cppu::UnoType<container::XNamed>::get();
+}
+
+sal_Bool SAL_CALL ScDPHierarchies::hasElements()
+{
+ return ( getCount() > 0 );
+}
+
+// end of XNameAccess implementation
+
+sal_Int32 ScDPHierarchies::getCount()
+{
+ return nHierCount;
+}
+
+ScDPHierarchy* ScDPHierarchies::getByIndex(tools::Long nIndex) const
+{
+ // pass hierarchy index to new object in case the implementation
+ // will be extended to more than one hierarchy
+
+ if ( nIndex >= 0 && nIndex < nHierCount )
+ {
+ if ( !ppHiers )
+ {
+ const_cast<ScDPHierarchies*>(this)->ppHiers.reset( new rtl::Reference<ScDPHierarchy>[nHierCount] );
+ for (sal_Int32 i=0; i<nHierCount; i++)
+ ppHiers[i] = nullptr;
+ }
+ if ( !ppHiers[nIndex].is() )
+ {
+ ppHiers[nIndex] = new ScDPHierarchy( pSource, nDim, nIndex );
+ }
+
+ return ppHiers[nIndex].get();
+ }
+
+ return nullptr; //TODO: exception?
+}
+
+ScDPHierarchy::ScDPHierarchy( ScDPSource* pSrc, sal_Int32 nD, sal_Int32 nH ) :
+ pSource( pSrc ),
+ nDim( nD ),
+ nHier( nH )
+{
+ //TODO: hold pSource
+}
+
+ScDPHierarchy::~ScDPHierarchy()
+{
+ //TODO: release pSource
+}
+
+ScDPLevels* ScDPHierarchy::GetLevelsObject()
+{
+ if (!mxLevels.is())
+ {
+ mxLevels = new ScDPLevels( pSource, nDim, nHier );
+ }
+ return mxLevels.get();
+}
+
+uno::Reference<container::XNameAccess> SAL_CALL ScDPHierarchy::getLevels()
+{
+ return GetLevelsObject();
+}
+
+OUString SAL_CALL ScDPHierarchy::getName()
+{
+ OUString aRet; //TODO: globstr-ID !!!!
+ switch (nHier)
+ {
+ case SC_DAPI_HIERARCHY_FLAT:
+ aRet = "flat";
+ break; //TODO: name ???????
+ case SC_DAPI_HIERARCHY_QUARTER:
+ aRet = "Quarter";
+ break; //TODO: name ???????
+ case SC_DAPI_HIERARCHY_WEEK:
+ aRet = "Week";
+ break; //TODO: name ???????
+ default:
+ OSL_FAIL( "ScDPHierarchy::getName: unexpected hierarchy" );
+ break;
+ }
+ return aRet;
+}
+
+void SAL_CALL ScDPHierarchy::setName( const OUString& /* rNewName */ )
+{
+ OSL_FAIL("not implemented"); //TODO: exception?
+}
+
+ScDPLevels::ScDPLevels( ScDPSource* pSrc, sal_Int32 nD, sal_Int32 nH ) :
+ pSource( pSrc ),
+ nDim( nD ),
+ nHier( nH )
+{
+ //TODO: hold pSource
+
+ // text columns have only one level
+
+ tools::Long nSrcDim = pSource->GetSourceDim( nDim );
+ if ( pSource->IsDateDimension( nSrcDim ) )
+ {
+ switch ( nHier )
+ {
+ case SC_DAPI_HIERARCHY_FLAT: nLevCount = SC_DAPI_FLAT_LEVELS; break;
+ case SC_DAPI_HIERARCHY_QUARTER: nLevCount = SC_DAPI_QUARTER_LEVELS; break;
+ case SC_DAPI_HIERARCHY_WEEK: nLevCount = SC_DAPI_WEEK_LEVELS; break;
+ default:
+ OSL_FAIL("wrong hierarchy");
+ nLevCount = 0;
+ }
+ }
+ else
+ nLevCount = 1;
+}
+
+ScDPLevels::~ScDPLevels()
+{
+ //TODO: release pSource
+}
+
+// very simple XNameAccess implementation using getCount/getByIndex
+
+uno::Any SAL_CALL ScDPLevels::getByName( const OUString& aName )
+{
+ tools::Long nCount = getCount();
+ for (tools::Long i=0; i<nCount; i++)
+ if ( getByIndex(i)->getName() == aName )
+ {
+ uno::Reference<container::XNamed> xNamed = getByIndex(i);
+ uno::Any aRet;
+ aRet <<= xNamed;
+ return aRet;
+ }
+
+ throw container::NoSuchElementException();
+}
+
+uno::Sequence<OUString> SAL_CALL ScDPLevels::getElementNames()
+{
+ tools::Long nCount = getCount();
+ uno::Sequence<OUString> aSeq(nCount);
+ OUString* pArr = aSeq.getArray();
+ for (tools::Long i=0; i<nCount; i++)
+ pArr[i] = getByIndex(i)->getName();
+ return aSeq;
+}
+
+sal_Bool SAL_CALL ScDPLevels::hasByName( const OUString& aName )
+{
+ tools::Long nCount = getCount();
+ for (tools::Long i=0; i<nCount; i++)
+ if ( getByIndex(i)->getName() == aName )
+ return true;
+ return false;
+}
+
+uno::Type SAL_CALL ScDPLevels::getElementType()
+{
+ return cppu::UnoType<container::XNamed>::get();
+}
+
+sal_Bool SAL_CALL ScDPLevels::hasElements()
+{
+ return ( getCount() > 0 );
+}
+
+// end of XNameAccess implementation
+
+sal_Int32 ScDPLevels::getCount() const
+{
+ return nLevCount;
+}
+
+ScDPLevel* ScDPLevels::getByIndex(sal_Int32 nIndex) const
+{
+ if ( nIndex >= 0 && nIndex < nLevCount )
+ {
+ if ( !ppLevs )
+ {
+ const_cast<ScDPLevels*>(this)->ppLevs.reset(new rtl::Reference<ScDPLevel>[nLevCount] );
+ for (tools::Long i=0; i<nLevCount; i++)
+ ppLevs[i] = nullptr;
+ }
+ if ( !ppLevs[nIndex].is() )
+ {
+ ppLevs[nIndex] = new ScDPLevel( pSource, nDim, nHier, nIndex );
+ }
+
+ return ppLevs[nIndex].get();
+ }
+
+ return nullptr; //TODO: exception?
+}
+
+namespace {
+
+class ScDPGlobalMembersOrder
+{
+ ScDPLevel& rLevel;
+ bool bAscending;
+
+public:
+ ScDPGlobalMembersOrder( ScDPLevel& rLev, bool bAsc ) :
+ rLevel(rLev),
+ bAscending(bAsc)
+ {}
+
+ bool operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const;
+};
+
+}
+
+bool ScDPGlobalMembersOrder::operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const
+{
+ sal_Int32 nCompare = 0;
+ // seems that some ::std::sort() implementations pass the same index twice
+ if( nIndex1 != nIndex2 )
+ {
+ ScDPMembers* pMembers = rLevel.GetMembersObject();
+ ScDPMember* pMember1 = pMembers->getByIndex(nIndex1);
+ ScDPMember* pMember2 = pMembers->getByIndex(nIndex2);
+ nCompare = pMember1->Compare( *pMember2 );
+ }
+ return bAscending ? (nCompare < 0) : (nCompare > 0);
+}
+
+ScDPLevel::ScDPLevel( ScDPSource* pSrc, sal_Int32 nD, sal_Int32 nH, sal_Int32 nL ) :
+ pSource( pSrc ),
+ nDim( nD ),
+ nHier( nH ),
+ nLev( nL ),
+ aSortInfo( OUString(), true, sheet::DataPilotFieldSortMode::NAME ), // default: sort by name
+ nSortMeasure( 0 ),
+ nAutoMeasure( 0 ),
+ bShowEmpty( false ),
+ bEnableLayout( false ),
+ bRepeatItemLabels( false )
+{
+ //TODO: hold pSource
+ // aSubTotals is empty
+}
+
+ScDPLevel::~ScDPLevel()
+{
+ //TODO: release pSource
+}
+
+void ScDPLevel::EvaluateSortOrder()
+{
+ switch (aSortInfo.Mode)
+ {
+ case sheet::DataPilotFieldSortMode::DATA:
+ {
+ // find index of measure (index among data dimensions)
+
+ tools::Long nMeasureCount = pSource->GetDataDimensionCount();
+ for (tools::Long nMeasure=0; nMeasure<nMeasureCount; nMeasure++)
+ {
+ if (pSource->GetDataDimName(nMeasure) == aSortInfo.Field)
+ {
+ nSortMeasure = nMeasure;
+ break;
+ }
+ }
+
+ //TODO: error if not found?
+ }
+ break;
+ case sheet::DataPilotFieldSortMode::MANUAL:
+ case sheet::DataPilotFieldSortMode::NAME:
+ {
+ ScDPMembers* pLocalMembers = GetMembersObject();
+ tools::Long nCount = pLocalMembers->getCount();
+
+ aGlobalOrder.resize( nCount );
+ for (tools::Long nPos=0; nPos<nCount; nPos++)
+ aGlobalOrder[nPos] = nPos;
+
+ // allow manual or name (manual is always ascending)
+ bool bAscending = ( aSortInfo.Mode == sheet::DataPilotFieldSortMode::MANUAL || aSortInfo.IsAscending );
+ ScDPGlobalMembersOrder aComp( *this, bAscending );
+ ::std::sort( aGlobalOrder.begin(), aGlobalOrder.end(), aComp );
+ }
+ break;
+ }
+
+ if ( !aAutoShowInfo.IsEnabled )
+ return;
+
+ // find index of measure (index among data dimensions)
+
+ tools::Long nMeasureCount = pSource->GetDataDimensionCount();
+ for (tools::Long nMeasure=0; nMeasure<nMeasureCount; nMeasure++)
+ {
+ if (pSource->GetDataDimName(nMeasure) == aAutoShowInfo.DataField)
+ {
+ nAutoMeasure = nMeasure;
+ break;
+ }
+ }
+
+ //TODO: error if not found?
+}
+
+void ScDPLevel::SetEnableLayout(bool bSet)
+{
+ bEnableLayout = bSet;
+}
+
+ScDPMembers* ScDPLevel::GetMembersObject()
+{
+ if (!mxMembers.is())
+ {
+ mxMembers = new ScDPMembers( pSource, nDim, nHier, nLev );
+ }
+ return mxMembers.get();
+}
+
+uno::Reference<sheet::XMembersAccess> SAL_CALL ScDPLevel::getMembers()
+{
+ return GetMembersObject();
+}
+
+uno::Sequence<sheet::MemberResult> SAL_CALL ScDPLevel::getResults()
+{
+ const uno::Sequence<sheet::MemberResult>* pRes = pSource->GetMemberResults( this );
+ if (pRes)
+ return *pRes;
+
+ return {}; //TODO: Error?
+}
+
+OUString SAL_CALL ScDPLevel::getName()
+{
+ tools::Long nSrcDim = pSource->GetSourceDim( nDim );
+ if ( pSource->IsDateDimension( nSrcDim ) )
+ {
+ OUString aRet; //TODO: globstr-ID !!!!
+
+ if ( nHier == SC_DAPI_HIERARCHY_QUARTER )
+ {
+ switch ( nLev )
+ {
+ case SC_DAPI_LEVEL_YEAR:
+ aRet = "Year";
+ break;
+ case SC_DAPI_LEVEL_QUARTER:
+ aRet = "Quarter";
+ break;
+ case SC_DAPI_LEVEL_MONTH:
+ aRet = "Month";
+ break;
+ case SC_DAPI_LEVEL_DAY:
+ aRet = "Day";
+ break;
+ default:
+ OSL_FAIL( "ScDPLevel::getName: unexpected level" );
+ break;
+ }
+ }
+ else if ( nHier == SC_DAPI_HIERARCHY_WEEK )
+ {
+ switch ( nLev )
+ {
+ case SC_DAPI_LEVEL_YEAR:
+ aRet = "Year";
+ break;
+ case SC_DAPI_LEVEL_WEEK:
+ aRet = "Week";
+ break;
+ case SC_DAPI_LEVEL_WEEKDAY:
+ aRet = "Weekday";
+ break;
+ default:
+ OSL_FAIL( "ScDPLevel::getName: unexpected level" );
+ break;
+ }
+ }
+ if (!aRet.isEmpty())
+ return aRet;
+ }
+
+ ScDPDimension* pDim = pSource->GetDimensionsObject()->getByIndex(nSrcDim);
+ if (!pDim)
+ return OUString();
+
+ return pDim->getName();
+}
+
+void SAL_CALL ScDPLevel::setName( const OUString& /* rNewName */ )
+{
+ OSL_FAIL("not implemented"); //TODO: exception?
+}
+
+uno::Sequence<sal_Int16> ScDPLevel::getSubTotals() const
+{
+ //TODO: separate functions for settings and evaluation?
+
+ tools::Long nSrcDim = pSource->GetSourceDim( nDim );
+ if ( !pSource->SubTotalAllowed( nSrcDim ) )
+ return {};
+
+ return aSubTotals;
+}
+
+// XPropertySet
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScDPLevel::getPropertySetInfo()
+{
+ static const SfxItemPropertyMapEntry aDPLevelMap_Impl[] =
+ {
+ //TODO: change type of AutoShow/Layout/Sorting to API struct when available
+ { SC_UNO_DP_AUTOSHOW, 0, cppu::UnoType<sheet::DataPilotFieldAutoShowInfo>::get(), 0, 0 },
+ { SC_UNO_DP_LAYOUT, 0, cppu::UnoType<sheet::DataPilotFieldLayoutInfo>::get(), 0, 0 },
+ { SC_UNO_DP_SHOWEMPTY, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNO_DP_REPEATITEMLABELS, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNO_DP_SORTING, 0, cppu::UnoType<sheet::DataPilotFieldSortInfo>::get(), 0, 0 },
+ { SC_UNO_DP_SUBTOTAL, 0, cppu::UnoType<uno::Sequence<sheet::GeneralFunction>>::get(), 0, 0 },
+ { SC_UNO_DP_SUBTOTAL2, 0, cppu::UnoType<uno::Sequence<sal_Int16>>::get(), 0, 0 },
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ static uno::Reference<beans::XPropertySetInfo> aRef =
+ new SfxItemPropertySetInfo( aDPLevelMap_Impl );
+ return aRef;
+}
+
+void SAL_CALL ScDPLevel::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue )
+{
+ if ( aPropertyName == SC_UNO_DP_SHOWEMPTY )
+ bShowEmpty = lcl_GetBoolFromAny(aValue);
+ else if ( aPropertyName == SC_UNO_DP_REPEATITEMLABELS )
+ bRepeatItemLabels = lcl_GetBoolFromAny(aValue);
+ else if ( aPropertyName == SC_UNO_DP_SUBTOTAL )
+ {
+ uno::Sequence<sheet::GeneralFunction> aSeq;
+ aValue >>= aSeq;
+ aSubTotals.realloc(aSeq.getLength());
+ std::transform(std::cbegin(aSeq), std::cend(aSeq), aSubTotals.getArray(),
+ [](const sheet::GeneralFunction& rFunc) -> sal_Int16 {
+ return static_cast<sal_Int16>(rFunc); });
+ }
+ else if ( aPropertyName == SC_UNO_DP_SUBTOTAL2 )
+ aValue >>= aSubTotals;
+ else if ( aPropertyName == SC_UNO_DP_SORTING )
+ aValue >>= aSortInfo;
+ else if ( aPropertyName == SC_UNO_DP_AUTOSHOW )
+ aValue >>= aAutoShowInfo;
+ else if ( aPropertyName == SC_UNO_DP_LAYOUT )
+ aValue >>= aLayoutInfo;
+ else
+ {
+ OSL_FAIL("unknown property");
+ }
+}
+
+uno::Any SAL_CALL ScDPLevel::getPropertyValue( const OUString& aPropertyName )
+{
+ uno::Any aRet;
+ if ( aPropertyName == SC_UNO_DP_SHOWEMPTY )
+ aRet <<= bShowEmpty;
+ else if ( aPropertyName == SC_UNO_DP_REPEATITEMLABELS )
+ aRet <<= bRepeatItemLabels;
+ else if ( aPropertyName == SC_UNO_DP_SUBTOTAL )
+ {
+ const uno::Sequence<sal_Int16> aSeq = getSubTotals();
+ uno::Sequence<sheet::GeneralFunction> aNewSeq(aSeq.getLength());
+ std::transform(aSeq.begin(), aSeq.end(), aNewSeq.getArray(),
+ [](const sal_Int16 nFunc) -> sheet::GeneralFunction {
+ if (nFunc == sheet::GeneralFunction2::MEDIAN)
+ return sheet::GeneralFunction_NONE;
+ return static_cast<sheet::GeneralFunction>(nFunc);
+ });
+
+ aRet <<= aNewSeq;
+ }
+ else if ( aPropertyName == SC_UNO_DP_SUBTOTAL2 )
+ {
+ uno::Sequence<sal_Int16> aSeq = getSubTotals(); //TODO: avoid extra copy?
+ aRet <<= aSeq;
+ }
+ else if ( aPropertyName == SC_UNO_DP_SORTING )
+ aRet <<= aSortInfo;
+ else if ( aPropertyName == SC_UNO_DP_AUTOSHOW )
+ aRet <<= aAutoShowInfo;
+ else if ( aPropertyName == SC_UNO_DP_LAYOUT )
+ aRet <<= aLayoutInfo;
+ else if (aPropertyName == SC_UNO_DP_LAYOUTNAME)
+ {
+ // read only property
+ tools::Long nSrcDim = pSource->GetSourceDim(nDim);
+ ScDPDimension* pDim = pSource->GetDimensionsObject()->getByIndex(nSrcDim);
+ if (!pDim)
+ return aRet;
+
+ const std::optional<OUString> & pLayoutName = pDim->GetLayoutName();
+ if (!pLayoutName)
+ return aRet;
+
+ aRet <<= *pLayoutName;
+ }
+ else
+ {
+ OSL_FAIL("unknown property");
+ }
+ return aRet;
+}
+
+SC_IMPL_DUMMY_PROPERTY_LISTENER( ScDPLevel )
+
+ScDPMembers::ScDPMembers( ScDPSource* pSrc, sal_Int32 nD, sal_Int32 nH, sal_Int32 nL ) :
+ pSource( pSrc ),
+ nDim( nD ),
+ nHier( nH ),
+ nLev( nL )
+{
+ //TODO: hold pSource
+
+ tools::Long nSrcDim = pSource->GetSourceDim( nDim );
+ if ( pSource->IsDataLayoutDimension(nSrcDim) )
+ nMbrCount = pSource->GetDataDimensionCount();
+ else if ( nHier != SC_DAPI_HIERARCHY_FLAT && pSource->IsDateDimension( nSrcDim ) )
+ {
+ nMbrCount = 0;
+ if ( nHier == SC_DAPI_HIERARCHY_QUARTER )
+ {
+ switch (nLev)
+ {
+ case SC_DAPI_LEVEL_YEAR:
+ {
+ const ScDPItemData* pLastNumData = nullptr;
+ for ( SCROW n = 0; n < static_cast<SCROW>(pSource->GetData()->GetColumnEntries(nDim).size()); n-- )
+ {
+ const ScDPItemData* pData = GetSrcItemDataByIndex( n );
+ if ( pData && pData->HasStringData() )
+ break;
+ else
+ pLastNumData = pData;
+ }
+
+ if ( pLastNumData )
+ {
+ const ScDPItemData* pFirstData = GetSrcItemDataByIndex( 0 );
+ double fFirstVal = pFirstData->GetValue();
+ double fLastVal = pLastNumData->GetValue();
+
+ tools::Long nFirstYear = pSource->GetData()->GetDatePart(
+ static_cast<tools::Long>(::rtl::math::approxFloor( fFirstVal )),
+ nHier, nLev );
+ tools::Long nLastYear = pSource->GetData()->GetDatePart(
+ static_cast<tools::Long>(::rtl::math::approxFloor( fLastVal )),
+ nHier, nLev );
+
+ nMbrCount = nLastYear + 1 - nFirstYear;
+ }
+ else
+ nMbrCount = 0; // no values
+ }
+ break;
+ case SC_DAPI_LEVEL_QUARTER: nMbrCount = 4; break;
+ case SC_DAPI_LEVEL_MONTH: nMbrCount = 12; break;
+ case SC_DAPI_LEVEL_DAY: nMbrCount = 31; break;
+ default:
+ OSL_FAIL( "ScDPMembers::ScDPMembers: unexpected level" );
+ break;
+ }
+ }
+ else if ( nHier == SC_DAPI_HIERARCHY_WEEK )
+ {
+ switch (nLev)
+ {
+ case SC_DAPI_LEVEL_YEAR: nMbrCount = 1; break; //TODO: get years from source
+ case SC_DAPI_LEVEL_WEEK: nMbrCount = 53; break;
+ case SC_DAPI_LEVEL_WEEKDAY: nMbrCount = 7; break;
+ default:
+ OSL_FAIL( "ScDPMembers::ScDPMembers: unexpected level" );
+ break;
+ }
+ }
+ }
+ else
+ nMbrCount = pSource->GetData()->GetMembersCount( nSrcDim );
+}
+
+ScDPMembers::~ScDPMembers()
+{
+}
+
+// XNameAccess implementation using getCount/getByIndex
+
+sal_Int32 ScDPMembers::GetIndexFromName( const OUString& rName ) const
+{
+ if ( aHashMap.empty() )
+ {
+ // store the index for each name
+
+ sal_Int32 nCount = getCount();
+ for (sal_Int32 i=0; i<nCount; i++)
+ aHashMap[ getByIndex(i)->getName() ] = i;
+ }
+
+ ScDPMembersHashMap::const_iterator aIter = aHashMap.find( rName );
+ if ( aIter != aHashMap.end() )
+ return aIter->second; // found index
+ else
+ return -1; // not found
+}
+
+uno::Any SAL_CALL ScDPMembers::getByName( const OUString& aName )
+{
+ sal_Int32 nIndex = GetIndexFromName( aName );
+ if ( nIndex >= 0 )
+ {
+ uno::Reference<container::XNamed> xNamed = getByIndex(nIndex);
+ uno::Any aRet;
+ aRet <<= xNamed;
+ return aRet;
+ }
+
+ throw container::NoSuchElementException();
+}
+
+uno::Sequence<OUString> SAL_CALL ScDPMembers::getElementNames()
+{
+ return getElementNames( false );
+}
+
+sal_Bool SAL_CALL ScDPMembers::hasByName( const OUString& aName )
+{
+ return ( GetIndexFromName( aName ) >= 0 );
+}
+
+uno::Type SAL_CALL ScDPMembers::getElementType()
+{
+ return cppu::UnoType<container::XNamed>::get();
+}
+
+sal_Bool SAL_CALL ScDPMembers::hasElements()
+{
+ return ( getCount() > 0 );
+}
+
+// end of XNameAccess implementation
+
+// XMembersAccess implementation
+
+uno::Sequence<OUString> SAL_CALL ScDPMembers::getLocaleIndependentElementNames()
+{
+ return getElementNames( true );
+}
+
+// end of XMembersAccess implementation
+
+uno::Sequence<OUString> ScDPMembers::getElementNames( bool bLocaleIndependent ) const
+{
+ // Return list of names in sorted order,
+ // so it's displayed in that order in the field options dialog.
+ // Sorting is done at the level object (parent of this).
+
+ ScDPLevel* pLevel = pSource->GetDimensionsObject()->getByIndex(nDim)->
+ GetHierarchiesObject()->getByIndex(nHier)->GetLevelsObject()->getByIndex(nLev);
+ pLevel->EvaluateSortOrder();
+ const std::vector<sal_Int32>& rGlobalOrder = pLevel->GetGlobalOrder();
+ bool bSort = !rGlobalOrder.empty();
+
+ tools::Long nCount = getCount();
+ uno::Sequence<OUString> aSeq(nCount);
+ OUString* pArr = aSeq.getArray();
+ for (tools::Long i=0; i<nCount; i++)
+ pArr[i] = getByIndex(bSort ? rGlobalOrder[i] : i)->GetNameStr( bLocaleIndependent);
+ return aSeq;
+}
+
+sal_Int32 ScDPMembers::getMinMembers() const
+{
+ // used in lcl_CountMinMembers
+
+ sal_Int32 nVisCount = 0;
+ if (!maMembers.empty())
+ {
+ nVisCount = std::count_if(maMembers.begin(), maMembers.end(), [](const rtl::Reference<ScDPMember>& pMbr) {
+ // count only visible with details (default is true for both)
+ return !pMbr || (pMbr->isVisible() && pMbr->getShowDetails()); });
+ }
+ else
+ nVisCount = nMbrCount; // default for all
+
+ return nVisCount;
+}
+
+ScDPMember* ScDPMembers::getByIndex(sal_Int32 nIndex) const
+{
+ // result of GetColumnEntries must not change between ScDPMembers ctor
+ // and all calls to getByIndex
+
+ if ( nIndex >= 0 && nIndex < nMbrCount )
+ {
+ if (maMembers.empty())
+ maMembers.resize(nMbrCount);
+
+ if (!maMembers[nIndex])
+ {
+ rtl::Reference<ScDPMember> pNew;
+ sal_Int32 nSrcDim = pSource->GetSourceDim( nDim );
+ if ( pSource->IsDataLayoutDimension(nSrcDim) )
+ {
+ // empty name (never shown, not used for lookup)
+ pNew.set(new ScDPMember(pSource, nDim, nHier, nLev, 0));
+ }
+ else if ( nHier != SC_DAPI_HIERARCHY_FLAT && pSource->IsDateDimension( nSrcDim ) )
+ {
+ sal_Int32 nGroupBy = 0;
+ sal_Int32 nVal = 0;
+ OUString aName;
+
+ if ( nLev == SC_DAPI_LEVEL_YEAR ) // YEAR is in both hierarchies
+ {
+ //TODO: cache year range here!
+
+ double fFirstVal = pSource->GetData()->GetMemberByIndex( nSrcDim, 0 )->GetValue();
+ tools::Long nFirstYear = pSource->GetData()->GetDatePart(
+ static_cast<tools::Long>(::rtl::math::approxFloor( fFirstVal )),
+ nHier, nLev );
+
+ nVal = nFirstYear + nIndex;
+ }
+ else if ( nHier == SC_DAPI_HIERARCHY_WEEK && nLev == SC_DAPI_LEVEL_WEEKDAY )
+ {
+ nVal = nIndex; // DayOfWeek is 0-based
+ aName = ScGlobal::GetCalendar().getDisplayName(
+ css::i18n::CalendarDisplayIndex::DAY,
+ sal::static_int_cast<sal_Int16>(nVal), 0 );
+ }
+ else if ( nHier == SC_DAPI_HIERARCHY_QUARTER && nLev == SC_DAPI_LEVEL_MONTH )
+ {
+ nVal = nIndex; // Month is 0-based
+ aName = ScGlobal::GetCalendar().getDisplayName(
+ css::i18n::CalendarDisplayIndex::MONTH,
+ sal::static_int_cast<sal_Int16>(nVal), 0 );
+ }
+ else
+ nVal = nIndex + 1; // Quarter, Day, Week are 1-based
+
+ switch (nLev)
+ {
+ case SC_DAPI_LEVEL_YEAR:
+ nGroupBy = sheet::DataPilotFieldGroupBy::YEARS;
+ break;
+ case SC_DAPI_LEVEL_QUARTER:
+ case SC_DAPI_LEVEL_WEEK:
+ nGroupBy = sheet::DataPilotFieldGroupBy::QUARTERS;
+ break;
+ case SC_DAPI_LEVEL_MONTH:
+ case SC_DAPI_LEVEL_WEEKDAY:
+ nGroupBy = sheet::DataPilotFieldGroupBy::MONTHS;
+ break;
+ case SC_DAPI_LEVEL_DAY:
+ nGroupBy = sheet::DataPilotFieldGroupBy::DAYS;
+ break;
+ default:
+ ;
+ }
+ if (aName.isEmpty())
+ aName = OUString::number(nVal);
+
+ ScDPItemData aData(nGroupBy, nVal);
+ SCROW nId = pSource->GetCache()->GetIdByItemData(nDim, aData);
+ pNew.set(new ScDPMember(pSource, nDim, nHier, nLev, nId));
+ }
+ else
+ {
+ const std::vector<SCROW>& memberIndexs = pSource->GetData()->GetColumnEntries(nSrcDim);
+ pNew.set(new ScDPMember(pSource, nDim, nHier, nLev, memberIndexs[nIndex]));
+ }
+ maMembers[nIndex] = pNew;
+ }
+
+ return maMembers[nIndex].get();
+ }
+
+ return nullptr; //TODO: exception?
+}
+
+ScDPMember::ScDPMember(
+ ScDPSource* pSrc, sal_Int32 nD, sal_Int32 nH, sal_Int32 nL, SCROW nIndex) :
+ pSource( pSrc ),
+ nDim( nD ),
+ nHier( nH ),
+ nLev( nL ),
+ mnDataId( nIndex ),
+ nPosition( -1 ),
+ bVisible( true ),
+ bShowDet( true )
+{
+ //TODO: hold pSource
+}
+
+ScDPMember::~ScDPMember()
+{
+ //TODO: release pSource
+}
+
+bool ScDPMember::IsNamedItem(SCROW nIndex) const
+{
+ sal_Int32 nSrcDim = pSource->GetSourceDim( nDim );
+ if ( nHier != SC_DAPI_HIERARCHY_FLAT && pSource->IsDateDimension( nSrcDim ) )
+ {
+ const ScDPItemData* pData = pSource->GetCache()->GetItemDataById(nDim, nIndex);
+ if (pData->IsValue())
+ {
+ tools::Long nComp = pSource->GetData()->GetDatePart(
+ static_cast<tools::Long>(::rtl::math::approxFloor( pData->GetValue() )),
+ nHier, nLev );
+ // fValue is converted from integer, so simple comparison works
+ const ScDPItemData* pData2 = GetItemData();
+ return pData2 && nComp == pData2->GetValue();
+ }
+ }
+
+ return nIndex == mnDataId;
+}
+
+sal_Int32 ScDPMember::Compare( const ScDPMember& rOther ) const
+{
+ if ( nPosition >= 0 )
+ {
+ if ( rOther.nPosition >= 0 )
+ {
+ OSL_ENSURE( nPosition != rOther.nPosition, "same position for two members" );
+ return ( nPosition < rOther.nPosition ) ? -1 : 1;
+ }
+ else
+ {
+ // only this has a position - members with specified positions come before those without
+ return -1;
+ }
+ }
+ else if ( rOther.nPosition >= 0 )
+ {
+ // only rOther has a position
+ return 1;
+ }
+
+ // no positions set - compare names
+ return pSource->GetData()->Compare( pSource->GetSourceDim(nDim),mnDataId,rOther.GetItemDataId());
+}
+
+ScDPItemData ScDPMember::FillItemData() const
+{
+ //TODO: handle date hierarchy...
+
+ const ScDPItemData* pData = GetItemData();
+ return (pData ? *pData : ScDPItemData());
+}
+
+const std::optional<OUString> & ScDPMember::GetLayoutName() const
+{
+ return mpLayoutName;
+}
+
+OUString ScDPMember::GetNameStr( bool bLocaleIndependent ) const
+{
+ const ScDPItemData* pData = GetItemData();
+ if (pData)
+ return pSource->GetData()->GetFormattedString(nDim, *pData, bLocaleIndependent);
+ return OUString();
+}
+
+OUString SAL_CALL ScDPMember::getName()
+{
+ return GetNameStr( false );
+}
+
+void SAL_CALL ScDPMember::setName( const OUString& /* rNewName */ )
+{
+ OSL_FAIL("not implemented"); //TODO: exception?
+}
+
+// XPropertySet
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScDPMember::getPropertySetInfo()
+{
+ static const SfxItemPropertyMapEntry aDPMemberMap_Impl[] =
+ {
+ { SC_UNO_DP_ISVISIBLE, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNO_DP_POSITION, 0, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNO_DP_SHOWDETAILS, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNO_DP_LAYOUTNAME, 0, cppu::UnoType<OUString>::get(), 0, 0 },
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ static uno::Reference<beans::XPropertySetInfo> aRef =
+ new SfxItemPropertySetInfo( aDPMemberMap_Impl );
+ return aRef;
+}
+
+void SAL_CALL ScDPMember::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue )
+{
+ if ( aPropertyName == SC_UNO_DP_ISVISIBLE )
+ bVisible = lcl_GetBoolFromAny(aValue);
+ else if ( aPropertyName == SC_UNO_DP_SHOWDETAILS )
+ bShowDet = lcl_GetBoolFromAny(aValue);
+ else if ( aPropertyName == SC_UNO_DP_POSITION )
+ aValue >>= nPosition;
+ else if (aPropertyName == SC_UNO_DP_LAYOUTNAME)
+ {
+ OUString aName;
+ if (aValue >>= aName)
+ mpLayoutName = aName;
+ }
+ else
+ {
+ OSL_FAIL("unknown property");
+ }
+}
+
+uno::Any SAL_CALL ScDPMember::getPropertyValue( const OUString& aPropertyName )
+{
+ uno::Any aRet;
+ if ( aPropertyName == SC_UNO_DP_ISVISIBLE )
+ aRet <<= bVisible;
+ else if ( aPropertyName == SC_UNO_DP_SHOWDETAILS )
+ aRet <<= bShowDet;
+ else if ( aPropertyName == SC_UNO_DP_POSITION )
+ aRet <<= nPosition;
+ else if (aPropertyName == SC_UNO_DP_LAYOUTNAME)
+ aRet <<= mpLayoutName ? *mpLayoutName : OUString();
+ else
+ {
+ OSL_FAIL("unknown property");
+ }
+ return aRet;
+}
+
+SC_IMPL_DUMMY_PROPERTY_LISTENER( ScDPMember )
+
+const ScDPCache* ScDPSource::GetCache()
+{
+ OSL_ENSURE( GetData() , "empty ScDPTableData pointer");
+ return ( GetData()!=nullptr ) ? &GetData()->GetCacheTable().getCache() : nullptr ;
+}
+
+const ScDPItemData* ScDPMember::GetItemData() const
+{
+ const ScDPItemData* pData = pSource->GetItemDataById(nDim, mnDataId);
+ SAL_WARN_IF( !pData, "sc.core", "ScDPMember::GetItemData: what data? nDim " << nDim << ", mnDataId " << mnDataId);
+ return pData;
+}
+
+const ScDPItemData* ScDPSource::GetItemDataById(sal_Int32 nDim, sal_Int32 nId)
+{
+ return GetData()->GetMemberById(nDim, nId);
+}
+
+const ScDPItemData* ScDPMembers::GetSrcItemDataByIndex(SCROW nIndex)
+{
+ const std::vector< SCROW >& memberIds = pSource->GetData()->GetColumnEntries( nDim );
+ if ( nIndex < 0 || o3tl::make_unsigned(nIndex) >= memberIds.size() )
+ return nullptr;
+ SCROW nId = memberIds[ nIndex ];
+ return pSource->GetItemDataById( nDim, nId );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */