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