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