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 using namespace std;
24 
25 ScDPResultFilter::ScDPResultFilter(const OUString& rDimName, bool bDataLayout) :
26  maDimName(rDimName), 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
40 void 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
64 void 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::getCharClassPtr()->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::getCharClassPtr()->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::getCharClassPtr()->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::getCharClassPtr()->uppercase(*pDimName),
158  ScGlobal::getCharClassPtr()->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 {
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::getCharClassPtr()->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::getCharClassPtr()->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::getCharClassPtr()->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 
245 double ScDPResultTree::getLeafResult(const css::sheet::DataPilotFieldFilter& rFilter) const
246 {
247  NamePairType aPair(
248  ScGlobal::getCharClassPtr()->uppercase(rFilter.FieldName),
249  ScGlobal::getCharClassPtr()->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
261 void 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: */
double getLeafResult(const css::sheet::DataPilotFieldFilter &rFilter) const
LeafValuesType maLeafValues
Definition: dpresfilter.hxx:96
#define EMPTY_OUSTRING
Definition: global.hxx:213
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:83
size_t operator()(const NamePairType &rPair) const
Definition: dpresfilter.cxx:31
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:25
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
static SC_DLLPUBLIC const CharClass * getCharClassPtr()
Definition: global.cxx:1012
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