diff options
Diffstat (limited to 'sc/source/core/data/dpresfilter.cxx')
-rw-r--r-- | sc/source/core/data/dpresfilter.cxx | 267 |
1 files changed, 267 insertions, 0 deletions
diff --git a/sc/source/core/data/dpresfilter.cxx b/sc/source/core/data/dpresfilter.cxx new file mode 100644 index 000000000..4cef11b76 --- /dev/null +++ b/sc/source/core/data/dpresfilter.cxx @@ -0,0 +1,267 @@ +/* -*- 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/. + */ + +#include <dpresfilter.hxx> +#include <global.hxx> + +#include <unotools/charclass.hxx> +#include <sal/log.hxx> +#include <o3tl/hash_combine.hxx> + +#include <com/sun/star/sheet/DataPilotFieldFilter.hpp> +#include <com/sun/star/uno/Sequence.hxx> + +#include <limits> + +using namespace com::sun::star; + +ScDPResultFilter::ScDPResultFilter(const OUString& rDimName, bool bDataLayout) : + maDimName(rDimName), mbHasValue(false), mbDataLayout(bDataLayout) {} + +ScDPResultFilterContext::ScDPResultFilterContext() : + mnCol(0), mnRow(0) {} + +size_t ScDPResultTree::NamePairHash::operator() (const NamePairType& rPair) const +{ + std::size_t seed = 0; + o3tl::hash_combine(seed, rPair.first.hashCode()); + o3tl::hash_combine(seed, rPair.second.hashCode()); + return seed; +} + +#if DEBUG_PIVOT_TABLE +void ScDPResultTree::DimensionNode::dump(int nLevel) const +{ + string aIndent(nLevel*2, ' '); + MembersType::const_iterator it = maChildMembersValueNames.begin(), itEnd = maChildMembersValueNames.end(); + for (; it != itEnd; ++it) + { + cout << aIndent << "member: "; + const ScDPItemData& rVal = it->first; + if (rVal.IsValue()) + cout << rVal.GetValue(); + else + cout << rVal.GetString(); + cout << endl; + + it->second->dump(nLevel+1); + } +} +#endif + +ScDPResultTree::MemberNode::MemberNode() {} + +ScDPResultTree::MemberNode::~MemberNode() {} + +#if DEBUG_PIVOT_TABLE +void ScDPResultTree::MemberNode::dump(int nLevel) const +{ + string aIndent(nLevel*2, ' '); + for (const auto& rValue : maValues) + cout << aIndent << "value: " << rValue << endl; + + for (const auto& [rName, rxDim] : maChildDimensions) + { + cout << aIndent << "dimension: " << rName << endl; + rxDim->dump(nLevel+1); + } +} +#endif + +ScDPResultTree::ScDPResultTree() : mpRoot(new MemberNode) {} +ScDPResultTree::~ScDPResultTree() +{ +} + +void ScDPResultTree::add( + const std::vector<ScDPResultFilter>& rFilters, double fVal) +{ + // TODO: I'll work on the col / row to value node mapping later. + + const OUString* pDimName = nullptr; + const OUString* pMemName = nullptr; + MemberNode* pMemNode = mpRoot.get(); + + for (const ScDPResultFilter& filter : rFilters) + { + if (filter.mbDataLayout) + continue; + + if (maPrimaryDimName.isEmpty()) + maPrimaryDimName = filter.maDimName; + + // See if this dimension exists. + auto& rDims = pMemNode->maChildDimensions; + OUString aUpperName = ScGlobal::getCharClass().uppercase(filter.maDimName); + auto itDim = rDims.find(aUpperName); + if (itDim == rDims.end()) + { + // New dimension. Insert it. + auto r = rDims.emplace(aUpperName, DimensionNode()); + assert(r.second); + itDim = r.first; + } + + pDimName = &itDim->first; + + // Now, see if this dimension member exists. + DimensionNode& rDim = itDim->second; + MembersType& rMembersValueNames = rDim.maChildMembersValueNames; + aUpperName = ScGlobal::getCharClass().uppercase(filter.maValueName); + MembersType::iterator itMem = rMembersValueNames.find(aUpperName); + if (itMem == rMembersValueNames.end()) + { + // New member. Insert it. + auto pNode = std::make_shared<MemberNode>(); + std::pair<MembersType::iterator, bool> r = + rMembersValueNames.emplace(aUpperName, pNode); + + if (!r.second) + // Insertion failed! + return; + + itMem = r.first; + + // If the locale independent value string isn't any different it + // makes no sense to add it to the separate mapping. + if (!filter.maValue.isEmpty() && filter.maValue != filter.maValueName) + { + MembersType& rMembersValues = rDim.maChildMembersValues; + aUpperName = ScGlobal::getCharClass().uppercase(filter.maValue); + MembersType::iterator itMemVal = rMembersValues.find(aUpperName); + if (itMemVal == rMembersValues.end()) + { + // New member. Insert it. + std::pair<MembersType::iterator, bool> it = + rMembersValues.emplace(aUpperName, pNode); + // If insertion failed do not bail out anymore. + SAL_WARN_IF( !it.second, "sc.core", "ScDPResultTree::add - rMembersValues.insert failed"); + } + } + } + + pMemName = &itMem->first; + pMemNode = itMem->second.get(); + } + + if (pDimName && pMemName) + { + NamePairType aNames( + ScGlobal::getCharClass().uppercase(*pDimName), + ScGlobal::getCharClass().uppercase(*pMemName)); + + LeafValuesType::iterator it = maLeafValues.find(aNames); + if (it == maLeafValues.end()) + { + // This name pair doesn't exist. Associate a new value for it. + maLeafValues.emplace(aNames, fVal); + } + else + { + // This name pair already exists. Set the value to NaN. + it->second = std::numeric_limits<double>::quiet_NaN(); + } + } + + pMemNode->maValues.push_back(fVal); +} + +void ScDPResultTree::swap(ScDPResultTree& rOther) +{ + std::swap(maPrimaryDimName, rOther.maPrimaryDimName); + std::swap(mpRoot, rOther.mpRoot); + maLeafValues.swap(rOther.maLeafValues); +} + +bool ScDPResultTree::empty() const +{ + return mpRoot->maChildDimensions.empty(); +} + +void ScDPResultTree::clear() +{ + maPrimaryDimName.clear(); + mpRoot.reset( new MemberNode ); +} + +const ScDPResultTree::ValuesType* ScDPResultTree::getResults( + const uno::Sequence<sheet::DataPilotFieldFilter>& rFilters) const +{ + const MemberNode* pMember = mpRoot.get(); + for (const sheet::DataPilotFieldFilter& rFilter : rFilters) + { + auto itDim = pMember->maChildDimensions.find( + ScGlobal::getCharClass().uppercase(rFilter.FieldName)); + + if (itDim == pMember->maChildDimensions.end()) + // Specified dimension not found. + return nullptr; + + const DimensionNode& rDim = itDim->second; + MembersType::const_iterator itMem( rDim.maChildMembersValueNames.find( + ScGlobal::getCharClass().uppercase( rFilter.MatchValueName))); + + if (itMem == rDim.maChildMembersValueNames.end()) + { + // Specified member name not found, try locale independent value. + itMem = rDim.maChildMembersValues.find( ScGlobal::getCharClass().uppercase( rFilter.MatchValue)); + + if (itMem == rDim.maChildMembersValues.end()) + // Specified member not found. + return nullptr; + } + + pMember = itMem->second.get(); + } + + if (pMember->maValues.empty()) + { + // Descend into dimension member children while there is no result and + // exactly one dimension field with exactly one member item, for which + // no further constraint (filter) has to match. + const MemberNode* pFieldMember = pMember; + while (pFieldMember->maChildDimensions.size() == 1) + { + auto itDim( pFieldMember->maChildDimensions.begin()); + const DimensionNode& rDim = itDim->second; + if (rDim.maChildMembersValueNames.size() != 1) + break; // while + pFieldMember = rDim.maChildMembersValueNames.begin()->second.get(); + if (!pFieldMember->maValues.empty()) + return &pFieldMember->maValues; + } + } + + return &pMember->maValues; +} + +double ScDPResultTree::getLeafResult(const css::sheet::DataPilotFieldFilter& rFilter) const +{ + NamePairType aPair( + ScGlobal::getCharClass().uppercase(rFilter.FieldName), + ScGlobal::getCharClass().uppercase(rFilter.MatchValueName)); + + LeafValuesType::const_iterator it = maLeafValues.find(aPair); + if (it != maLeafValues.end()) + // Found! + return it->second; + + // Not found. Return an NaN. + return std::numeric_limits<double>::quiet_NaN(); +} + +#if DEBUG_PIVOT_TABLE +void ScDPResultTree::dump() const +{ + cout << "primary dimension name: " << maPrimaryDimName << endl; + mpRoot->dump(0); +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |