LibreOffice Module sc (master) 1
dpresfilter.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 */
9
10#include <dpresfilter.hxx>
11#include <global.hxx>
12
14#include <sal/log.hxx>
15#include <o3tl/hash_combine.hxx>
16
17#include <com/sun/star/sheet/DataPilotFieldFilter.hpp>
18#include <com/sun/star/uno/Sequence.hxx>
19
20#include <limits>
21#include <utility>
22
23using namespace com::sun::star;
24
25ScDPResultFilter::ScDPResultFilter(OUString aDimName, bool bDataLayout) :
26 maDimName(std::move(aDimName)), mbHasValue(false), mbDataLayout(bDataLayout) {}
27
29 mnCol(0), mnRow(0) {}
30
32{
33 std::size_t seed = 0;
34 o3tl::hash_combine(seed, rPair.first.hashCode());
35 o3tl::hash_combine(seed, rPair.second.hashCode());
36 return seed;
37}
38
39#if DEBUG_PIVOT_TABLE
40void ScDPResultTree::DimensionNode::dump(int nLevel) const
41{
42 string aIndent(nLevel*2, ' ');
43 MembersType::const_iterator it = maChildMembersValueNames.begin(), itEnd = maChildMembersValueNames.end();
44 for (; it != itEnd; ++it)
45 {
46 cout << aIndent << "member: ";
47 const ScDPItemData& rVal = it->first;
48 if (rVal.IsValue())
49 cout << rVal.GetValue();
50 else
51 cout << rVal.GetString();
52 cout << endl;
53
54 it->second->dump(nLevel+1);
55 }
56}
57#endif
58
60
62
63#if DEBUG_PIVOT_TABLE
64void ScDPResultTree::MemberNode::dump(int nLevel) const
65{
66 string aIndent(nLevel*2, ' ');
67 for (const auto& rValue : maValues)
68 cout << aIndent << "value: " << rValue << endl;
69
70 for (const auto& [rName, rxDim] : maChildDimensions)
71 {
72 cout << aIndent << "dimension: " << rName << endl;
73 rxDim->dump(nLevel+1);
74 }
75}
76#endif
77
80{
81}
82
84 const std::vector<ScDPResultFilter>& rFilters, double fVal)
85{
86 // TODO: I'll work on the col / row to value node mapping later.
87
88 const OUString* pDimName = nullptr;
89 const OUString* pMemName = nullptr;
90 MemberNode* pMemNode = mpRoot.get();
91
92 for (const ScDPResultFilter& filter : rFilters)
93 {
94 if (filter.mbDataLayout)
95 continue;
96
97 if (maPrimaryDimName.isEmpty())
98 maPrimaryDimName = filter.maDimName;
99
100 // See if this dimension exists.
101 auto& rDims = pMemNode->maChildDimensions;
102 OUString aUpperName = ScGlobal::getCharClass().uppercase(filter.maDimName);
103 auto itDim = rDims.find(aUpperName);
104 if (itDim == rDims.end())
105 {
106 // New dimension. Insert it.
107 auto r = rDims.emplace(aUpperName, DimensionNode());
108 assert(r.second);
109 itDim = r.first;
110 }
111
112 pDimName = &itDim->first;
113
114 // Now, see if this dimension member exists.
115 DimensionNode& rDim = itDim->second;
116 MembersType& rMembersValueNames = rDim.maChildMembersValueNames;
117 aUpperName = ScGlobal::getCharClass().uppercase(filter.maValueName);
118 MembersType::iterator itMem = rMembersValueNames.find(aUpperName);
119 if (itMem == rMembersValueNames.end())
120 {
121 // New member. Insert it.
122 auto pNode = std::make_shared<MemberNode>();
123 std::pair<MembersType::iterator, bool> r =
124 rMembersValueNames.emplace(aUpperName, pNode);
125
126 if (!r.second)
127 // Insertion failed!
128 return;
129
130 itMem = r.first;
131
132 // If the locale independent value string isn't any different it
133 // makes no sense to add it to the separate mapping.
134 if (!filter.maValue.isEmpty() && filter.maValue != filter.maValueName)
135 {
136 MembersType& rMembersValues = rDim.maChildMembersValues;
137 aUpperName = ScGlobal::getCharClass().uppercase(filter.maValue);
138 MembersType::iterator itMemVal = rMembersValues.find(aUpperName);
139 if (itMemVal == rMembersValues.end())
140 {
141 // New member. Insert it.
142 std::pair<MembersType::iterator, bool> it =
143 rMembersValues.emplace(aUpperName, pNode);
144 // If insertion failed do not bail out anymore.
145 SAL_WARN_IF( !it.second, "sc.core", "ScDPResultTree::add - rMembersValues.insert failed");
146 }
147 }
148 }
149
150 pMemName = &itMem->first;
151 pMemNode = itMem->second.get();
152 }
153
154 if (pDimName && pMemName)
155 {
156 NamePairType aNames(
157 ScGlobal::getCharClass().uppercase(*pDimName),
158 ScGlobal::getCharClass().uppercase(*pMemName));
159
160 LeafValuesType::iterator it = maLeafValues.find(aNames);
161 if (it == maLeafValues.end())
162 {
163 // This name pair doesn't exist. Associate a new value for it.
164 maLeafValues.emplace(aNames, fVal);
165 }
166 else
167 {
168 // This name pair already exists. Set the value to NaN.
169 it->second = std::numeric_limits<double>::quiet_NaN();
170 }
171 }
172
173 pMemNode->maValues.push_back(fVal);
174}
175
177{
178 std::swap(maPrimaryDimName, rOther.maPrimaryDimName);
179 std::swap(mpRoot, rOther.mpRoot);
180 maLeafValues.swap(rOther.maLeafValues);
181}
182
184{
185 return mpRoot->maChildDimensions.empty();
186}
187
189{
190 maPrimaryDimName.clear();
191 mpRoot.reset( new MemberNode );
192}
193
195 const uno::Sequence<sheet::DataPilotFieldFilter>& rFilters) const
196{
197 const MemberNode* pMember = mpRoot.get();
198 for (const sheet::DataPilotFieldFilter& rFilter : rFilters)
199 {
200 auto itDim = pMember->maChildDimensions.find(
201 ScGlobal::getCharClass().uppercase(rFilter.FieldName));
202
203 if (itDim == pMember->maChildDimensions.end())
204 // Specified dimension not found.
205 return nullptr;
206
207 const DimensionNode& rDim = itDim->second;
208 MembersType::const_iterator itMem( rDim.maChildMembersValueNames.find(
209 ScGlobal::getCharClass().uppercase( rFilter.MatchValueName)));
210
211 if (itMem == rDim.maChildMembersValueNames.end())
212 {
213 // Specified member name not found, try locale independent value.
214 itMem = rDim.maChildMembersValues.find( ScGlobal::getCharClass().uppercase( rFilter.MatchValue));
215
216 if (itMem == rDim.maChildMembersValues.end())
217 // Specified member not found.
218 return nullptr;
219 }
220
221 pMember = itMem->second.get();
222 }
223
224 if (pMember->maValues.empty())
225 {
226 // Descend into dimension member children while there is no result and
227 // exactly one dimension field with exactly one member item, for which
228 // no further constraint (filter) has to match.
229 const MemberNode* pFieldMember = pMember;
230 while (pFieldMember->maChildDimensions.size() == 1)
231 {
232 auto itDim( pFieldMember->maChildDimensions.begin());
233 const DimensionNode& rDim = itDim->second;
234 if (rDim.maChildMembersValueNames.size() != 1)
235 break; // while
236 pFieldMember = rDim.maChildMembersValueNames.begin()->second.get();
237 if (!pFieldMember->maValues.empty())
238 return &pFieldMember->maValues;
239 }
240 }
241
242 return &pMember->maValues;
243}
244
245double ScDPResultTree::getLeafResult(const css::sheet::DataPilotFieldFilter& rFilter) const
246{
247 NamePairType aPair(
248 ScGlobal::getCharClass().uppercase(rFilter.FieldName),
249 ScGlobal::getCharClass().uppercase(rFilter.MatchValueName));
250
251 LeafValuesType::const_iterator it = maLeafValues.find(aPair);
252 if (it != maLeafValues.end())
253 // Found!
254 return it->second;
255
256 // Not found. Return an NaN.
257 return std::numeric_limits<double>::quiet_NaN();
258}
259
260#if DEBUG_PIVOT_TABLE
261void ScDPResultTree::dump() const
262{
263 cout << "primary dimension name: " << maPrimaryDimName << endl;
264 mpRoot->dump(0);
265}
266#endif
267
268/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
OUString uppercase(const OUString &rStr, sal_Int32 nPos, sal_Int32 nCount) const
When assigning a string value, you can also assign an interned string whose life-cycle is managed by ...
Definition: dpitemdata.hxx:29
double GetValue() const
Definition: dpitemdata.cxx:347
bool IsValue() const
Definition: dpitemdata.cxx:322
OUString GetString() const
Definition: dpitemdata.cxx:327
This class maintains pivot table calculation result in a tree structure which represents the logical ...
Definition: dpresfilter.hxx:56
std::map< OUString, std::shared_ptr< MemberNode > > MembersType
Definition: dpresfilter.hxx:61
void add(const std::vector< ScDPResultFilter > &rFilter, double fVal)
Add a single value filter path.
Definition: dpresfilter.cxx:83
OUString maPrimaryDimName
Definition: dpresfilter.hxx:98
std::unique_ptr< MemberNode > mpRoot
Definition: dpresfilter.hxx:99
double getLeafResult(const css::sheet::DataPilotFieldFilter &rFilter) const
std::vector< double > ValuesType
Definition: dpresfilter.hxx:58
bool empty() const
LeafValuesType maLeafValues
Definition: dpresfilter.hxx:96
const ValuesType * getResults(const css::uno::Sequence< css::sheet::DataPilotFieldFilter > &rFilters) const
void swap(ScDPResultTree &rOther)
std::pair< OUString, OUString > NamePairType
Definition: dpresfilter.hxx:89
static SC_DLLPUBLIC const CharClass & getCharClass()
Definition: global.cxx:1064
sal_Int32 mnRow
sal_Int32 mnCol
#define SAL_WARN_IF(condition, area, stream)
std::enable_if_t<(sizeof(N)==4)> hash_combine(N &nSeed, T const *pValue, size_t nCount)
TOOLS_DLLPUBLIC SvStream & endl(SvStream &rStr)
ScDPResultFilter(OUString aDimName, bool bDataLayout)
Definition: dpresfilter.cxx:25
std::map< OUString, DimensionNode > maChildDimensions
Definition: dpresfilter.hxx:77
size_t operator()(const NamePairType &rPair) const
Definition: dpresfilter.cxx:31