Line data Source code
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 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #include "dptabres.hxx"
21 :
22 : #include "dptabdat.hxx"
23 : #include "dptabsrc.hxx"
24 : #include "global.hxx"
25 : #include "subtotal.hxx"
26 : #include "globstr.hrc"
27 : #include "dpitemdata.hxx"
28 :
29 : #include "document.hxx"
30 : #include "dpresfilter.hxx"
31 : #include "dputil.hxx"
32 :
33 : #include <osl/diagnose.h>
34 : #include <rtl/math.hxx>
35 : #include <rtl/strbuf.hxx>
36 :
37 : #include <math.h>
38 : #include <float.h>
39 : #include <algorithm>
40 : #include <unordered_map>
41 : #include <boost/checked_delete.hpp>
42 : #include <boost/scoped_ptr.hpp>
43 :
44 : #include <com/sun/star/sheet/DataResultFlags.hpp>
45 : #include <com/sun/star/sheet/MemberResultFlags.hpp>
46 : #include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
47 : #include <com/sun/star/sheet/DataPilotFieldReferenceType.hpp>
48 : #include <com/sun/star/sheet/DataPilotFieldReferenceItemType.hpp>
49 : #include <com/sun/star/sheet/DataPilotFieldShowItemsMode.hpp>
50 : #include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
51 : #include <com/sun/star/sheet/DataPilotFieldFilter.hpp>
52 :
53 : using namespace com::sun::star;
54 : using ::std::vector;
55 : using ::std::pair;
56 : using ::com::sun::star::uno::Sequence;
57 :
58 : namespace {
59 :
60 : sal_uInt16 nFuncStrIds[12] = // matching enum ScSubTotalFunc
61 : {
62 : 0, // SUBTOTAL_FUNC_NONE
63 : STR_FUN_TEXT_AVG, // SUBTOTAL_FUNC_AVE
64 : STR_FUN_TEXT_COUNT, // SUBTOTAL_FUNC_CNT
65 : STR_FUN_TEXT_COUNT, // SUBTOTAL_FUNC_CNT2
66 : STR_FUN_TEXT_MAX, // SUBTOTAL_FUNC_MAX
67 : STR_FUN_TEXT_MIN, // SUBTOTAL_FUNC_MIN
68 : STR_FUN_TEXT_PRODUCT, // SUBTOTAL_FUNC_PROD
69 : STR_FUN_TEXT_STDDEV, // SUBTOTAL_FUNC_STD
70 : STR_FUN_TEXT_STDDEV, // SUBTOTAL_FUNC_STDP
71 : STR_FUN_TEXT_SUM, // SUBTOTAL_FUNC_SUM
72 : STR_FUN_TEXT_VAR, // SUBTOTAL_FUNC_VAR
73 : STR_FUN_TEXT_VAR // SUBTOTAL_FUNC_VARP
74 : };
75 :
76 88 : bool lcl_SearchMember( const std::vector <ScDPResultMember *>& list, SCROW nOrder, SCROW& rIndex)
77 : {
78 88 : rIndex = list.size();
79 88 : bool bFound = false;
80 88 : SCROW nLo = 0;
81 88 : SCROW nHi = list.size() - 1;
82 : SCROW nIndex;
83 211 : while (nLo <= nHi)
84 : {
85 35 : nIndex = (nLo + nHi) / 2;
86 35 : if ( list[nIndex]->GetOrder() < nOrder )
87 32 : nLo = nIndex + 1;
88 : else
89 : {
90 3 : nHi = nIndex - 1;
91 3 : if ( list[nIndex]->GetOrder() == nOrder )
92 : {
93 0 : bFound = true;
94 0 : nLo = nIndex;
95 : }
96 : }
97 : }
98 88 : rIndex = nLo;
99 88 : return bFound;
100 : }
101 :
102 : class FilterStack
103 : {
104 : std::vector<ScDPResultFilter>& mrFilters;
105 : public:
106 1921 : FilterStack(std::vector<ScDPResultFilter>& rFilters) : mrFilters(rFilters) {}
107 :
108 420 : void pushDimName(const OUString& rName, bool bDataLayout)
109 : {
110 420 : mrFilters.push_back(ScDPResultFilter(rName, bDataLayout));
111 420 : }
112 :
113 1501 : void pushDimValue(const OUString& rValue)
114 : {
115 1501 : ScDPResultFilter& rFilter = mrFilters.back();
116 1501 : rFilter.maValue = rValue;
117 1501 : rFilter.mbHasValue = true;
118 1501 : }
119 :
120 1921 : ~FilterStack()
121 : {
122 1921 : ScDPResultFilter& rFilter = mrFilters.back();
123 1921 : if (rFilter.mbHasValue)
124 1501 : rFilter.mbHasValue = false;
125 : else
126 420 : mrFilters.pop_back();
127 1921 : }
128 : };
129 :
130 : }
131 :
132 : // function objects for sorting of the column and row members:
133 :
134 : class ScDPRowMembersOrder
135 : {
136 : ScDPResultDimension& rDimension;
137 : long nMeasure;
138 : bool bAscending;
139 :
140 : public:
141 0 : ScDPRowMembersOrder( ScDPResultDimension& rDim, long nM, bool bAsc ) :
142 : rDimension(rDim),
143 : nMeasure(nM),
144 0 : bAscending(bAsc)
145 0 : {}
146 0 : ~ScDPRowMembersOrder() {}
147 :
148 : bool operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const;
149 : };
150 :
151 : class ScDPColMembersOrder
152 : {
153 : ScDPDataDimension& rDimension;
154 : long nMeasure;
155 : bool bAscending;
156 :
157 : public:
158 0 : ScDPColMembersOrder( ScDPDataDimension& rDim, long nM, bool bAsc ) :
159 : rDimension(rDim),
160 : nMeasure(nM),
161 0 : bAscending(bAsc)
162 0 : {}
163 0 : ~ScDPColMembersOrder() {}
164 :
165 : bool operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const;
166 : };
167 :
168 0 : static bool lcl_IsLess( const ScDPDataMember* pDataMember1, const ScDPDataMember* pDataMember2, long nMeasure, bool bAscending )
169 : {
170 : // members can be NULL if used for rows
171 :
172 0 : ScDPSubTotalState aEmptyState;
173 0 : const ScDPAggData* pAgg1 = pDataMember1 ? pDataMember1->GetConstAggData( nMeasure, aEmptyState ) : NULL;
174 0 : const ScDPAggData* pAgg2 = pDataMember2 ? pDataMember2->GetConstAggData( nMeasure, aEmptyState ) : NULL;
175 :
176 0 : bool bError1 = pAgg1 && pAgg1->HasError();
177 0 : bool bError2 = pAgg2 && pAgg2->HasError();
178 0 : if ( bError1 )
179 0 : return false; // errors are always sorted at the end
180 0 : else if ( bError2 )
181 0 : return true; // errors are always sorted at the end
182 : else
183 : {
184 0 : double fVal1 = ( pAgg1 && pAgg1->HasData() ) ? pAgg1->GetResult() : 0.0; // no data is sorted as 0
185 0 : double fVal2 = ( pAgg2 && pAgg2->HasData() ) ? pAgg2->GetResult() : 0.0;
186 :
187 : // compare values
188 : // don't have to check approxEqual, as this is the only sort criterion
189 :
190 0 : return bAscending ? ( fVal1 < fVal2 ) : ( fVal1 > fVal2 );
191 : }
192 : }
193 :
194 0 : static bool lcl_IsEqual( const ScDPDataMember* pDataMember1, const ScDPDataMember* pDataMember2, long nMeasure )
195 : {
196 : // members can be NULL if used for rows
197 :
198 0 : ScDPSubTotalState aEmptyState;
199 0 : const ScDPAggData* pAgg1 = pDataMember1 ? pDataMember1->GetConstAggData( nMeasure, aEmptyState ) : NULL;
200 0 : const ScDPAggData* pAgg2 = pDataMember2 ? pDataMember2->GetConstAggData( nMeasure, aEmptyState ) : NULL;
201 :
202 0 : bool bError1 = pAgg1 && pAgg1->HasError();
203 0 : bool bError2 = pAgg2 && pAgg2->HasError();
204 0 : if ( bError1 )
205 : {
206 0 : if ( bError2 )
207 0 : return true; // equal
208 : else
209 0 : return false;
210 : }
211 0 : else if ( bError2 )
212 0 : return false;
213 : else
214 : {
215 0 : double fVal1 = ( pAgg1 && pAgg1->HasData() ) ? pAgg1->GetResult() : 0.0; // no data is sorted as 0
216 0 : double fVal2 = ( pAgg2 && pAgg2->HasData() ) ? pAgg2->GetResult() : 0.0;
217 :
218 : // compare values
219 : // this is used to find equal data at the end of the AutoShow range, so approxEqual must be used
220 :
221 0 : return rtl::math::approxEqual( fVal1, fVal2 );
222 : }
223 : }
224 :
225 0 : bool ScDPRowMembersOrder::operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const
226 : {
227 0 : const ScDPResultMember* pMember1 = rDimension.GetMember(nIndex1);
228 0 : const ScDPResultMember* pMember2 = rDimension.GetMember(nIndex2);
229 :
230 : // make the hide item to the largest order.
231 0 : if ( !pMember1->IsVisible() || !pMember2->IsVisible() )
232 0 : return pMember1->IsVisible();
233 0 : const ScDPDataMember* pDataMember1 = pMember1->GetDataRoot() ;
234 0 : const ScDPDataMember* pDataMember2 = pMember2->GetDataRoot();
235 : // GetDataRoot can be NULL if there was no data.
236 : // IsVisible == false can happen after AutoShow.
237 0 : return lcl_IsLess( pDataMember1, pDataMember2, nMeasure, bAscending );
238 : }
239 :
240 0 : bool ScDPColMembersOrder::operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const
241 : {
242 0 : const ScDPDataMember* pDataMember1 = rDimension.GetMember(nIndex1);
243 0 : const ScDPDataMember* pDataMember2 = rDimension.GetMember(nIndex2);
244 0 : bool bHide1 = pDataMember1 && !pDataMember1->IsVisible();
245 0 : bool bHide2 = pDataMember2 && !pDataMember2->IsVisible();
246 0 : if ( bHide1 || bHide2 )
247 0 : return !bHide1;
248 0 : return lcl_IsLess( pDataMember1, pDataMember2, nMeasure, bAscending );
249 : }
250 :
251 879 : ScDPInitState::Member::Member(long nSrcIndex, SCROW nNameIndex) :
252 879 : mnSrcIndex(nSrcIndex), mnNameIndex(nNameIndex) {}
253 :
254 879 : void ScDPInitState::AddMember( long nSourceIndex, SCROW nMember )
255 : {
256 879 : maMembers.push_back(Member(nSourceIndex, nMember));
257 879 : }
258 :
259 879 : void ScDPInitState::RemoveMember()
260 : {
261 : OSL_ENSURE(!maMembers.empty(), "ScDPInitState::RemoveMember: Attempt to remmove member while empty.");
262 879 : if (!maMembers.empty())
263 879 : maMembers.pop_back();
264 879 : }
265 :
266 : namespace {
267 :
268 : #if DEBUG_PIVOT_TABLE
269 : void lcl_DumpRow(
270 : const OUString& rType, const OUString& rName, const ScDPAggData* pAggData,
271 : ScDocument* pDoc, ScAddress& rPos )
272 : {
273 : SCCOL nCol = rPos.Col();
274 : SCROW nRow = rPos.Row();
275 : SCTAB nTab = rPos.Tab();
276 : pDoc->SetString( nCol++, nRow, nTab, rType );
277 : pDoc->SetString( nCol++, nRow, nTab, rName );
278 : while ( pAggData )
279 : {
280 : pDoc->SetValue( nCol++, nRow, nTab, pAggData->GetResult() );
281 : pAggData = pAggData->GetExistingChild();
282 : }
283 : rPos.SetRow( nRow + 1 );
284 : }
285 :
286 : void lcl_Indent( ScDocument* pDoc, SCROW nStartRow, const ScAddress& rPos )
287 : {
288 : SCCOL nCol = rPos.Col();
289 : SCTAB nTab = rPos.Tab();
290 :
291 : OUString aString;
292 : for (SCROW nRow = nStartRow; nRow < rPos.Row(); nRow++)
293 : {
294 : aString = pDoc->GetString(nCol, nRow, nTab);
295 : if (!aString.isEmpty())
296 : {
297 : aString = " " + aString;
298 : pDoc->SetString( nCol, nRow, nTab, aString );
299 : }
300 : }
301 : }
302 : #endif
303 :
304 : }
305 :
306 91 : ScDPRunningTotalState::ScDPRunningTotalState( ScDPResultMember* pColRoot, ScDPResultMember* pRowRoot ) :
307 91 : pColResRoot(pColRoot), pRowResRoot(pRowRoot)
308 : {
309 : // These arrays should never be empty as the terminating value must be present at all times.
310 91 : maColVisible.push_back(-1);
311 91 : maColSorted.push_back(-1);
312 91 : maRowVisible.push_back(-1);
313 91 : maRowSorted.push_back(-1);
314 91 : }
315 :
316 1237 : void ScDPRunningTotalState::AddColIndex( long nVisible, long nSorted )
317 : {
318 1237 : maColVisible.back() = nVisible;
319 1237 : maColVisible.push_back(-1);
320 :
321 1237 : maColSorted.back() = nSorted;
322 1237 : maColSorted.push_back(-1);
323 1237 : }
324 :
325 399 : void ScDPRunningTotalState::AddRowIndex( long nVisible, long nSorted )
326 : {
327 399 : maRowVisible.back() = nVisible;
328 399 : maRowVisible.push_back(-1);
329 :
330 399 : maRowSorted.back() = nSorted;
331 399 : maRowSorted.push_back(-1);
332 399 : }
333 :
334 1237 : void ScDPRunningTotalState::RemoveColIndex()
335 : {
336 : OSL_ENSURE(!maColVisible.empty() && !maColSorted.empty(), "ScDPRunningTotalState::RemoveColIndex: array is already empty!");
337 1237 : if (maColVisible.size() >= 2)
338 : {
339 1237 : maColVisible.pop_back();
340 1237 : maColVisible.back() = -1;
341 : }
342 :
343 1237 : if (maColSorted.size() >= 2)
344 : {
345 1237 : maColSorted.pop_back();
346 1237 : maColSorted.back() = -1;
347 : }
348 1237 : }
349 :
350 399 : void ScDPRunningTotalState::RemoveRowIndex()
351 : {
352 : OSL_ENSURE(!maRowVisible.empty() && !maRowSorted.empty(), "ScDPRunningTotalState::RemoveRowIndex: array is already empty!");
353 399 : if (maRowVisible.size() >= 2)
354 : {
355 399 : maRowVisible.pop_back();
356 399 : maRowVisible.back() = -1;
357 : }
358 :
359 399 : if (maRowSorted.size() >= 2)
360 : {
361 399 : maRowSorted.pop_back();
362 399 : maRowSorted.back() = -1;
363 : }
364 399 : }
365 :
366 12 : ScDPRelativePos::ScDPRelativePos( long nBase, long nDir ) :
367 : nBasePos( nBase ),
368 12 : nDirection( nDir )
369 : {
370 12 : }
371 :
372 1969 : void ScDPAggData::Update( const ScDPValue& rNext, ScSubTotalFunc eFunc, const ScDPSubTotalState& rSubState )
373 : {
374 1969 : if (nCount<0) // error?
375 0 : return; // nothing more...
376 :
377 1969 : if (rNext.meType == ScDPValue::Empty)
378 4 : return;
379 :
380 1965 : if ( rSubState.eColForce != SUBTOTAL_FUNC_NONE && rSubState.eRowForce != SUBTOTAL_FUNC_NONE &&
381 0 : rSubState.eColForce != rSubState.eRowForce )
382 0 : return;
383 1965 : if ( rSubState.eColForce != SUBTOTAL_FUNC_NONE ) eFunc = rSubState.eColForce;
384 1965 : if ( rSubState.eRowForce != SUBTOTAL_FUNC_NONE ) eFunc = rSubState.eRowForce;
385 :
386 1965 : if ( eFunc == SUBTOTAL_FUNC_NONE )
387 0 : return;
388 :
389 1965 : if ( eFunc != SUBTOTAL_FUNC_CNT2 ) // CNT2 counts everything, incl. strings and errors
390 : {
391 1797 : if (rNext.meType == ScDPValue::Error)
392 : {
393 0 : nCount = -1; // -1 for error (not for CNT2)
394 0 : return;
395 : }
396 1797 : if (rNext.meType == ScDPValue::String)
397 0 : return; // ignore
398 : }
399 :
400 1965 : ++nCount; // for all functions
401 :
402 1965 : switch (eFunc)
403 : {
404 : case SUBTOTAL_FUNC_SUM:
405 : case SUBTOTAL_FUNC_AVE:
406 1797 : if ( !SubTotal::SafePlus( fVal, rNext.mfValue ) )
407 0 : nCount = -1; // -1 for error
408 1797 : break;
409 : case SUBTOTAL_FUNC_PROD:
410 0 : if ( nCount == 1 ) // copy first value (fVal is initialized to 0)
411 0 : fVal = rNext.mfValue;
412 0 : else if ( !SubTotal::SafeMult( fVal, rNext.mfValue ) )
413 0 : nCount = -1; // -1 for error
414 0 : break;
415 : case SUBTOTAL_FUNC_CNT:
416 : case SUBTOTAL_FUNC_CNT2:
417 : // nothing more than incrementing nCount
418 168 : break;
419 : case SUBTOTAL_FUNC_MAX:
420 0 : if ( nCount == 1 || rNext.mfValue > fVal )
421 0 : fVal = rNext.mfValue;
422 0 : break;
423 : case SUBTOTAL_FUNC_MIN:
424 0 : if ( nCount == 1 || rNext.mfValue < fVal )
425 0 : fVal = rNext.mfValue;
426 0 : break;
427 : case SUBTOTAL_FUNC_STD:
428 : case SUBTOTAL_FUNC_STDP:
429 : case SUBTOTAL_FUNC_VAR:
430 : case SUBTOTAL_FUNC_VARP:
431 : {
432 : // fAux is used to sum up squares
433 0 : if ( !SubTotal::SafePlus( fVal, rNext.mfValue ) )
434 0 : nCount = -1; // -1 for error
435 0 : double fAdd = rNext.mfValue;
436 0 : if ( !SubTotal::SafeMult( fAdd, rNext.mfValue ) ||
437 0 : !SubTotal::SafePlus( fAux, fAdd ) )
438 0 : nCount = -1; // -1 for error
439 : }
440 0 : break;
441 : default:
442 : OSL_FAIL("invalid function");
443 : }
444 : }
445 :
446 1974 : void ScDPAggData::Calculate( ScSubTotalFunc eFunc, const ScDPSubTotalState& rSubState )
447 : {
448 : // calculate the original result
449 : // (without reference value, used as the basis for reference value calculation)
450 :
451 : // called several times at the cross-section of several subtotals - don't calculate twice then
452 1974 : if ( IsCalculated() )
453 0 : return;
454 :
455 1974 : if ( rSubState.eColForce != SUBTOTAL_FUNC_NONE ) eFunc = rSubState.eColForce;
456 1974 : if ( rSubState.eRowForce != SUBTOTAL_FUNC_NONE ) eFunc = rSubState.eRowForce;
457 :
458 1974 : if ( eFunc == SUBTOTAL_FUNC_NONE ) // this happens when there is no data dimension
459 : {
460 82 : nCount = SC_DPAGG_RESULT_EMPTY; // make sure there's a valid state for HasData etc.
461 82 : return;
462 : }
463 :
464 : // check the error conditions for the selected function
465 :
466 1892 : bool bError = false;
467 1892 : switch (eFunc)
468 : {
469 : case SUBTOTAL_FUNC_SUM:
470 : case SUBTOTAL_FUNC_PROD:
471 : case SUBTOTAL_FUNC_CNT:
472 : case SUBTOTAL_FUNC_CNT2:
473 1892 : bError = ( nCount < 0 ); // only real errors
474 1892 : break;
475 :
476 : case SUBTOTAL_FUNC_AVE:
477 : case SUBTOTAL_FUNC_MAX:
478 : case SUBTOTAL_FUNC_MIN:
479 : case SUBTOTAL_FUNC_STDP:
480 : case SUBTOTAL_FUNC_VARP:
481 0 : bError = ( nCount <= 0 ); // no data is an error
482 0 : break;
483 :
484 : case SUBTOTAL_FUNC_STD:
485 : case SUBTOTAL_FUNC_VAR:
486 0 : bError = ( nCount < 2 ); // need at least 2 values
487 0 : break;
488 :
489 : default:
490 : OSL_FAIL("invalid function");
491 : }
492 :
493 : // calculate the selected function
494 :
495 1892 : double fResult = 0.0;
496 1892 : if ( !bError )
497 : {
498 1892 : switch (eFunc)
499 : {
500 : case SUBTOTAL_FUNC_MAX:
501 : case SUBTOTAL_FUNC_MIN:
502 : case SUBTOTAL_FUNC_SUM:
503 : case SUBTOTAL_FUNC_PROD:
504 : // different error conditions are handled above
505 1844 : fResult = fVal;
506 1844 : break;
507 :
508 : case SUBTOTAL_FUNC_CNT:
509 : case SUBTOTAL_FUNC_CNT2:
510 48 : fResult = nCount;
511 48 : break;
512 :
513 : case SUBTOTAL_FUNC_AVE:
514 0 : if ( nCount > 0 )
515 0 : fResult = fVal / (double) nCount;
516 0 : break;
517 :
518 : //TODO: use safe mul for fVal * fVal
519 :
520 : case SUBTOTAL_FUNC_STD:
521 0 : if ( nCount >= 2 )
522 0 : fResult = sqrt((fAux - fVal*fVal/(double)(nCount)) / (double)(nCount-1));
523 0 : break;
524 : case SUBTOTAL_FUNC_VAR:
525 0 : if ( nCount >= 2 )
526 0 : fResult = (fAux - fVal*fVal/(double)(nCount)) / (double)(nCount-1);
527 0 : break;
528 : case SUBTOTAL_FUNC_STDP:
529 0 : if ( nCount > 0 )
530 0 : fResult = sqrt((fAux - fVal*fVal/(double)(nCount)) / (double)nCount);
531 0 : break;
532 : case SUBTOTAL_FUNC_VARP:
533 0 : if ( nCount > 0 )
534 0 : fResult = (fAux - fVal*fVal/(double)(nCount)) / (double)nCount;
535 0 : break;
536 : default:
537 : OSL_FAIL("invalid function");
538 : }
539 : }
540 :
541 1892 : bool bEmpty = ( nCount == 0 ); // no data
542 :
543 : // store the result
544 : // Empty is checked first, so empty results are shown empty even for "average" etc.
545 : // If these results should be treated as errors in reference value calculations,
546 : // a separate state value (EMPTY_ERROR) is needed.
547 : // Now, for compatibility, empty "average" results are counted as 0.
548 :
549 1892 : if ( bEmpty )
550 905 : nCount = SC_DPAGG_RESULT_EMPTY;
551 987 : else if ( bError )
552 0 : nCount = SC_DPAGG_RESULT_ERROR;
553 : else
554 987 : nCount = SC_DPAGG_RESULT_VALID;
555 :
556 1892 : if ( bEmpty || bError )
557 905 : fResult = 0.0; // default, in case the state is later modified
558 :
559 1892 : fVal = fResult; // used directly from now on
560 1892 : fAux = 0.0; // used for running total or original result of reference value
561 : }
562 :
563 1989 : bool ScDPAggData::IsCalculated() const
564 : {
565 1989 : return ( nCount <= SC_DPAGG_RESULT_EMPTY );
566 : }
567 :
568 815 : double ScDPAggData::GetResult() const
569 : {
570 : OSL_ENSURE( IsCalculated(), "ScDPAggData not calculated" );
571 :
572 815 : return fVal; // use calculated value
573 : }
574 :
575 781 : bool ScDPAggData::HasError() const
576 : {
577 : OSL_ENSURE( IsCalculated(), "ScDPAggData not calculated" );
578 :
579 781 : return ( nCount == SC_DPAGG_RESULT_ERROR );
580 : }
581 :
582 1416 : bool ScDPAggData::HasData() const
583 : {
584 : OSL_ENSURE( IsCalculated(), "ScDPAggData not calculated" );
585 :
586 1416 : return ( nCount != SC_DPAGG_RESULT_EMPTY ); // values or error
587 : }
588 :
589 19 : void ScDPAggData::SetResult( double fNew )
590 : {
591 : OSL_ENSURE( IsCalculated(), "ScDPAggData not calculated" );
592 :
593 19 : fVal = fNew; // don't reset error flag
594 19 : }
595 :
596 0 : void ScDPAggData::SetError()
597 : {
598 : OSL_ENSURE( IsCalculated(), "ScDPAggData not calculated" );
599 :
600 0 : nCount = SC_DPAGG_RESULT_ERROR;
601 0 : }
602 :
603 20 : void ScDPAggData::SetEmpty( bool bSet )
604 : {
605 : OSL_ENSURE( IsCalculated(), "ScDPAggData not calculated" );
606 :
607 20 : if ( bSet )
608 6 : nCount = SC_DPAGG_RESULT_EMPTY;
609 : else
610 14 : nCount = SC_DPAGG_RESULT_VALID;
611 20 : }
612 :
613 19 : double ScDPAggData::GetAuxiliary() const
614 : {
615 : // after Calculate, fAux is used as auxiliary value for running totals and reference values
616 : OSL_ENSURE( IsCalculated(), "ScDPAggData not calculated" );
617 :
618 19 : return fAux;
619 : }
620 :
621 26 : void ScDPAggData::SetAuxiliary( double fNew )
622 : {
623 : // after Calculate, fAux is used as auxiliary value for running totals and reference values
624 : OSL_ENSURE( IsCalculated(), "ScDPAggData not calculated" );
625 :
626 26 : fAux = fNew;
627 26 : }
628 :
629 2081 : ScDPAggData* ScDPAggData::GetChild()
630 : {
631 2081 : if (!pChild)
632 1187 : pChild = new ScDPAggData;
633 2081 : return pChild;
634 : }
635 :
636 41 : void ScDPAggData::Reset()
637 : {
638 41 : fVal = 0.0;
639 41 : fAux = 0.0;
640 41 : nCount = SC_DPAGG_EMPTY;
641 41 : delete pChild;
642 41 : pChild = NULL;
643 41 : }
644 :
645 : #if DEBUG_PIVOT_TABLE
646 : void ScDPAggData::Dump(int nIndent) const
647 : {
648 : std::string aIndent(nIndent*2, ' ');
649 : std::cout << aIndent << "* ";
650 : if (IsCalculated())
651 : std::cout << GetResult();
652 : else
653 : std::cout << "not calculated";
654 :
655 : std::cout << " [val=" << fVal << "; aux=" << fAux << "; count=" << nCount << "]" << std::endl;
656 : }
657 : #endif
658 :
659 91 : ScDPRowTotals::ScDPRowTotals() :
660 91 : bIsInColRoot( false )
661 : {
662 91 : }
663 :
664 91 : ScDPRowTotals::~ScDPRowTotals()
665 : {
666 91 : }
667 :
668 15 : static ScDPAggData* lcl_GetChildTotal( ScDPAggData* pFirst, long nMeasure )
669 : {
670 : OSL_ENSURE( nMeasure >= 0, "GetColTotal: no measure" );
671 :
672 15 : ScDPAggData* pAgg = pFirst;
673 15 : long nSkip = nMeasure;
674 :
675 : // subtotal settings are ignored - column/row totals exist once per measure
676 :
677 15 : for ( long nPos=0; nPos<nSkip; nPos++ )
678 0 : pAgg = pAgg->GetChild(); // column total is constructed empty - children need to be created
679 :
680 15 : if ( !pAgg->IsCalculated() )
681 : {
682 : // for first use, simulate an empty calculation
683 3 : ScDPSubTotalState aEmptyState;
684 3 : pAgg->Calculate( SUBTOTAL_FUNC_SUM, aEmptyState );
685 : }
686 :
687 15 : return pAgg;
688 : }
689 :
690 5 : ScDPAggData* ScDPRowTotals::GetRowTotal( long nMeasure )
691 : {
692 5 : return lcl_GetChildTotal( &aRowTotal, nMeasure );
693 : }
694 :
695 5 : ScDPAggData* ScDPRowTotals::GetGrandTotal( long nMeasure )
696 : {
697 5 : return lcl_GetChildTotal( &aGrandTotal, nMeasure );
698 : }
699 :
700 113 : static ScSubTotalFunc lcl_GetForceFunc( const ScDPLevel* pLevel, long nFuncNo )
701 : {
702 113 : ScSubTotalFunc eRet = SUBTOTAL_FUNC_NONE;
703 113 : if ( pLevel )
704 : {
705 : //TODO: direct access via ScDPLevel
706 :
707 0 : uno::Sequence<sheet::GeneralFunction> aSeq = pLevel->getSubTotals();
708 0 : long nSequence = aSeq.getLength();
709 0 : if ( nSequence && aSeq[0] != sheet::GeneralFunction_AUTO )
710 : {
711 : // For manual subtotals, "automatic" is added as first function.
712 : // ScDPResultMember::GetSubTotalCount adds to the count, here NONE has to be
713 : // returned as the first function then.
714 :
715 0 : --nFuncNo; // keep NONE for first (check below), move the other entries
716 : }
717 :
718 0 : if ( nFuncNo >= 0 && nFuncNo < nSequence )
719 : {
720 0 : sheet::GeneralFunction eUser = aSeq.getConstArray()[nFuncNo];
721 0 : if (eUser != sheet::GeneralFunction_AUTO)
722 0 : eRet = ScDPUtil::toSubTotalFunc(eUser);
723 0 : }
724 : }
725 113 : return eRet;
726 : }
727 :
728 91 : ScDPResultData::ScDPResultData( ScDPSource& rSrc ) :
729 : mrSource(rSrc),
730 : bLateInit( false ),
731 : bDataAtCol( false ),
732 91 : bDataAtRow( false )
733 : {
734 91 : }
735 :
736 178 : ScDPResultData::~ScDPResultData()
737 : {
738 89 : std::for_each(maDimMembers.begin(), maDimMembers.end(), boost::checked_deleter<ResultMembers>());
739 89 : }
740 :
741 91 : void ScDPResultData::SetMeasureData(
742 : std::vector<ScSubTotalFunc>& rFunctions, std::vector<sheet::DataPilotFieldReference>& rRefs,
743 : std::vector<sal_uInt16>& rRefOrient, std::vector<OUString>& rNames )
744 : {
745 : // We need to have at least one measure data at all times.
746 :
747 91 : maMeasureFuncs.swap(rFunctions);
748 91 : if (maMeasureFuncs.empty())
749 11 : maMeasureFuncs.push_back(SUBTOTAL_FUNC_NONE);
750 :
751 91 : maMeasureRefs.swap(rRefs);
752 91 : if (maMeasureRefs.empty())
753 11 : maMeasureRefs.push_back(sheet::DataPilotFieldReference()); // default ctor is ok.
754 :
755 91 : maMeasureRefOrients.swap(rRefOrient);
756 91 : if (maMeasureRefOrients.empty())
757 11 : maMeasureRefOrients.push_back(sheet::DataPilotFieldOrientation_HIDDEN);
758 :
759 91 : maMeasureNames.swap(rNames);
760 91 : if (maMeasureNames.empty())
761 11 : maMeasureNames.push_back(ScGlobal::GetRscString(STR_EMPTYDATA));
762 91 : }
763 :
764 91 : void ScDPResultData::SetDataLayoutOrientation( sal_uInt16 nOrient )
765 : {
766 91 : bDataAtCol = ( nOrient == sheet::DataPilotFieldOrientation_COLUMN );
767 91 : bDataAtRow = ( nOrient == sheet::DataPilotFieldOrientation_ROW );
768 91 : }
769 :
770 91 : void ScDPResultData::SetLateInit( bool bSet )
771 : {
772 91 : bLateInit = bSet;
773 91 : }
774 :
775 1570 : long ScDPResultData::GetColStartMeasure() const
776 : {
777 1570 : if (maMeasureFuncs.size() == 1)
778 1450 : return 0;
779 :
780 120 : return bDataAtCol ? SC_DPMEASURE_ALL : SC_DPMEASURE_ANY;
781 : }
782 :
783 513 : long ScDPResultData::GetRowStartMeasure() const
784 : {
785 513 : if (maMeasureFuncs.size() == 1)
786 459 : return 0;
787 :
788 54 : return bDataAtRow ? SC_DPMEASURE_ALL : SC_DPMEASURE_ANY;
789 : }
790 :
791 4045 : ScSubTotalFunc ScDPResultData::GetMeasureFunction(long nMeasure) const
792 : {
793 : OSL_ENSURE((size_t) nMeasure < maMeasureFuncs.size(), "bumm");
794 4045 : return maMeasureFuncs[nMeasure];
795 : }
796 :
797 3726 : const sheet::DataPilotFieldReference& ScDPResultData::GetMeasureRefVal(long nMeasure) const
798 : {
799 : OSL_ENSURE((size_t) nMeasure < maMeasureRefs.size(), "bumm");
800 3726 : return maMeasureRefs[nMeasure];
801 : }
802 :
803 20 : sal_uInt16 ScDPResultData::GetMeasureRefOrient(long nMeasure) const
804 : {
805 : OSL_ENSURE((size_t) nMeasure < maMeasureRefOrients.size(), "bumm");
806 20 : return maMeasureRefOrients[nMeasure];
807 : }
808 :
809 214 : OUString ScDPResultData::GetMeasureString(long nMeasure, bool bForce, ScSubTotalFunc eForceFunc, bool& rbTotalResult) const
810 : {
811 : // with bForce==true, return function instead of "result" for single measure
812 : // with eForceFunc != SUBTOTAL_FUNC_NONE, always use eForceFunc
813 214 : rbTotalResult = false;
814 214 : if ( nMeasure < 0 || (maMeasureFuncs.size() == 1 && !bForce && eForceFunc == SUBTOTAL_FUNC_NONE) )
815 : {
816 : // for user-specified subtotal function with all measures,
817 : // display only function name
818 109 : if ( eForceFunc != SUBTOTAL_FUNC_NONE )
819 0 : return ScGlobal::GetRscString(nFuncStrIds[eForceFunc]);
820 :
821 109 : rbTotalResult = true;
822 109 : return ScGlobal::GetRscString(STR_TABLE_ERGEBNIS);
823 : }
824 : else
825 : {
826 : OSL_ENSURE((size_t) nMeasure < maMeasureFuncs.size(), "bumm");
827 105 : const ScDPDimension* pDataDim = mrSource.GetDataDimension(nMeasure);
828 105 : if (pDataDim)
829 : {
830 94 : const OUString* pLayoutName = pDataDim->GetLayoutName();
831 94 : if (pLayoutName)
832 0 : return *pLayoutName;
833 : }
834 :
835 : ScSubTotalFunc eFunc = ( eForceFunc == SUBTOTAL_FUNC_NONE ) ?
836 105 : GetMeasureFunction(nMeasure) : eForceFunc;
837 :
838 105 : return ScDPUtil::getDisplayedMeasureName(maMeasureNames[nMeasure], eFunc);
839 : }
840 : }
841 :
842 26 : OUString ScDPResultData::GetMeasureDimensionName(long nMeasure) const
843 : {
844 26 : if ( nMeasure < 0 )
845 : {
846 : OSL_FAIL("GetMeasureDimensionName: negative");
847 0 : return OUString("***");
848 : }
849 :
850 26 : return mrSource.GetDataDimName(nMeasure);
851 : }
852 :
853 255 : bool ScDPResultData::IsBaseForGroup( long nDim ) const
854 : {
855 255 : return mrSource.GetData()->IsBaseForGroup(nDim);
856 : }
857 :
858 322 : long ScDPResultData::GetGroupBase( long nGroupDim ) const
859 : {
860 322 : return mrSource.GetData()->GetGroupBase(nGroupDim);
861 : }
862 :
863 363 : bool ScDPResultData::IsNumOrDateGroup( long nDim ) const
864 : {
865 363 : return mrSource.GetData()->IsNumOrDateGroup(nDim);
866 : }
867 :
868 59 : bool ScDPResultData::IsInGroup( SCROW nGroupDataId, long nGroupIndex,
869 : const ScDPItemData& rBaseData, long nBaseIndex ) const
870 : {
871 59 : const ScDPItemData* pGroupData = mrSource.GetItemDataById(nGroupIndex , nGroupDataId);
872 59 : if ( pGroupData )
873 59 : return mrSource.GetData()->IsInGroup(*pGroupData, nGroupIndex, rBaseData, nBaseIndex);
874 : else
875 0 : return false;
876 : }
877 :
878 8 : bool ScDPResultData::HasCommonElement( SCROW nFirstDataId, long nFirstIndex,
879 : const ScDPItemData& rSecondData, long nSecondIndex ) const
880 : {
881 8 : const ScDPItemData* pFirstData = mrSource.GetItemDataById(nFirstIndex , nFirstDataId);
882 8 : if ( pFirstData )
883 8 : return mrSource.GetData()->HasCommonElement(*pFirstData, nFirstIndex, rSecondData, nSecondIndex);
884 : else
885 0 : return false;
886 : }
887 :
888 860 : ResultMembers* ScDPResultData::GetDimResultMembers(long nDim, ScDPDimension* pDim, ScDPLevel* pLevel) const
889 : {
890 860 : if (nDim < static_cast<long>(maDimMembers.size()) && maDimMembers[nDim])
891 696 : return maDimMembers[nDim];
892 :
893 164 : maDimMembers.resize(nDim+1, NULL);
894 :
895 164 : ResultMembers* pResultMembers = new ResultMembers();
896 : // global order is used to initialize aMembers, so it doesn't have to be looked at later
897 164 : const ScMemberSortOrder& rGlobalOrder = pLevel->GetGlobalOrder();
898 :
899 164 : ScDPMembers* pMembers = pLevel->GetMembersObject();
900 164 : long nMembCount = pMembers->getCount();
901 896 : for (long i = 0; i < nMembCount; ++i)
902 : {
903 732 : long nSorted = rGlobalOrder.empty() ? i : rGlobalOrder[i];
904 732 : ScDPMember* pMember = pMembers->getByIndex(nSorted);
905 732 : if (!pResultMembers->FindMember(pMember->GetItemDataId()))
906 : {
907 732 : ScDPParentDimData* pNew = new ScDPParentDimData(i, pDim, pLevel, pMember);
908 732 : pResultMembers->InsertMember(pNew);
909 : }
910 : }
911 :
912 164 : maDimMembers[nDim] = pResultMembers;
913 164 : return maDimMembers[nDim];
914 : }
915 :
916 685 : ScDPResultMember::ScDPResultMember(
917 : const ScDPResultData* pData, const ScDPParentDimData& rParentDimData, bool bForceSub ) :
918 : pResultData( pData ),
919 : aParentDimData( rParentDimData ),
920 : pChildDimension( NULL ),
921 : pDataRoot( NULL ),
922 : bHasElements( false ),
923 : bForceSubTotal( bForceSub ),
924 : bHasHiddenDetails( false ),
925 : bInitialized( false ),
926 : bAutoHidden( false ),
927 685 : nMemberStep( 1 )
928 : {
929 : // pParentLevel/pMemberDesc is 0 for root members
930 685 : }
931 :
932 182 : ScDPResultMember::ScDPResultMember(
933 : const ScDPResultData* pData, bool bForceSub ) :
934 : pResultData( pData ),
935 : pChildDimension( NULL ),
936 : pDataRoot( NULL ),
937 : bHasElements( false ),
938 : bForceSubTotal( bForceSub ),
939 : bHasHiddenDetails( false ),
940 : bInitialized( false ),
941 : bAutoHidden( false ),
942 182 : nMemberStep( 1 )
943 : {
944 182 : }
945 1696 : ScDPResultMember::~ScDPResultMember()
946 : {
947 848 : delete pChildDimension;
948 848 : delete pDataRoot;
949 848 : }
950 :
951 12 : OUString ScDPResultMember::GetName() const
952 : {
953 12 : const ScDPMember* pMemberDesc = GetDPMember();
954 12 : if (pMemberDesc)
955 12 : return pMemberDesc->GetNameStr();
956 : else
957 0 : return ScGlobal::GetRscString(STR_PIVOT_TOTAL); // root member
958 : }
959 :
960 1501 : OUString ScDPResultMember::GetDisplayName() const
961 : {
962 1501 : const ScDPMember* pDPMember = GetDPMember();
963 1501 : if (!pDPMember)
964 0 : return OUString();
965 :
966 1501 : ScDPItemData aItem;
967 1501 : pDPMember->FillItemData(aItem);
968 1501 : if (aParentDimData.mpParentDim)
969 : {
970 1501 : long nDim = aParentDimData.mpParentDim->GetDimension();
971 1501 : return pResultData->GetSource().GetData()->GetFormattedString(nDim, aItem);
972 : }
973 :
974 0 : return aItem.GetString();
975 : }
976 :
977 6007 : void ScDPResultMember::FillItemData( ScDPItemData& rData ) const
978 : {
979 6007 : const ScDPMember* pMemberDesc = GetDPMember();
980 6007 : if (pMemberDesc)
981 5888 : pMemberDesc->FillItemData( rData );
982 : else
983 119 : rData.SetString( ScGlobal::GetRscString(STR_PIVOT_TOTAL) ); // root member
984 6007 : }
985 :
986 3705 : bool ScDPResultMember::IsNamedItem( SCROW nIndex ) const
987 : {
988 : //TODO: store ScDPMember pointer instead of ScDPMember ???
989 3705 : const ScDPMember* pMemberDesc = GetDPMember();
990 3705 : if (pMemberDesc)
991 3705 : return pMemberDesc->IsNamedItem(nIndex);
992 0 : return false;
993 : }
994 :
995 821 : bool ScDPResultMember::IsValidEntry( const vector< SCROW >& aMembers ) const
996 : {
997 821 : if ( !IsValid() )
998 5 : return false;
999 :
1000 816 : const ScDPResultDimension* pChildDim = GetChildDimension();
1001 816 : if (pChildDim)
1002 : {
1003 139 : if (aMembers.size() < 2)
1004 0 : return false;
1005 :
1006 139 : vector<SCROW>::const_iterator itr = aMembers.begin();
1007 139 : vector<SCROW> aChildMembers(++itr, aMembers.end());
1008 139 : return pChildDim->IsValidEntry(aChildMembers);
1009 : }
1010 : else
1011 677 : return true;
1012 : }
1013 :
1014 198 : void ScDPResultMember::InitFrom( const vector<ScDPDimension*>& ppDim, const vector<ScDPLevel*>& ppLev,
1015 : size_t nPos, ScDPInitState& rInitState ,
1016 : bool bInitChild )
1017 : {
1018 : // with LateInit, initialize only those members that have data
1019 198 : if ( pResultData->IsLateInit() )
1020 174 : return;
1021 :
1022 24 : bInitialized = true;
1023 :
1024 24 : if (nPos >= ppDim.size())
1025 20 : return;
1026 :
1027 : // skip child dimension if details are not shown
1028 4 : if ( GetDPMember() && !GetDPMember()->getShowDetails() )
1029 : {
1030 : // Show DataLayout dimension
1031 0 : nMemberStep = 1;
1032 0 : while ( nPos < ppDim.size() )
1033 : {
1034 0 : if ( ppDim[nPos]->getIsDataLayoutDimension() )
1035 : {
1036 0 : if ( !pChildDimension )
1037 0 : pChildDimension = new ScDPResultDimension( pResultData );
1038 0 : pChildDimension->InitFrom( ppDim, ppLev, nPos, rInitState , false );
1039 0 : return;
1040 : }
1041 : else
1042 : { //find next dim
1043 0 : nPos ++;
1044 0 : nMemberStep ++;
1045 : }
1046 : }
1047 0 : bHasHiddenDetails = true; // only if there is a next dimension
1048 0 : return;
1049 : }
1050 :
1051 4 : if ( bInitChild )
1052 : {
1053 4 : pChildDimension = new ScDPResultDimension( pResultData );
1054 4 : pChildDimension->InitFrom(ppDim, ppLev, nPos, rInitState, true);
1055 : }
1056 : }
1057 :
1058 1845 : void ScDPResultMember::LateInitFrom(
1059 : LateInitParams& rParams, const vector<SCROW>& pItemData, size_t nPos, ScDPInitState& rInitState)
1060 : {
1061 : // without LateInit, everything has already been initialized
1062 1845 : if ( !pResultData->IsLateInit() )
1063 32 : return;
1064 :
1065 1813 : bInitialized = true;
1066 :
1067 1813 : if ( rParams.IsEnd( nPos ) /*nPos >= ppDim.size()*/)
1068 : // No next dimension. Bail out.
1069 1008 : return;
1070 :
1071 : // skip child dimension if details are not shown
1072 805 : if ( GetDPMember() && !GetDPMember()->getShowDetails() )
1073 : {
1074 : // Show DataLayout dimension
1075 0 : nMemberStep = 1;
1076 0 : while ( !rParams.IsEnd( nPos ) )
1077 : {
1078 0 : if ( rParams.GetDim( nPos )->getIsDataLayoutDimension() )
1079 : {
1080 0 : if ( !pChildDimension )
1081 0 : pChildDimension = new ScDPResultDimension( pResultData );
1082 :
1083 : // #i111462# reset InitChild flag only for this child dimension's LateInitFrom call,
1084 : // not for following members of parent dimensions
1085 0 : bool bWasInitChild = rParams.GetInitChild();
1086 0 : rParams.SetInitChild( false );
1087 0 : pChildDimension->LateInitFrom( rParams, pItemData, nPos, rInitState );
1088 0 : rParams.SetInitChild( bWasInitChild );
1089 0 : return;
1090 : }
1091 : else
1092 : { //find next dim
1093 0 : nPos ++;
1094 0 : nMemberStep ++;
1095 : }
1096 : }
1097 0 : bHasHiddenDetails = true; // only if there is a next dimension
1098 0 : return;
1099 : }
1100 :
1101 : // LateInitFrom is called several times...
1102 805 : if ( rParams.GetInitChild() )
1103 : {
1104 805 : if ( !pChildDimension )
1105 197 : pChildDimension = new ScDPResultDimension( pResultData );
1106 805 : pChildDimension->LateInitFrom( rParams, pItemData, nPos, rInitState );
1107 : }
1108 : }
1109 :
1110 2730 : bool ScDPResultMember::IsSubTotalInTitle(long nMeasure) const
1111 : {
1112 2730 : bool bRet = false;
1113 3626 : if ( pChildDimension && /*pParentLevel*/GetParentLevel() &&
1114 2957 : /*pParentLevel*/GetParentLevel()->IsOutlineLayout() && /*pParentLevel*/GetParentLevel()->IsSubtotalsAtTop() )
1115 : {
1116 : long nUserSubStart;
1117 0 : long nSubTotals = GetSubTotalCount( &nUserSubStart );
1118 0 : nSubTotals -= nUserSubStart; // visible count
1119 0 : if ( nSubTotals )
1120 : {
1121 0 : if ( nMeasure == SC_DPMEASURE_ALL )
1122 0 : nSubTotals *= pResultData->GetMeasureCount(); // number of subtotals that will be inserted
1123 :
1124 : // only a single subtotal row will be shown in the outline title row
1125 0 : if ( nSubTotals == 1 )
1126 0 : bRet = true;
1127 : }
1128 : }
1129 2730 : return bRet;
1130 : }
1131 :
1132 6113 : long ScDPResultMember::GetSize(long nMeasure) const
1133 : {
1134 6113 : if ( !IsVisible() )
1135 994 : return 0;
1136 5119 : const ScDPLevel* pParentLevel = GetParentLevel();
1137 5119 : long nExtraSpace = 0;
1138 5119 : if ( pParentLevel && pParentLevel->IsAddEmpty() )
1139 0 : ++nExtraSpace;
1140 :
1141 5119 : if ( pChildDimension )
1142 : {
1143 : // outline layout takes up an extra row for the title only if subtotals aren't shown in that row
1144 1303 : if ( pParentLevel && pParentLevel->IsOutlineLayout() && !IsSubTotalInTitle( nMeasure ) )
1145 0 : ++nExtraSpace;
1146 :
1147 1303 : long nSize = pChildDimension->GetSize(nMeasure);
1148 : long nUserSubStart;
1149 1303 : long nUserSubCount = GetSubTotalCount( &nUserSubStart );
1150 1303 : nUserSubCount -= nUserSubStart; // for output size, use visible count
1151 1303 : if ( nUserSubCount )
1152 : {
1153 644 : if ( nMeasure == SC_DPMEASURE_ALL )
1154 8 : nSize += pResultData->GetMeasureCount() * nUserSubCount;
1155 : else
1156 636 : nSize += nUserSubCount;
1157 : }
1158 1303 : return nSize + nExtraSpace;
1159 : }
1160 : else
1161 : {
1162 3816 : if ( nMeasure == SC_DPMEASURE_ALL )
1163 0 : return pResultData->GetMeasureCount() + nExtraSpace;
1164 : else
1165 3816 : return 1 + nExtraSpace;
1166 : }
1167 : }
1168 :
1169 12525 : bool ScDPResultMember::IsVisible() const
1170 : {
1171 12525 : if (!bInitialized)
1172 1445 : return false;
1173 :
1174 11080 : if (!IsValid())
1175 9 : return false;
1176 :
1177 11071 : if (bHasElements)
1178 11071 : return true;
1179 :
1180 : // not initialized -> shouldn't be there at all
1181 : // (allocated only to preserve ordering)
1182 0 : const ScDPLevel* pParentLevel = GetParentLevel();
1183 :
1184 0 : return (pParentLevel && pParentLevel->getShowEmpty());
1185 : }
1186 :
1187 17300 : bool ScDPResultMember::IsValid() const
1188 : {
1189 : // non-Valid members are left out of calculation
1190 :
1191 : // was member set no invisible at the DataPilotSource?
1192 17300 : const ScDPMember* pMemberDesc = GetDPMember();
1193 17300 : if ( pMemberDesc && !pMemberDesc->isVisible() )
1194 15 : return false;
1195 :
1196 17285 : if ( bAutoHidden )
1197 0 : return false;
1198 :
1199 17285 : return true;
1200 : }
1201 :
1202 10572 : long ScDPResultMember::GetSubTotalCount( long* pUserSubStart ) const
1203 : {
1204 10572 : if ( pUserSubStart )
1205 4033 : *pUserSubStart = 0; // default
1206 :
1207 10572 : const ScDPLevel* pParentLevel = GetParentLevel();
1208 :
1209 10572 : if ( bForceSubTotal ) // set if needed for root members
1210 2125 : return 1; // grand total is always "automatic"
1211 8447 : else if ( pParentLevel )
1212 : {
1213 : //TODO: direct access via ScDPLevel
1214 :
1215 7573 : uno::Sequence<sheet::GeneralFunction> aSeq = pParentLevel->getSubTotals();
1216 7573 : long nSequence = aSeq.getLength();
1217 7573 : if ( nSequence && aSeq[0] != sheet::GeneralFunction_AUTO )
1218 : {
1219 : // For manual subtotals, always add "automatic" as first function
1220 : // (used for calculation, but not for display, needed for sorting, see lcl_GetForceFunc)
1221 :
1222 0 : ++nSequence;
1223 0 : if ( pUserSubStart )
1224 0 : *pUserSubStart = 1; // visible subtotals start at 1
1225 : }
1226 7573 : return nSequence;
1227 : }
1228 : else
1229 874 : return 0;
1230 : }
1231 :
1232 1307 : void ScDPResultMember::ProcessData( const vector< SCROW >& aChildMembers, const ScDPResultDimension* pDataDim,
1233 : const vector< SCROW >& aDataMembers, const vector<ScDPValue>& aValues )
1234 : {
1235 1307 : SetHasElements();
1236 :
1237 1307 : if (pChildDimension)
1238 575 : pChildDimension->ProcessData( aChildMembers, pDataDim, aDataMembers, aValues );
1239 :
1240 1307 : if ( !pDataRoot )
1241 : {
1242 690 : pDataRoot = new ScDPDataMember( pResultData, NULL );
1243 690 : if ( pDataDim )
1244 272 : pDataRoot->InitFrom( pDataDim ); // recursive
1245 : }
1246 :
1247 1307 : ScDPSubTotalState aSubState; // initial state
1248 :
1249 1307 : long nUserSubCount = GetSubTotalCount();
1250 :
1251 : // Calculate at least automatic if no subtotals are selected,
1252 : // show only own values if there's no child dimension (innermost).
1253 1307 : if ( !nUserSubCount || !pChildDimension )
1254 885 : nUserSubCount = 1;
1255 :
1256 1307 : const ScDPLevel* pParentLevel = GetParentLevel();
1257 :
1258 2614 : for (long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic"
1259 : {
1260 : // #i68338# if nUserSubCount is 1 (automatic only), don't set nRowSubTotalFunc
1261 1307 : if ( pChildDimension && nUserSubCount > 1 )
1262 : {
1263 0 : aSubState.nRowSubTotalFunc = nUserPos;
1264 0 : aSubState.eRowForce = lcl_GetForceFunc( pParentLevel, nUserPos );
1265 : }
1266 :
1267 1307 : pDataRoot->ProcessData( aDataMembers, aValues, aSubState );
1268 : }
1269 1307 : }
1270 :
1271 : /**
1272 : * Parse subtotal string and replace all occurrences of '?' with the caption
1273 : * string. Do ensure that escaped characters are not translated.
1274 : */
1275 0 : static OUString lcl_parseSubtotalName(const OUString& rSubStr, const OUString& rCaption)
1276 : {
1277 0 : OUStringBuffer aNewStr;
1278 0 : sal_Int32 n = rSubStr.getLength();
1279 0 : bool bEscaped = false;
1280 0 : for (sal_Int32 i = 0; i < n; ++i)
1281 : {
1282 0 : sal_Unicode c = rSubStr[i];
1283 0 : if (!bEscaped && c == '\\')
1284 : {
1285 0 : bEscaped = true;
1286 0 : continue;
1287 : }
1288 :
1289 0 : if (!bEscaped && c == '?')
1290 0 : aNewStr.append(rCaption);
1291 : else
1292 0 : aNewStr.append(c);
1293 0 : bEscaped = false;
1294 : }
1295 0 : return aNewStr.makeStringAndClear();
1296 : }
1297 :
1298 709 : void ScDPResultMember::FillMemberResults(
1299 : uno::Sequence<sheet::MemberResult>* pSequences, long& rPos, long nMeasure, bool bRoot,
1300 : const OUString* pMemberName, const OUString* pMemberCaption )
1301 : {
1302 : // IsVisible() test is in ScDPResultDimension::FillMemberResults
1303 : // (not on data layout dimension)
1304 :
1305 709 : if (!pSequences->getLength())
1306 : // empty sequence. Bail out.
1307 709 : return;
1308 :
1309 709 : long nSize = GetSize(nMeasure);
1310 709 : sheet::MemberResult* pArray = pSequences->getArray();
1311 : OSL_ENSURE( rPos+nSize <= pSequences->getLength(), "bumm" );
1312 :
1313 709 : bool bIsNumeric = false;
1314 709 : OUString aName;
1315 709 : if ( pMemberName ) // if pMemberName != NULL, use instead of real member name
1316 : {
1317 22 : aName = *pMemberName;
1318 : }
1319 : else
1320 : {
1321 687 : ScDPItemData aItemData;
1322 687 : FillItemData( aItemData );
1323 687 : if (aParentDimData.mpParentDim)
1324 : {
1325 568 : long nDim = aParentDimData.mpParentDim->GetDimension();
1326 568 : aName = pResultData->GetSource().GetData()->GetFormattedString(nDim, aItemData);
1327 : }
1328 : else
1329 : {
1330 119 : long nDim = -1;
1331 119 : const ScDPMember* pMem = GetDPMember();
1332 119 : if (pMem)
1333 0 : nDim = pMem->GetDim();
1334 119 : aName = pResultData->GetSource().GetData()->GetFormattedString(nDim, aItemData);
1335 : }
1336 :
1337 687 : ScDPItemData::Type eType = aItemData.GetType();
1338 687 : bIsNumeric = eType == ScDPItemData::Value || eType == ScDPItemData::GroupValue;
1339 : }
1340 :
1341 709 : const ScDPDimension* pParentDim = GetParentDim();
1342 709 : if ( bIsNumeric && pParentDim && pResultData->IsNumOrDateGroup( pParentDim->GetDimension() ) )
1343 : {
1344 : // Numeric group dimensions use numeric entries for proper sorting,
1345 : // but the group titles must be output as text.
1346 19 : bIsNumeric = false;
1347 : }
1348 :
1349 1418 : OUString aCaption = aName;
1350 709 : const ScDPMember* pMemberDesc = GetDPMember();
1351 709 : if (pMemberDesc)
1352 : {
1353 590 : const OUString* pLayoutName = pMemberDesc->GetLayoutName();
1354 590 : if (pLayoutName)
1355 : {
1356 0 : aCaption = *pLayoutName;
1357 0 : bIsNumeric = false; // layout name is always non-numeric.
1358 : }
1359 : }
1360 :
1361 709 : if ( pMemberCaption ) // use pMemberCaption if != NULL
1362 22 : aCaption = *pMemberCaption;
1363 709 : if (aCaption.isEmpty())
1364 1 : aCaption = ScGlobal::GetRscString(STR_EMPTYDATA);
1365 :
1366 709 : if (bIsNumeric)
1367 344 : pArray[rPos].Flags |= sheet::MemberResultFlags::NUMERIC;
1368 : else
1369 365 : pArray[rPos].Flags &= ~sheet::MemberResultFlags::NUMERIC;
1370 :
1371 709 : const ScDPLevel* pParentLevel = GetParentLevel();
1372 709 : if ( nSize && !bRoot ) // root is overwritten by first dimension
1373 : {
1374 590 : pArray[rPos].Name = aName;
1375 590 : pArray[rPos].Caption = aCaption;
1376 590 : pArray[rPos].Flags |= sheet::MemberResultFlags::HASMEMBER;
1377 :
1378 : // set "continue" flag (removed for subtotals later)
1379 628 : for (long i=1; i<nSize; i++)
1380 38 : pArray[rPos+i].Flags |= sheet::MemberResultFlags::CONTINUE;
1381 590 : if ( pParentLevel && pParentLevel->getRepeatItemLabels() )
1382 : {
1383 4 : long nSizeNonEmpty = nSize;
1384 4 : if ( pParentLevel->IsAddEmpty() )
1385 0 : --nSizeNonEmpty;
1386 8 : for (long i=1; i<nSizeNonEmpty; i++)
1387 : {
1388 4 : pArray[rPos+i].Name = aName;
1389 4 : pArray[rPos+i].Caption = aCaption;
1390 4 : pArray[rPos+i].Flags |= sheet::MemberResultFlags::HASMEMBER;
1391 : }
1392 : }
1393 : }
1394 :
1395 709 : long nExtraSpace = 0;
1396 709 : if ( pParentLevel && pParentLevel->IsAddEmpty() )
1397 0 : ++nExtraSpace;
1398 :
1399 709 : bool bTitleLine = false;
1400 709 : if ( pParentLevel && pParentLevel->IsOutlineLayout() )
1401 0 : bTitleLine = true;
1402 :
1403 : // if the subtotals are shown at the top (title row) in outline layout,
1404 : // no extra row for the subtotals is needed
1405 709 : bool bSubTotalInTitle = IsSubTotalInTitle( nMeasure );
1406 :
1407 709 : bool bHasChild = ( pChildDimension != NULL );
1408 709 : if (bHasChild)
1409 : {
1410 189 : if ( bTitleLine ) // in tabular layout the title is on a separate row
1411 0 : ++rPos; // -> fill child dimension one row below
1412 :
1413 189 : if (bRoot) // same sequence for root member
1414 118 : pChildDimension->FillMemberResults( pSequences, rPos, nMeasure );
1415 : else
1416 71 : pChildDimension->FillMemberResults( pSequences + nMemberStep/*1*/, rPos, nMeasure );
1417 :
1418 189 : if ( bTitleLine ) // title row is included in GetSize, so the following
1419 0 : --rPos; // positions are calculated with the normal values
1420 : }
1421 :
1422 709 : rPos += nSize;
1423 :
1424 : long nUserSubStart;
1425 709 : long nUserSubCount = GetSubTotalCount(&nUserSubStart);
1426 709 : if ( nUserSubCount && pChildDimension && !bSubTotalInTitle )
1427 : {
1428 111 : long nMemberMeasure = nMeasure;
1429 111 : long nSubSize = pResultData->GetCountForMeasure(nMeasure);
1430 :
1431 111 : rPos -= nSubSize * (nUserSubCount - nUserSubStart); // GetSize includes space for SubTotal
1432 111 : rPos -= nExtraSpace; // GetSize includes the empty line
1433 :
1434 222 : for (long nUserPos=nUserSubStart; nUserPos<nUserSubCount; nUserPos++)
1435 : {
1436 224 : for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ )
1437 : {
1438 113 : if ( nMeasure == SC_DPMEASURE_ALL )
1439 4 : nMemberMeasure = nSubCount;
1440 :
1441 113 : ScSubTotalFunc eForce = SUBTOTAL_FUNC_NONE;
1442 113 : if (bHasChild)
1443 113 : eForce = lcl_GetForceFunc( pParentLevel, nUserPos );
1444 :
1445 113 : bool bTotalResult = false;
1446 113 : OUString aSubStr = aCaption + " " + pResultData->GetMeasureString(nMemberMeasure, false, eForce, bTotalResult);
1447 :
1448 113 : if (bTotalResult)
1449 : {
1450 109 : if (pMemberDesc)
1451 : {
1452 : // single data field layout.
1453 0 : const OUString* pSubtotalName = pParentDim->GetSubtotalName();
1454 0 : if (pSubtotalName)
1455 0 : aSubStr = lcl_parseSubtotalName(*pSubtotalName, aCaption);
1456 0 : pArray[rPos].Flags &= ~sheet::MemberResultFlags::GRANDTOTAL;
1457 : }
1458 : else
1459 : {
1460 : // root member - subtotal (grand total?) for multi-data field layout.
1461 109 : const OUString* pGrandTotalName = pResultData->GetSource().GetGrandTotalName();
1462 109 : if (pGrandTotalName)
1463 0 : aSubStr = *pGrandTotalName;
1464 109 : pArray[rPos].Flags |= sheet::MemberResultFlags::GRANDTOTAL;
1465 : }
1466 : }
1467 :
1468 113 : pArray[rPos].Name = aName;
1469 113 : pArray[rPos].Caption = aSubStr;
1470 113 : pArray[rPos].Flags = ( pArray[rPos].Flags |
1471 113 : ( sheet::MemberResultFlags::HASMEMBER | sheet::MemberResultFlags::SUBTOTAL) ) &
1472 113 : ~sheet::MemberResultFlags::CONTINUE;
1473 :
1474 113 : if ( nMeasure == SC_DPMEASURE_ALL )
1475 : {
1476 : // data layout dimension is (direct/indirect) child of this.
1477 : // data layout dimension must have name for all entries.
1478 :
1479 4 : uno::Sequence<sheet::MemberResult>* pLayoutSeq = pSequences;
1480 4 : if (!bRoot)
1481 0 : ++pLayoutSeq;
1482 4 : ScDPResultDimension* pLayoutDim = pChildDimension;
1483 12 : while ( pLayoutDim && !pLayoutDim->IsDataLayout() )
1484 : {
1485 4 : pLayoutDim = pLayoutDim->GetFirstChildDimension();
1486 4 : ++pLayoutSeq;
1487 : }
1488 4 : if ( pLayoutDim )
1489 : {
1490 4 : sheet::MemberResult* pLayoutArray = pLayoutSeq->getArray();
1491 4 : pLayoutArray[rPos].Name = pResultData->GetMeasureDimensionName(nMemberMeasure);
1492 : }
1493 : }
1494 :
1495 113 : rPos += 1;
1496 113 : }
1497 : }
1498 :
1499 111 : rPos += nExtraSpace; // add again (subtracted above)
1500 709 : }
1501 : }
1502 :
1503 490 : void ScDPResultMember::FillDataResults(
1504 : const ScDPResultMember* pRefMember,
1505 : ScDPResultFilterContext& rFilterCxt, uno::Sequence<uno::Sequence<sheet::DataResult> >& rSequence,
1506 : long nMeasure) const
1507 : {
1508 490 : boost::scoped_ptr<FilterStack> pFilterStack;
1509 490 : const ScDPMember* pDPMember = GetDPMember();
1510 490 : if (pDPMember)
1511 : {
1512 : // Root result has no corresponding DP member. Only take the non-root results.
1513 399 : OUString aMemStr = GetDisplayName();
1514 399 : pFilterStack.reset(new FilterStack(rFilterCxt.maFilters));
1515 399 : pFilterStack->pushDimValue(aMemStr);
1516 : }
1517 :
1518 : // IsVisible() test is in ScDPResultDimension::FillDataResults
1519 : // (not on data layout dimension)
1520 490 : const ScDPLevel* pParentLevel = GetParentLevel();
1521 490 : long nStartRow = rFilterCxt.mnRow;
1522 :
1523 490 : long nExtraSpace = 0;
1524 490 : if ( pParentLevel && pParentLevel->IsAddEmpty() )
1525 0 : ++nExtraSpace;
1526 :
1527 490 : bool bTitleLine = false;
1528 490 : if ( pParentLevel && pParentLevel->IsOutlineLayout() )
1529 0 : bTitleLine = true;
1530 :
1531 490 : bool bSubTotalInTitle = IsSubTotalInTitle( nMeasure );
1532 :
1533 490 : bool bHasChild = ( pChildDimension != NULL );
1534 490 : if (bHasChild)
1535 : {
1536 136 : if ( bTitleLine ) // in tabular layout the title is on a separate row
1537 0 : ++rFilterCxt.mnRow; // -> fill child dimension one row below
1538 :
1539 136 : long nOldRow = rFilterCxt.mnRow;
1540 136 : pChildDimension->FillDataResults(pRefMember, rFilterCxt, rSequence, nMeasure);
1541 136 : rFilterCxt.mnRow = nOldRow; // Revert to the original row before the call.
1542 :
1543 136 : rFilterCxt.mnRow += GetSize( nMeasure );
1544 :
1545 136 : if ( bTitleLine ) // title row is included in GetSize, so the following
1546 0 : --rFilterCxt.mnRow; // positions are calculated with the normal values
1547 : }
1548 :
1549 : long nUserSubStart;
1550 490 : long nUserSubCount = GetSubTotalCount(&nUserSubStart);
1551 490 : if ( nUserSubCount || !bHasChild )
1552 : {
1553 : // Calculate at least automatic if no subtotals are selected,
1554 : // show only own values if there's no child dimension (innermost).
1555 428 : if ( !nUserSubCount || !bHasChild )
1556 : {
1557 354 : nUserSubCount = 1;
1558 354 : nUserSubStart = 0;
1559 : }
1560 :
1561 428 : long nMemberMeasure = nMeasure;
1562 428 : long nSubSize = pResultData->GetCountForMeasure(nMeasure);
1563 428 : if (bHasChild)
1564 : {
1565 74 : rFilterCxt.mnRow -= nSubSize * ( nUserSubCount - nUserSubStart ); // GetSize includes space for SubTotal
1566 74 : rFilterCxt.mnRow -= nExtraSpace; // GetSize includes the empty line
1567 : }
1568 :
1569 428 : long nMoveSubTotal = 0;
1570 428 : if ( bSubTotalInTitle )
1571 : {
1572 0 : nMoveSubTotal = rFilterCxt.mnRow - nStartRow; // force to first (title) row
1573 0 : rFilterCxt.mnRow = nStartRow;
1574 : }
1575 :
1576 428 : if ( pDataRoot )
1577 : {
1578 427 : ScDPSubTotalState aSubState; // initial state
1579 :
1580 854 : for (long nUserPos=nUserSubStart; nUserPos<nUserSubCount; nUserPos++)
1581 : {
1582 427 : if ( bHasChild && nUserSubCount > 1 )
1583 : {
1584 0 : aSubState.nRowSubTotalFunc = nUserPos;
1585 0 : aSubState.eRowForce = lcl_GetForceFunc( /*pParentLevel*/GetParentLevel() , nUserPos );
1586 : }
1587 :
1588 856 : for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ )
1589 : {
1590 429 : if ( nMeasure == SC_DPMEASURE_ALL )
1591 4 : nMemberMeasure = nSubCount;
1592 425 : else if ( pResultData->GetColStartMeasure() == SC_DPMEASURE_ALL )
1593 19 : nMemberMeasure = SC_DPMEASURE_ALL;
1594 :
1595 : OSL_ENSURE( rFilterCxt.mnRow < rSequence.getLength(), "bumm" );
1596 429 : uno::Sequence<sheet::DataResult>& rSubSeq = rSequence.getArray()[rFilterCxt.mnRow];
1597 429 : rFilterCxt.mnCol = 0;
1598 429 : if (pRefMember->IsVisible())
1599 429 : pDataRoot->FillDataRow(pRefMember, rFilterCxt, rSubSeq, nMemberMeasure, bHasChild, aSubState);
1600 :
1601 429 : rFilterCxt.mnRow += 1;
1602 : }
1603 : }
1604 : }
1605 : else
1606 1 : rFilterCxt.mnRow += nSubSize * ( nUserSubCount - nUserSubStart ); // empty rows occur when ShowEmpty is true
1607 :
1608 : // add extra space again if subtracted from GetSize above,
1609 : // add to own size if no children
1610 428 : rFilterCxt.mnRow += nExtraSpace;
1611 428 : rFilterCxt.mnRow += nMoveSubTotal;
1612 490 : }
1613 490 : }
1614 :
1615 496 : void ScDPResultMember::UpdateDataResults( const ScDPResultMember* pRefMember, long nMeasure ) const
1616 : {
1617 : // IsVisible() test is in ScDPResultDimension::FillDataResults
1618 : // (not on data layout dimension)
1619 :
1620 496 : bool bHasChild = ( pChildDimension != NULL );
1621 :
1622 496 : long nUserSubCount = GetSubTotalCount();
1623 :
1624 : // process subtotals even if not shown
1625 :
1626 : // Calculate at least automatic if no subtotals are selected,
1627 : // show only own values if there's no child dimension (innermost).
1628 496 : if (!nUserSubCount || !bHasChild)
1629 421 : nUserSubCount = 1;
1630 :
1631 496 : long nMemberMeasure = nMeasure;
1632 496 : long nSubSize = pResultData->GetCountForMeasure(nMeasure);
1633 :
1634 496 : if (pDataRoot)
1635 : {
1636 495 : ScDPSubTotalState aSubState; // initial state
1637 :
1638 990 : for (long nUserPos = 0; nUserPos < nUserSubCount; ++nUserPos) // including hidden "automatic"
1639 : {
1640 495 : if (bHasChild && nUserSubCount > 1)
1641 : {
1642 0 : aSubState.nRowSubTotalFunc = nUserPos;
1643 0 : aSubState.eRowForce = lcl_GetForceFunc(GetParentLevel(), nUserPos);
1644 : }
1645 :
1646 1000 : for (long nSubCount = 0; nSubCount < nSubSize; ++nSubCount)
1647 : {
1648 505 : if (nMeasure == SC_DPMEASURE_ALL)
1649 20 : nMemberMeasure = nSubCount;
1650 485 : else if (pResultData->GetColStartMeasure() == SC_DPMEASURE_ALL)
1651 19 : nMemberMeasure = SC_DPMEASURE_ALL;
1652 :
1653 505 : pDataRoot->UpdateDataRow(pRefMember, nMemberMeasure, bHasChild, aSubState);
1654 : }
1655 : }
1656 : }
1657 :
1658 496 : if (bHasChild) // child dimension must be processed last, so the column total is known
1659 : {
1660 137 : pChildDimension->UpdateDataResults( pRefMember, nMeasure );
1661 : }
1662 496 : }
1663 :
1664 482 : void ScDPResultMember::SortMembers( ScDPResultMember* pRefMember )
1665 : {
1666 482 : bool bHasChild = ( pChildDimension != NULL );
1667 482 : if (bHasChild)
1668 136 : pChildDimension->SortMembers( pRefMember ); // sorting is done at the dimension
1669 :
1670 482 : if ( IsRoot() && pDataRoot )
1671 : {
1672 : // use the row root member to sort columns
1673 : // sub total count is always 1
1674 :
1675 90 : pDataRoot->SortMembers( pRefMember );
1676 : }
1677 482 : }
1678 :
1679 6 : void ScDPResultMember::DoAutoShow( ScDPResultMember* pRefMember )
1680 : {
1681 6 : bool bHasChild = ( pChildDimension != NULL );
1682 6 : if (bHasChild)
1683 1 : pChildDimension->DoAutoShow( pRefMember ); // sorting is done at the dimension
1684 :
1685 6 : if ( IsRoot()&& pDataRoot )
1686 : {
1687 : // use the row root member to sort columns
1688 : // sub total count is always 1
1689 :
1690 1 : pDataRoot->DoAutoShow( pRefMember );
1691 : }
1692 6 : }
1693 :
1694 12 : void ScDPResultMember::ResetResults()
1695 : {
1696 12 : if (pDataRoot)
1697 11 : pDataRoot->ResetResults();
1698 :
1699 12 : if (pChildDimension)
1700 2 : pChildDimension->ResetResults();
1701 12 : }
1702 :
1703 490 : void ScDPResultMember::UpdateRunningTotals( const ScDPResultMember* pRefMember, long nMeasure,
1704 : ScDPRunningTotalState& rRunning, ScDPRowTotals& rTotals ) const
1705 : {
1706 : // IsVisible() test is in ScDPResultDimension::FillDataResults
1707 : // (not on data layout dimension)
1708 :
1709 490 : rTotals.SetInColRoot( IsRoot() );
1710 :
1711 490 : bool bHasChild = ( pChildDimension != NULL );
1712 :
1713 490 : long nUserSubCount = GetSubTotalCount();
1714 : //if ( nUserSubCount || !bHasChild )
1715 : {
1716 : // Calculate at least automatic if no subtotals are selected,
1717 : // show only own values if there's no child dimension (innermost).
1718 490 : if ( !nUserSubCount || !bHasChild )
1719 416 : nUserSubCount = 1;
1720 :
1721 490 : long nMemberMeasure = nMeasure;
1722 490 : long nSubSize = pResultData->GetCountForMeasure(nMeasure);
1723 :
1724 490 : if ( pDataRoot )
1725 : {
1726 489 : ScDPSubTotalState aSubState; // initial state
1727 :
1728 978 : for (long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic"
1729 : {
1730 489 : if ( bHasChild && nUserSubCount > 1 )
1731 : {
1732 0 : aSubState.nRowSubTotalFunc = nUserPos;
1733 0 : aSubState.eRowForce = lcl_GetForceFunc(GetParentLevel(), nUserPos);
1734 : }
1735 :
1736 988 : for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ )
1737 : {
1738 499 : if ( nMeasure == SC_DPMEASURE_ALL )
1739 20 : nMemberMeasure = nSubCount;
1740 479 : else if ( pResultData->GetColStartMeasure() == SC_DPMEASURE_ALL )
1741 19 : nMemberMeasure = SC_DPMEASURE_ALL;
1742 :
1743 499 : if (pRefMember->IsVisible())
1744 : pDataRoot->UpdateRunningTotals(
1745 499 : pRefMember, nMemberMeasure, bHasChild, aSubState, rRunning, rTotals, *this);
1746 : }
1747 : }
1748 : }
1749 : }
1750 :
1751 490 : if (bHasChild) // child dimension must be processed last, so the column total is known
1752 : {
1753 136 : pChildDimension->UpdateRunningTotals( pRefMember, nMeasure, rRunning, rTotals );
1754 : }
1755 490 : }
1756 :
1757 : #if DEBUG_PIVOT_TABLE
1758 : void ScDPResultMember::DumpState( const ScDPResultMember* pRefMember, ScDocument* pDoc, ScAddress& rPos ) const
1759 : {
1760 : lcl_DumpRow( OUString("ScDPResultMember"), GetName(), NULL, pDoc, rPos );
1761 : SCROW nStartRow = rPos.Row();
1762 :
1763 : if (pDataRoot)
1764 : pDataRoot->DumpState( pRefMember, pDoc, rPos );
1765 :
1766 : if (pChildDimension)
1767 : pChildDimension->DumpState( pRefMember, pDoc, rPos );
1768 :
1769 : lcl_Indent( pDoc, nStartRow, rPos );
1770 : }
1771 :
1772 : void ScDPResultMember::Dump(int nIndent) const
1773 : {
1774 : std::string aIndent(nIndent*2, ' ');
1775 : std::cout << aIndent << "-- result member '" << GetName() << "'" << std::endl;
1776 :
1777 : std::cout << aIndent << " column totals" << std::endl;
1778 : for (const ScDPAggData* p = &aColTotal; p; p = p->GetExistingChild())
1779 : p->Dump(nIndent+1);
1780 :
1781 : if (pChildDimension)
1782 : pChildDimension->Dump(nIndent+1);
1783 :
1784 : if (pDataRoot)
1785 : {
1786 : std::cout << aIndent << " data root" << std::endl;
1787 : pDataRoot->Dump(nIndent+1);
1788 : }
1789 : }
1790 : #endif
1791 :
1792 5 : ScDPAggData* ScDPResultMember::GetColTotal( long nMeasure ) const
1793 : {
1794 5 : return lcl_GetChildTotal( const_cast<ScDPAggData*>(&aColTotal), nMeasure );
1795 : }
1796 :
1797 5586 : void ScDPResultMember::FillVisibilityData(ScDPResultVisibilityData& rData) const
1798 : {
1799 5586 : if (pChildDimension)
1800 1596 : pChildDimension->FillVisibilityData(rData);
1801 5586 : }
1802 :
1803 2107 : ScDPDataMember::ScDPDataMember( const ScDPResultData* pData, const ScDPResultMember* pRes ) :
1804 : pResultData( pData ),
1805 : pResultMember( pRes ),
1806 2107 : pChildDimension( NULL )
1807 : {
1808 : // pResultMember is 0 for root members
1809 2107 : }
1810 :
1811 4150 : ScDPDataMember::~ScDPDataMember()
1812 : {
1813 2075 : delete pChildDimension;
1814 2075 : }
1815 :
1816 0 : OUString ScDPDataMember::GetName() const
1817 : {
1818 0 : if (pResultMember)
1819 0 : return pResultMember->GetName();
1820 : else
1821 0 : return EMPTY_OUSTRING;
1822 : }
1823 :
1824 0 : bool ScDPDataMember::IsVisible() const
1825 : {
1826 0 : if (pResultMember)
1827 0 : return pResultMember->IsVisible();
1828 : else
1829 0 : return false;
1830 : }
1831 :
1832 1400 : bool ScDPDataMember::IsNamedItem( SCROW nRow ) const
1833 : {
1834 1400 : if (pResultMember)
1835 1400 : return pResultMember->IsNamedItem(nRow);
1836 : else
1837 0 : return false;
1838 : }
1839 :
1840 0 : bool ScDPDataMember::HasHiddenDetails() const
1841 : {
1842 0 : if (pResultMember)
1843 0 : return pResultMember->HasHiddenDetails();
1844 : else
1845 0 : return false;
1846 : }
1847 :
1848 323 : void ScDPDataMember::InitFrom( const ScDPResultDimension* pDim )
1849 : {
1850 323 : if ( !pChildDimension )
1851 323 : pChildDimension = new ScDPDataDimension(pResultData);
1852 323 : pChildDimension->InitFrom(pDim);
1853 323 : }
1854 :
1855 : const long SC_SUBTOTALPOS_AUTO = -1; // default
1856 : const long SC_SUBTOTALPOS_SKIP = -2; // don't use
1857 :
1858 8573 : static long lcl_GetSubTotalPos( const ScDPSubTotalState& rSubState )
1859 : {
1860 8573 : if ( rSubState.nColSubTotalFunc >= 0 && rSubState.nRowSubTotalFunc >= 0 &&
1861 0 : rSubState.nColSubTotalFunc != rSubState.nRowSubTotalFunc )
1862 : {
1863 : // #i68338# don't return the same index for different combinations (leading to repeated updates),
1864 : // return a "don't use" value instead
1865 :
1866 0 : return SC_SUBTOTALPOS_SKIP;
1867 : }
1868 :
1869 8573 : long nRet = SC_SUBTOTALPOS_AUTO;
1870 8573 : if ( rSubState.nColSubTotalFunc >= 0 ) nRet = rSubState.nColSubTotalFunc;
1871 8573 : if ( rSubState.nRowSubTotalFunc >= 0 ) nRet = rSubState.nRowSubTotalFunc;
1872 8573 : return nRet;
1873 : }
1874 :
1875 1865 : void ScDPDataMember::UpdateValues( const vector<ScDPValue>& aValues, const ScDPSubTotalState& rSubState )
1876 : {
1877 : //TODO: find out how many and which subtotals are used
1878 :
1879 1865 : ScDPAggData* pAgg = &aAggregate;
1880 :
1881 1865 : long nSubPos = lcl_GetSubTotalPos(rSubState);
1882 1865 : if (nSubPos == SC_SUBTOTALPOS_SKIP)
1883 1865 : return;
1884 1865 : if (nSubPos > 0)
1885 : {
1886 0 : long nSkip = nSubPos * pResultData->GetMeasureCount();
1887 0 : for (long i=0; i<nSkip; i++)
1888 0 : pAgg = pAgg->GetChild(); // created if not there
1889 : }
1890 :
1891 1865 : size_t nCount = aValues.size();
1892 3834 : for (size_t nPos = 0; nPos < nCount; ++nPos)
1893 : {
1894 1969 : pAgg->Update(aValues[nPos], pResultData->GetMeasureFunction(nPos), rSubState);
1895 1969 : pAgg = pAgg->GetChild();
1896 : }
1897 : }
1898 :
1899 1865 : void ScDPDataMember::ProcessData( const vector< SCROW >& aChildMembers, const vector<ScDPValue>& aValues,
1900 : const ScDPSubTotalState& rSubState )
1901 : {
1902 1865 : if ( pResultData->IsLateInit() && !pChildDimension && pResultMember && pResultMember->GetChildDimension() )
1903 : {
1904 : // if this DataMember doesn't have a child dimension because the ResultMember's
1905 : // child dimension wasn't there yet during this DataMembers's creation,
1906 : // create the child dimension now
1907 51 : InitFrom( pResultMember->GetChildDimension() );
1908 : }
1909 :
1910 1865 : long nUserSubCount = pResultMember ? pResultMember->GetSubTotalCount() : 0;
1911 :
1912 : // Calculate at least automatic if no subtotals are selected,
1913 : // show only own values if there's no child dimension (innermost).
1914 1865 : if ( !nUserSubCount || !pChildDimension )
1915 1865 : nUserSubCount = 1;
1916 :
1917 1865 : ScDPSubTotalState aLocalSubState = rSubState; // keep row state, modify column
1918 3730 : for (long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic"
1919 : {
1920 1865 : if ( pChildDimension && nUserSubCount > 1 )
1921 : {
1922 0 : const ScDPLevel* pForceLevel = pResultMember ? pResultMember->GetParentLevel() : NULL;
1923 0 : aLocalSubState.nColSubTotalFunc = nUserPos;
1924 0 : aLocalSubState.eColForce = lcl_GetForceFunc( pForceLevel, nUserPos );
1925 : }
1926 :
1927 1865 : UpdateValues( aValues, aLocalSubState );
1928 : }
1929 :
1930 1865 : if (pChildDimension)
1931 558 : pChildDimension->ProcessData( aChildMembers, aValues, rSubState ); // with unmodified subtotal state
1932 1865 : }
1933 :
1934 1416 : bool ScDPDataMember::HasData( long nMeasure, const ScDPSubTotalState& rSubState ) const
1935 : {
1936 1416 : if ( rSubState.eColForce != SUBTOTAL_FUNC_NONE && rSubState.eRowForce != SUBTOTAL_FUNC_NONE &&
1937 0 : rSubState.eColForce != rSubState.eRowForce )
1938 0 : return false;
1939 :
1940 : // HasData can be different between measures!
1941 :
1942 1416 : const ScDPAggData* pAgg = GetConstAggData( nMeasure, rSubState );
1943 1416 : if (!pAgg)
1944 0 : return false; //TODO: error?
1945 :
1946 1416 : return pAgg->HasData();
1947 : }
1948 :
1949 776 : bool ScDPDataMember::HasError( long nMeasure, const ScDPSubTotalState& rSubState ) const
1950 : {
1951 776 : const ScDPAggData* pAgg = GetConstAggData( nMeasure, rSubState );
1952 776 : if (!pAgg)
1953 0 : return true;
1954 :
1955 776 : return pAgg->HasError();
1956 : }
1957 :
1958 776 : double ScDPDataMember::GetAggregate( long nMeasure, const ScDPSubTotalState& rSubState ) const
1959 : {
1960 776 : const ScDPAggData* pAgg = GetConstAggData( nMeasure, rSubState );
1961 776 : if (!pAgg)
1962 0 : return DBL_MAX; //TODO: error?
1963 :
1964 776 : return pAgg->GetResult();
1965 : }
1966 :
1967 3730 : ScDPAggData* ScDPDataMember::GetAggData( long nMeasure, const ScDPSubTotalState& rSubState )
1968 : {
1969 : OSL_ENSURE( nMeasure >= 0, "GetAggData: no measure" );
1970 :
1971 3730 : ScDPAggData* pAgg = &aAggregate;
1972 3730 : long nSkip = nMeasure;
1973 3730 : long nSubPos = lcl_GetSubTotalPos(rSubState);
1974 3730 : if (nSubPos == SC_SUBTOTALPOS_SKIP)
1975 0 : return NULL;
1976 3730 : if (nSubPos > 0)
1977 0 : nSkip += nSubPos * pResultData->GetMeasureCount();
1978 :
1979 3842 : for ( long nPos=0; nPos<nSkip; nPos++ )
1980 112 : pAgg = pAgg->GetChild(); //TODO: need to create children here?
1981 :
1982 3730 : return pAgg;
1983 : }
1984 :
1985 2978 : const ScDPAggData* ScDPDataMember::GetConstAggData( long nMeasure, const ScDPSubTotalState& rSubState ) const
1986 : {
1987 : OSL_ENSURE( nMeasure >= 0, "GetConstAggData: no measure" );
1988 :
1989 2978 : const ScDPAggData* pAgg = &aAggregate;
1990 2978 : long nSkip = nMeasure;
1991 2978 : long nSubPos = lcl_GetSubTotalPos(rSubState);
1992 2978 : if (nSubPos == SC_SUBTOTALPOS_SKIP)
1993 0 : return NULL;
1994 2978 : if (nSubPos > 0)
1995 0 : nSkip += nSubPos * pResultData->GetMeasureCount();
1996 :
1997 3065 : for ( long nPos=0; nPos<nSkip; nPos++ )
1998 : {
1999 87 : pAgg = pAgg->GetExistingChild();
2000 87 : if (!pAgg)
2001 0 : return NULL;
2002 : }
2003 :
2004 2978 : return pAgg;
2005 : }
2006 :
2007 1531 : void ScDPDataMember::FillDataRow(
2008 : const ScDPResultMember* pRefMember, ScDPResultFilterContext& rFilterCxt,
2009 : uno::Sequence<sheet::DataResult>& rSequence, long nMeasure, bool bIsSubTotalRow,
2010 : const ScDPSubTotalState& rSubState) const
2011 : {
2012 1531 : boost::scoped_ptr<FilterStack> pFilterStack;
2013 1531 : if (pResultMember)
2014 : {
2015 : // Topmost data member (pResultMember=NULL) doesn't need to be handled
2016 : // since its immediate parent result member is linked to the same
2017 : // dimension member.
2018 1102 : pFilterStack.reset(new FilterStack(rFilterCxt.maFilters));
2019 1102 : pFilterStack->pushDimValue(pResultMember->GetDisplayName());
2020 : }
2021 :
2022 : OSL_ENSURE( pRefMember == pResultMember || !pResultMember, "bla" );
2023 :
2024 1531 : long nStartCol = rFilterCxt.mnCol;
2025 :
2026 1531 : const ScDPDataDimension* pDataChild = GetChildDimension();
2027 1531 : const ScDPResultDimension* pRefChild = pRefMember->GetChildDimension();
2028 :
2029 1531 : const ScDPLevel* pRefParentLevel = pRefMember->GetParentLevel();
2030 :
2031 1531 : long nExtraSpace = 0;
2032 1531 : if ( pRefParentLevel && pRefParentLevel->IsAddEmpty() )
2033 0 : ++nExtraSpace;
2034 :
2035 1531 : bool bTitleLine = false;
2036 1531 : if ( pRefParentLevel && pRefParentLevel->IsOutlineLayout() )
2037 0 : bTitleLine = true;
2038 :
2039 1531 : bool bSubTotalInTitle = pRefMember->IsSubTotalInTitle( nMeasure );
2040 :
2041 : // leave space for children even if the DataMember hasn't been initialized
2042 : // (pDataChild is null then, this happens when no values for it are in this row)
2043 1531 : bool bHasChild = ( pRefChild != NULL );
2044 :
2045 1531 : if ( bHasChild )
2046 : {
2047 344 : if ( bTitleLine ) // in tabular layout the title is on a separate column
2048 0 : ++rFilterCxt.mnCol; // -> fill child dimension one column below
2049 :
2050 344 : if ( pDataChild )
2051 : {
2052 284 : long nOldCol = rFilterCxt.mnCol;
2053 284 : pDataChild->FillDataRow(pRefChild, rFilterCxt, rSequence, nMeasure, bIsSubTotalRow, rSubState);
2054 284 : rFilterCxt.mnCol = nOldCol; // Revert to the old column value before the call.
2055 : }
2056 344 : rFilterCxt.mnCol += (sal_uInt16)pRefMember->GetSize( nMeasure );
2057 :
2058 344 : if ( bTitleLine ) // title column is included in GetSize, so the following
2059 0 : --rFilterCxt.mnCol; // positions are calculated with the normal values
2060 : }
2061 :
2062 : long nUserSubStart;
2063 1531 : long nUserSubCount = pRefMember->GetSubTotalCount(&nUserSubStart);
2064 1531 : if ( nUserSubCount || !bHasChild )
2065 : {
2066 : // Calculate at least automatic if no subtotals are selected,
2067 : // show only own values if there's no child dimension (innermost).
2068 1416 : if ( !nUserSubCount || !bHasChild )
2069 : {
2070 1187 : nUserSubCount = 1;
2071 1187 : nUserSubStart = 0;
2072 : }
2073 :
2074 1416 : ScDPSubTotalState aLocalSubState(rSubState); // keep row state, modify column
2075 :
2076 1416 : long nMemberMeasure = nMeasure;
2077 1416 : long nSubSize = pResultData->GetCountForMeasure(nMeasure);
2078 1416 : if (bHasChild)
2079 : {
2080 229 : rFilterCxt.mnCol -= nSubSize * ( nUserSubCount - nUserSubStart ); // GetSize includes space for SubTotal
2081 229 : rFilterCxt.mnCol -= nExtraSpace; // GetSize includes the empty line
2082 : }
2083 :
2084 1416 : long nMoveSubTotal = 0;
2085 1416 : if ( bSubTotalInTitle )
2086 : {
2087 0 : nMoveSubTotal = rFilterCxt.mnCol - nStartCol; // force to first (title) column
2088 0 : rFilterCxt.mnCol = nStartCol;
2089 : }
2090 :
2091 2832 : for (long nUserPos=nUserSubStart; nUserPos<nUserSubCount; nUserPos++)
2092 : {
2093 1416 : if ( pChildDimension && nUserSubCount > 1 )
2094 : {
2095 0 : const ScDPLevel* pForceLevel = pResultMember ? pResultMember->GetParentLevel() : NULL;
2096 0 : aLocalSubState.nColSubTotalFunc = nUserPos;
2097 0 : aLocalSubState.eColForce = lcl_GetForceFunc( pForceLevel, nUserPos );
2098 : }
2099 :
2100 2832 : for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ )
2101 : {
2102 1416 : if ( nMeasure == SC_DPMEASURE_ALL )
2103 0 : nMemberMeasure = nSubCount;
2104 :
2105 : OSL_ENSURE( rFilterCxt.mnCol < rSequence.getLength(), "bumm" );
2106 1416 : sheet::DataResult& rRes = rSequence.getArray()[rFilterCxt.mnCol];
2107 :
2108 1416 : if ( HasData( nMemberMeasure, aLocalSubState ) )
2109 : {
2110 776 : if ( HasError( nMemberMeasure, aLocalSubState ) )
2111 : {
2112 0 : rRes.Value = 0;
2113 0 : rRes.Flags |= sheet::DataResultFlags::ERROR;
2114 : }
2115 : else
2116 : {
2117 776 : rRes.Value = GetAggregate( nMemberMeasure, aLocalSubState );
2118 776 : rRes.Flags |= sheet::DataResultFlags::HASDATA;
2119 : }
2120 : }
2121 :
2122 1416 : if ( bHasChild || bIsSubTotalRow )
2123 448 : rRes.Flags |= sheet::DataResultFlags::SUBTOTAL;
2124 :
2125 1416 : rFilterCxt.maFilterSet.add(rFilterCxt.maFilters, rFilterCxt.mnCol, rFilterCxt.mnRow, rRes.Value);
2126 1416 : rFilterCxt.mnCol += 1;
2127 : }
2128 : }
2129 :
2130 : // add extra space again if subtracted from GetSize above,
2131 : // add to own size if no children
2132 1416 : rFilterCxt.mnCol += nExtraSpace;
2133 1416 : rFilterCxt.mnCol += nMoveSubTotal;
2134 1531 : }
2135 1531 : }
2136 :
2137 1952 : void ScDPDataMember::UpdateDataRow(
2138 : const ScDPResultMember* pRefMember, long nMeasure, bool bIsSubTotalRow,
2139 : const ScDPSubTotalState& rSubState )
2140 : {
2141 : OSL_ENSURE( pRefMember == pResultMember || !pResultMember, "bla" );
2142 :
2143 : // Calculate must be called even if not visible (for use as reference value)
2144 1952 : const ScDPDataDimension* pDataChild = GetChildDimension();
2145 1952 : const ScDPResultDimension* pRefChild = pRefMember->GetChildDimension();
2146 :
2147 : // leave space for children even if the DataMember hasn't been initialized
2148 : // (pDataChild is null then, this happens when no values for it are in this row)
2149 1952 : bool bHasChild = ( pRefChild != NULL );
2150 :
2151 : // process subtotals even if not shown
2152 1952 : long nUserSubCount = pRefMember->GetSubTotalCount();
2153 :
2154 : // Calculate at least automatic if no subtotals are selected,
2155 : // show only own values if there's no child dimension (innermost).
2156 1952 : if ( !nUserSubCount || !bHasChild )
2157 1693 : nUserSubCount = 1;
2158 :
2159 1952 : ScDPSubTotalState aLocalSubState(rSubState); // keep row state, modify column
2160 :
2161 1952 : long nMemberMeasure = nMeasure;
2162 1952 : long nSubSize = pResultData->GetCountForMeasure(nMeasure);
2163 :
2164 3904 : for (long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic"
2165 : {
2166 1952 : if ( pChildDimension && nUserSubCount > 1 )
2167 : {
2168 0 : const ScDPLevel* pForceLevel = pResultMember ? pResultMember->GetParentLevel() : NULL;
2169 0 : aLocalSubState.nColSubTotalFunc = nUserPos;
2170 0 : aLocalSubState.eColForce = lcl_GetForceFunc( pForceLevel, nUserPos );
2171 : }
2172 :
2173 3923 : for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ )
2174 : {
2175 1971 : if ( nMeasure == SC_DPMEASURE_ALL )
2176 38 : nMemberMeasure = nSubCount;
2177 :
2178 : // update data...
2179 1971 : ScDPAggData* pAggData = GetAggData( nMemberMeasure, aLocalSubState );
2180 1971 : if (pAggData)
2181 : {
2182 : //TODO: aLocalSubState?
2183 1971 : ScSubTotalFunc eFunc = pResultData->GetMeasureFunction( nMemberMeasure );
2184 1971 : sheet::DataPilotFieldReference aReferenceValue = pResultData->GetMeasureRefVal( nMemberMeasure );
2185 1971 : sal_Int32 eRefType = aReferenceValue.ReferenceType;
2186 :
2187 : // calculate the result first - for all members, regardless of reference value
2188 1971 : pAggData->Calculate( eFunc, aLocalSubState );
2189 :
2190 1971 : if ( eRefType == sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE ||
2191 1961 : eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE ||
2192 : eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE )
2193 : {
2194 : // copy the result into auxiliary value, so differences can be
2195 : // calculated in any order
2196 15 : pAggData->SetAuxiliary( pAggData->GetResult() );
2197 1971 : }
2198 : // column/row percentage/index is now in UpdateRunningTotals, so it doesn't disturb sorting
2199 : }
2200 : }
2201 : }
2202 :
2203 1952 : if ( bHasChild ) // child dimension must be processed last, so the row total is known
2204 : {
2205 449 : if ( pDataChild )
2206 329 : pDataChild->UpdateDataRow( pRefChild, nMeasure, bIsSubTotalRow, rSubState );
2207 : }
2208 1952 : }
2209 :
2210 299 : void ScDPDataMember::SortMembers( ScDPResultMember* pRefMember )
2211 : {
2212 : OSL_ENSURE( pRefMember == pResultMember || !pResultMember, "bla" );
2213 :
2214 299 : if ( pRefMember->IsVisible() ) //TODO: here or in ScDPDataDimension ???
2215 : {
2216 299 : ScDPDataDimension* pDataChild = GetChildDimension();
2217 299 : ScDPResultDimension* pRefChild = pRefMember->GetChildDimension();
2218 299 : if ( pRefChild && pDataChild )
2219 62 : pDataChild->SortMembers( pRefChild ); // sorting is done at the dimension
2220 : }
2221 299 : }
2222 :
2223 6 : void ScDPDataMember::DoAutoShow( ScDPResultMember* pRefMember )
2224 : {
2225 : OSL_ENSURE( pRefMember == pResultMember || !pResultMember, "bla" );
2226 :
2227 6 : if ( pRefMember->IsVisible() ) //TODO: here or in ScDPDataDimension ???
2228 : {
2229 6 : ScDPDataDimension* pDataChild = GetChildDimension();
2230 6 : ScDPResultDimension* pRefChild = pRefMember->GetChildDimension();
2231 6 : if ( pRefChild && pDataChild )
2232 1 : pDataChild->DoAutoShow( pRefChild ); // sorting is done at the dimension
2233 : }
2234 6 : }
2235 :
2236 41 : void ScDPDataMember::ResetResults()
2237 : {
2238 41 : aAggregate.Reset();
2239 :
2240 41 : ScDPDataDimension* pDataChild = GetChildDimension();
2241 41 : if ( pDataChild )
2242 6 : pDataChild->ResetResults();
2243 41 : }
2244 :
2245 1736 : void ScDPDataMember::UpdateRunningTotals(
2246 : const ScDPResultMember* pRefMember, long nMeasure, bool bIsSubTotalRow,
2247 : const ScDPSubTotalState& rSubState, ScDPRunningTotalState& rRunning,
2248 : ScDPRowTotals& rTotals, const ScDPResultMember& rRowParent )
2249 : {
2250 : OSL_ENSURE( pRefMember == pResultMember || !pResultMember, "bla" );
2251 :
2252 1736 : const ScDPDataDimension* pDataChild = GetChildDimension();
2253 1736 : const ScDPResultDimension* pRefChild = pRefMember->GetChildDimension();
2254 :
2255 1736 : bool bIsRoot = ( pResultMember == NULL || pResultMember->GetParentLevel() == NULL );
2256 :
2257 : // leave space for children even if the DataMember hasn't been initialized
2258 : // (pDataChild is null then, this happens when no values for it are in this row)
2259 1736 : bool bHasChild = ( pRefChild != NULL );
2260 :
2261 1736 : long nUserSubCount = pRefMember->GetSubTotalCount();
2262 : {
2263 : // Calculate at least automatic if no subtotals are selected,
2264 : // show only own values if there's no child dimension (innermost).
2265 1736 : if ( !nUserSubCount || !bHasChild )
2266 1483 : nUserSubCount = 1;
2267 :
2268 1736 : ScDPSubTotalState aLocalSubState(rSubState); // keep row state, modify column
2269 :
2270 1736 : long nMemberMeasure = nMeasure;
2271 1736 : long nSubSize = pResultData->GetCountForMeasure(nMeasure);
2272 :
2273 3472 : for (long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic"
2274 : {
2275 1736 : if ( pChildDimension && nUserSubCount > 1 )
2276 : {
2277 0 : const ScDPLevel* pForceLevel = pResultMember ? pResultMember->GetParentLevel() : NULL;
2278 0 : aLocalSubState.nColSubTotalFunc = nUserPos;
2279 0 : aLocalSubState.eColForce = lcl_GetForceFunc( pForceLevel, nUserPos );
2280 : }
2281 :
2282 3491 : for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ )
2283 : {
2284 1755 : if ( nMeasure == SC_DPMEASURE_ALL )
2285 38 : nMemberMeasure = nSubCount;
2286 :
2287 : // update data...
2288 1755 : ScDPAggData* pAggData = GetAggData( nMemberMeasure, aLocalSubState );
2289 1755 : if (pAggData)
2290 : {
2291 : //TODO: aLocalSubState?
2292 1755 : sheet::DataPilotFieldReference aReferenceValue = pResultData->GetMeasureRefVal( nMemberMeasure );
2293 1755 : sal_Int32 eRefType = aReferenceValue.ReferenceType;
2294 :
2295 1755 : if ( eRefType == sheet::DataPilotFieldReferenceType::RUNNING_TOTAL ||
2296 1745 : eRefType == sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE ||
2297 1740 : eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE ||
2298 : eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE )
2299 : {
2300 20 : bool bRunningTotal = ( eRefType == sheet::DataPilotFieldReferenceType::RUNNING_TOTAL );
2301 : bool bRelative =
2302 20 : ( aReferenceValue.ReferenceItemType != sheet::DataPilotFieldReferenceItemType::NAMED && !bRunningTotal );
2303 : long nRelativeDir = bRelative ?
2304 20 : ( ( aReferenceValue.ReferenceItemType == sheet::DataPilotFieldReferenceItemType::PREVIOUS ) ? -1 : 1 ) : 0;
2305 :
2306 20 : const ScDPRunningTotalState::IndexArray& rColVisible = rRunning.GetColVisible();
2307 20 : const ScDPRunningTotalState::IndexArray& rColSorted = rRunning.GetColSorted();
2308 20 : const ScDPRunningTotalState::IndexArray& rRowVisible = rRunning.GetRowVisible();
2309 20 : const ScDPRunningTotalState::IndexArray& rRowSorted = rRunning.GetRowSorted();
2310 :
2311 20 : OUString aRefFieldName = aReferenceValue.ReferenceField;
2312 :
2313 : //TODO: aLocalSubState?
2314 20 : sal_uInt16 nRefOrient = pResultData->GetMeasureRefOrient( nMemberMeasure );
2315 20 : bool bRefDimInCol = ( nRefOrient == sheet::DataPilotFieldOrientation_COLUMN );
2316 20 : bool bRefDimInRow = ( nRefOrient == sheet::DataPilotFieldOrientation_ROW );
2317 :
2318 20 : ScDPResultDimension* pSelectDim = NULL;
2319 20 : long nRowPos = 0;
2320 20 : long nColPos = 0;
2321 :
2322 : // find the reference field in column or row dimensions
2323 :
2324 20 : if ( bRefDimInRow ) // look in row dimensions
2325 : {
2326 20 : pSelectDim = rRunning.GetRowResRoot()->GetChildDimension();
2327 40 : while ( pSelectDim && pSelectDim->GetName() != aRefFieldName )
2328 : {
2329 0 : long nIndex = rRowSorted[nRowPos];
2330 0 : if ( nIndex >= 0 && nIndex < pSelectDim->GetMemberCount() )
2331 0 : pSelectDim = pSelectDim->GetMember(nIndex)->GetChildDimension();
2332 : else
2333 0 : pSelectDim = NULL;
2334 0 : ++nRowPos;
2335 : }
2336 : // child dimension of innermost member?
2337 20 : if ( pSelectDim && rRowSorted[nRowPos] < 0 )
2338 4 : pSelectDim = NULL;
2339 : }
2340 :
2341 20 : if ( bRefDimInCol ) // look in column dimensions
2342 : {
2343 0 : pSelectDim = rRunning.GetColResRoot()->GetChildDimension();
2344 0 : while ( pSelectDim && pSelectDim->GetName() != aRefFieldName )
2345 : {
2346 0 : long nIndex = rColSorted[nColPos];
2347 0 : if ( nIndex >= 0 && nIndex < pSelectDim->GetMemberCount() )
2348 0 : pSelectDim = pSelectDim->GetMember(nIndex)->GetChildDimension();
2349 : else
2350 0 : pSelectDim = NULL;
2351 0 : ++nColPos;
2352 : }
2353 : // child dimension of innermost member?
2354 0 : if ( pSelectDim && rColSorted[nColPos] < 0 )
2355 0 : pSelectDim = NULL;
2356 : }
2357 :
2358 20 : bool bNoDetailsInRef = false;
2359 20 : if ( pSelectDim && bRunningTotal )
2360 : {
2361 : // Running totals:
2362 : // If details are hidden for this member in the reference dimension,
2363 : // don't show or sum up the value. Otherwise, for following members,
2364 : // the running totals of details and subtotals wouldn't match.
2365 :
2366 4 : long nMyIndex = bRefDimInCol ? rColSorted[nColPos] : rRowSorted[nRowPos];
2367 4 : if ( nMyIndex >= 0 && nMyIndex < pSelectDim->GetMemberCount() )
2368 : {
2369 4 : const ScDPResultMember* pMyRefMember = pSelectDim->GetMember(nMyIndex);
2370 4 : if ( pMyRefMember && pMyRefMember->HasHiddenDetails() )
2371 : {
2372 0 : pSelectDim = NULL; // don't calculate
2373 0 : bNoDetailsInRef = true; // show error, not empty
2374 : }
2375 : }
2376 : }
2377 :
2378 20 : if ( bRelative )
2379 : {
2380 : // Difference/Percentage from previous/next:
2381 : // If details are hidden for this member in the innermost column/row
2382 : // dimension (the orientation of the reference dimension), show an
2383 : // error value.
2384 : // - If the no-details dimension is the reference dimension, its
2385 : // members will be skipped when finding the previous/next member,
2386 : // so there must be no results for its members.
2387 : // - If the no-details dimension is outside of the reference dimension,
2388 : // no calculation in the reference dimension is possible.
2389 : // - Otherwise, the error isn't strictly necessary, but shown for
2390 : // consistency.
2391 :
2392 : bool bInnerNoDetails = bRefDimInCol ? HasHiddenDetails() :
2393 0 : ( !bRefDimInRow || rRowParent.HasHiddenDetails() );
2394 0 : if ( bInnerNoDetails )
2395 : {
2396 0 : pSelectDim = NULL;
2397 0 : bNoDetailsInRef = true; // show error, not empty
2398 : }
2399 : }
2400 :
2401 20 : if ( !bRefDimInCol && !bRefDimInRow ) // invalid dimension specified
2402 0 : bNoDetailsInRef = true; // pSelectDim is then already NULL
2403 :
2404 : // get the member for the reference item and do the calculation
2405 :
2406 20 : if ( bRunningTotal )
2407 : {
2408 : // running total in (dimension) -> find first existing member
2409 :
2410 5 : if ( pSelectDim )
2411 : {
2412 : ScDPDataMember* pSelectMember;
2413 4 : if ( bRefDimInCol )
2414 : pSelectMember = ScDPResultDimension::GetColReferenceMember( NULL, NULL,
2415 0 : nColPos, rRunning );
2416 : else
2417 : {
2418 4 : const long* pRowSorted = &rRowSorted[0];
2419 4 : const long* pColSorted = &rColSorted[0];
2420 4 : pRowSorted += nRowPos + 1; // including the reference dimension
2421 : pSelectMember = pSelectDim->GetRowReferenceMember(
2422 4 : NULL, NULL, pRowSorted, pColSorted);
2423 : }
2424 :
2425 4 : if ( pSelectMember )
2426 : {
2427 : // The running total is kept as the auxiliary value in
2428 : // the first available member for the reference dimension.
2429 : // Members are visited in final order, so each one's result
2430 : // can be used and then modified.
2431 :
2432 : ScDPAggData* pSelectData = pSelectMember->
2433 4 : GetAggData( nMemberMeasure, aLocalSubState );
2434 4 : if ( pSelectData )
2435 : {
2436 4 : double fTotal = pSelectData->GetAuxiliary();
2437 4 : fTotal += pAggData->GetResult();
2438 4 : pSelectData->SetAuxiliary( fTotal );
2439 4 : pAggData->SetResult( fTotal );
2440 4 : pAggData->SetEmpty(false); // always display
2441 : }
2442 : }
2443 : else
2444 0 : pAggData->SetError();
2445 : }
2446 1 : else if (bNoDetailsInRef)
2447 0 : pAggData->SetError();
2448 : else
2449 1 : pAggData->SetEmpty(true); // empty (dim set to 0 above)
2450 : }
2451 : else
2452 : {
2453 : // difference/percentage -> find specified member
2454 :
2455 15 : if ( pSelectDim )
2456 : {
2457 12 : OUString aRefItemName = aReferenceValue.ReferenceItemName;
2458 12 : ScDPRelativePos aRefItemPos( 0, nRelativeDir ); // nBasePos is modified later
2459 :
2460 12 : const OUString* pRefName = NULL;
2461 12 : const ScDPRelativePos* pRefPos = NULL;
2462 12 : if ( bRelative )
2463 0 : pRefPos = &aRefItemPos;
2464 : else
2465 12 : pRefName = &aRefItemName;
2466 :
2467 : ScDPDataMember* pSelectMember;
2468 12 : if ( bRefDimInCol )
2469 : {
2470 0 : aRefItemPos.nBasePos = rColVisible[nColPos]; // without sort order applied
2471 : pSelectMember = ScDPResultDimension::GetColReferenceMember( pRefPos, pRefName,
2472 0 : nColPos, rRunning );
2473 : }
2474 : else
2475 : {
2476 12 : aRefItemPos.nBasePos = rRowVisible[nRowPos]; // without sort order applied
2477 12 : const long* pRowSorted = &rRowSorted[0];
2478 12 : const long* pColSorted = &rColSorted[0];
2479 12 : pRowSorted += nRowPos + 1; // including the reference dimension
2480 : pSelectMember = pSelectDim->GetRowReferenceMember(
2481 12 : pRefPos, pRefName, pRowSorted, pColSorted);
2482 : }
2483 :
2484 : // difference or perc.difference is empty for the reference item itself
2485 12 : if ( pSelectMember == this &&
2486 : eRefType != sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE )
2487 : {
2488 2 : pAggData->SetEmpty(true);
2489 : }
2490 10 : else if ( pSelectMember )
2491 : {
2492 : const ScDPAggData* pOtherAggData = pSelectMember->
2493 10 : GetConstAggData( nMemberMeasure, aLocalSubState );
2494 : OSL_ENSURE( pOtherAggData, "no agg data" );
2495 10 : if ( pOtherAggData )
2496 : {
2497 : // Reference member may be visited before or after this one,
2498 : // so the auxiliary value is used for the original result.
2499 :
2500 10 : double fOtherResult = pOtherAggData->GetAuxiliary();
2501 10 : double fThisResult = pAggData->GetResult();
2502 10 : bool bError = false;
2503 10 : switch ( eRefType )
2504 : {
2505 : case sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE:
2506 3 : fThisResult = fThisResult - fOtherResult;
2507 3 : break;
2508 : case sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE:
2509 4 : if ( fOtherResult == 0.0 )
2510 0 : bError = true;
2511 : else
2512 4 : fThisResult = fThisResult / fOtherResult;
2513 4 : break;
2514 : case sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE:
2515 3 : if ( fOtherResult == 0.0 )
2516 0 : bError = true;
2517 : else
2518 3 : fThisResult = ( fThisResult - fOtherResult ) / fOtherResult;
2519 3 : break;
2520 : default:
2521 : OSL_FAIL("invalid calculation type");
2522 : }
2523 10 : if ( bError )
2524 : {
2525 0 : pAggData->SetError();
2526 : }
2527 : else
2528 : {
2529 10 : pAggData->SetResult(fThisResult);
2530 10 : pAggData->SetEmpty(false); // always display
2531 : }
2532 : //TODO: errors in data?
2533 : }
2534 : }
2535 0 : else if (bRelative && !bNoDetailsInRef)
2536 0 : pAggData->SetEmpty(true); // empty
2537 : else
2538 0 : pAggData->SetError(); // error
2539 : }
2540 3 : else if (bNoDetailsInRef)
2541 0 : pAggData->SetError(); // error
2542 : else
2543 3 : pAggData->SetEmpty(true); // empty
2544 20 : }
2545 : }
2546 1735 : else if ( eRefType == sheet::DataPilotFieldReferenceType::ROW_PERCENTAGE ||
2547 1730 : eRefType == sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE ||
2548 1730 : eRefType == sheet::DataPilotFieldReferenceType::TOTAL_PERCENTAGE ||
2549 : eRefType == sheet::DataPilotFieldReferenceType::INDEX )
2550 : {
2551 :
2552 : // set total values when they are encountered (always before their use)
2553 :
2554 5 : ScDPAggData* pColTotalData = pRefMember->GetColTotal( nMemberMeasure );
2555 5 : ScDPAggData* pRowTotalData = rTotals.GetRowTotal( nMemberMeasure );
2556 5 : ScDPAggData* pGrandTotalData = rTotals.GetGrandTotal( nMemberMeasure );
2557 :
2558 5 : double fTotalValue = pAggData->HasError() ? 0 : pAggData->GetResult();
2559 :
2560 5 : if ( bIsRoot && rTotals.IsInColRoot() && pGrandTotalData )
2561 1 : pGrandTotalData->SetAuxiliary( fTotalValue );
2562 :
2563 5 : if ( bIsRoot && pRowTotalData )
2564 5 : pRowTotalData->SetAuxiliary( fTotalValue );
2565 :
2566 5 : if ( rTotals.IsInColRoot() && pColTotalData )
2567 1 : pColTotalData->SetAuxiliary( fTotalValue );
2568 :
2569 : // find relation to total values
2570 :
2571 5 : switch ( eRefType )
2572 : {
2573 : case sheet::DataPilotFieldReferenceType::ROW_PERCENTAGE:
2574 : case sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE:
2575 : case sheet::DataPilotFieldReferenceType::TOTAL_PERCENTAGE:
2576 : {
2577 : double nTotal;
2578 5 : if ( eRefType == sheet::DataPilotFieldReferenceType::ROW_PERCENTAGE )
2579 0 : nTotal = pRowTotalData->GetAuxiliary();
2580 5 : else if ( eRefType == sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE )
2581 5 : nTotal = pColTotalData->GetAuxiliary();
2582 : else
2583 0 : nTotal = pGrandTotalData->GetAuxiliary();
2584 :
2585 5 : if ( nTotal == 0.0 )
2586 0 : pAggData->SetError();
2587 : else
2588 5 : pAggData->SetResult( pAggData->GetResult() / nTotal );
2589 : }
2590 5 : break;
2591 : case sheet::DataPilotFieldReferenceType::INDEX:
2592 : {
2593 0 : double nColTotal = pColTotalData->GetAuxiliary();
2594 0 : double nRowTotal = pRowTotalData->GetAuxiliary();
2595 0 : double nGrandTotal = pGrandTotalData->GetAuxiliary();
2596 0 : if ( nRowTotal == 0.0 || nColTotal == 0.0 )
2597 0 : pAggData->SetError();
2598 : else
2599 : pAggData->SetResult(
2600 0 : ( pAggData->GetResult() * nGrandTotal ) /
2601 0 : ( nRowTotal * nColTotal ) );
2602 : }
2603 0 : break;
2604 : }
2605 1755 : }
2606 : }
2607 : }
2608 : }
2609 : }
2610 :
2611 1736 : if ( bHasChild ) // child dimension must be processed last, so the row total is known
2612 : {
2613 443 : if ( pDataChild )
2614 : pDataChild->UpdateRunningTotals( pRefChild, nMeasure,
2615 323 : bIsSubTotalRow, rSubState, rRunning, rTotals, rRowParent );
2616 : }
2617 1736 : }
2618 :
2619 : #if DEBUG_PIVOT_TABLE
2620 : void ScDPDataMember::DumpState( const ScDPResultMember* pRefMember, ScDocument* pDoc, ScAddress& rPos ) const
2621 : {
2622 : lcl_DumpRow( OUString("ScDPDataMember"), GetName(), &aAggregate, pDoc, rPos );
2623 : SCROW nStartRow = rPos.Row();
2624 :
2625 : const ScDPDataDimension* pDataChild = GetChildDimension();
2626 : const ScDPResultDimension* pRefChild = pRefMember->GetChildDimension();
2627 : if ( pDataChild && pRefChild )
2628 : pDataChild->DumpState( pRefChild, pDoc, rPos );
2629 :
2630 : lcl_Indent( pDoc, nStartRow, rPos );
2631 : }
2632 :
2633 : void ScDPDataMember::Dump(int nIndent) const
2634 : {
2635 : std::string aIndent(nIndent*2, ' ');
2636 : std::cout << aIndent << "-- data member '"
2637 : << (pResultMember ? pResultMember->GetName() : OUString()) << "'" << std::endl;
2638 : for (const ScDPAggData* pAgg = &aAggregate; pAgg; pAgg = pAgg->GetExistingChild())
2639 : pAgg->Dump(nIndent+1);
2640 :
2641 : if (pChildDimension)
2642 : pChildDimension->Dump(nIndent+1);
2643 : }
2644 : #endif
2645 :
2646 : // Helper class to select the members to include in
2647 : // ScDPResultDimension::InitFrom or LateInitFrom if groups are used
2648 :
2649 : class ScDPGroupCompare
2650 : {
2651 : private:
2652 : const ScDPResultData* pResultData;
2653 : const ScDPInitState& rInitState;
2654 : long nDimSource;
2655 : bool bIncludeAll;
2656 : bool bIsBase;
2657 : long nGroupBase;
2658 : public:
2659 : ScDPGroupCompare( const ScDPResultData* pData, const ScDPInitState& rState, long nDimension );
2660 255 : ~ScDPGroupCompare() {}
2661 :
2662 685 : bool IsIncluded( const ScDPMember& rMember ) { return bIncludeAll || TestIncluded( rMember ); }
2663 : bool TestIncluded( const ScDPMember& rMember );
2664 : };
2665 :
2666 255 : ScDPGroupCompare::ScDPGroupCompare( const ScDPResultData* pData, const ScDPInitState& rState, long nDimension ) :
2667 : pResultData( pData ),
2668 : rInitState( rState ),
2669 255 : nDimSource( nDimension )
2670 : {
2671 255 : bIsBase = pResultData->IsBaseForGroup( nDimSource );
2672 255 : nGroupBase = pResultData->GetGroupBase( nDimSource ); //TODO: get together in one call?
2673 :
2674 : // if bIncludeAll is set, TestIncluded doesn't need to be called
2675 255 : bIncludeAll = !( bIsBase || nGroupBase >= 0 );
2676 255 : }
2677 :
2678 79 : bool ScDPGroupCompare::TestIncluded( const ScDPMember& rMember )
2679 : {
2680 79 : bool bInclude = true;
2681 79 : if ( bIsBase )
2682 : {
2683 : // need to check all previous groups
2684 : //TODO: get array of groups (or indexes) before loop?
2685 45 : ScDPItemData aMemberData;
2686 45 : rMember.FillItemData( aMemberData );
2687 :
2688 45 : const std::vector<ScDPInitState::Member>& rMemStates = rInitState.GetMembers();
2689 45 : std::vector<ScDPInitState::Member>::const_iterator it = rMemStates.begin(), itEnd = rMemStates.end();
2690 104 : for (; it != itEnd && bInclude; ++it)
2691 : {
2692 59 : if (pResultData->GetGroupBase(it->mnSrcIndex) == nDimSource)
2693 : {
2694 : bInclude = pResultData->IsInGroup(
2695 59 : it->mnNameIndex, it->mnSrcIndex, aMemberData, nDimSource);
2696 : }
2697 45 : }
2698 : }
2699 34 : else if ( nGroupBase >= 0 )
2700 : {
2701 : // base isn't used in preceding fields
2702 : // -> look for other groups using the same base
2703 :
2704 : //TODO: get array of groups (or indexes) before loop?
2705 34 : ScDPItemData aMemberData;
2706 34 : rMember.FillItemData( aMemberData );
2707 34 : const std::vector<ScDPInitState::Member>& rMemStates = rInitState.GetMembers();
2708 34 : std::vector<ScDPInitState::Member>::const_iterator it = rMemStates.begin(), itEnd = rMemStates.end();
2709 42 : for (; it != itEnd && bInclude; ++it)
2710 : {
2711 8 : if (pResultData->GetGroupBase(it->mnSrcIndex) == nGroupBase)
2712 : {
2713 : // coverity[copy_paste_error] - same base (hierarchy between
2714 : // the two groups is irrelevant)
2715 : bInclude = pResultData->HasCommonElement(
2716 8 : it->mnNameIndex, it->mnSrcIndex, aMemberData, nDimSource);
2717 : }
2718 :
2719 34 : }
2720 : }
2721 :
2722 79 : return bInclude;
2723 : }
2724 :
2725 201 : ScDPResultDimension::ScDPResultDimension( const ScDPResultData* pData ) :
2726 : pResultData( pData ),
2727 : nSortMeasure( 0 ),
2728 : bIsDataLayout( false ),
2729 : bSortByData( false ),
2730 : bSortAscending( false ),
2731 : bAutoShow( false ),
2732 : bAutoTopItems( false ),
2733 : bInitialized( false ),
2734 : nAutoMeasure( 0 ),
2735 201 : nAutoCount( 0 )
2736 : {
2737 201 : }
2738 :
2739 390 : ScDPResultDimension::~ScDPResultDimension()
2740 : {
2741 1060 : for( int i = maMemberArray.size () ; i-- > 0 ; )
2742 670 : delete maMemberArray[i];
2743 195 : }
2744 :
2745 2325 : ScDPResultMember *ScDPResultDimension::FindMember( SCROW iData ) const
2746 : {
2747 2325 : if( bIsDataLayout )
2748 116 : return maMemberArray[0];
2749 :
2750 2209 : MemberHash::const_iterator aRes = maMemberHash.find( iData );
2751 2209 : if( aRes != maMemberHash.end()) {
2752 2180 : if ( aRes->second->IsNamedItem( iData ) )
2753 2180 : return aRes->second;
2754 : OSL_FAIL("problem! hash result is not the same as IsNamedItem");
2755 : }
2756 :
2757 : unsigned int i;
2758 29 : unsigned int nCount = maMemberArray.size();
2759 : ScDPResultMember* pResultMember;
2760 66 : for( i = 0; i < nCount ; i++ )
2761 : {
2762 37 : pResultMember = maMemberArray[i];
2763 37 : if ( pResultMember->IsNamedItem( iData ) )
2764 0 : return pResultMember;
2765 : }
2766 29 : return NULL;
2767 : }
2768 :
2769 4 : void ScDPResultDimension::InitFrom(
2770 : const vector<ScDPDimension*>& ppDim, const vector<ScDPLevel*>& ppLev,
2771 : size_t nPos, ScDPInitState& rInitState, bool bInitChild )
2772 : {
2773 4 : if (nPos >= ppDim.size() || nPos >= ppLev.size())
2774 : {
2775 0 : bInitialized = true;
2776 0 : return;
2777 : }
2778 :
2779 4 : ScDPDimension* pThisDim = ppDim[nPos];
2780 4 : ScDPLevel* pThisLevel = ppLev[nPos];
2781 :
2782 4 : if (!pThisDim || !pThisLevel)
2783 : {
2784 0 : bInitialized = true;
2785 0 : return;
2786 : }
2787 :
2788 4 : bIsDataLayout = pThisDim->getIsDataLayoutDimension(); // member
2789 4 : aDimensionName = pThisDim->getName(); // member
2790 :
2791 : // Check the autoshow setting. If it's enabled, store the settings.
2792 4 : const sheet::DataPilotFieldAutoShowInfo& rAutoInfo = pThisLevel->GetAutoShow();
2793 4 : if ( rAutoInfo.IsEnabled )
2794 : {
2795 0 : bAutoShow = true;
2796 0 : bAutoTopItems = ( rAutoInfo.ShowItemsMode == sheet::DataPilotFieldShowItemsMode::FROM_TOP );
2797 0 : nAutoMeasure = pThisLevel->GetAutoMeasure();
2798 0 : nAutoCount = rAutoInfo.ItemCount;
2799 : }
2800 :
2801 : // Check the sort info, and store the settings if appropriate.
2802 4 : const sheet::DataPilotFieldSortInfo& rSortInfo = pThisLevel->GetSortInfo();
2803 4 : if ( rSortInfo.Mode == sheet::DataPilotFieldSortMode::DATA )
2804 : {
2805 0 : bSortByData = true;
2806 0 : bSortAscending = rSortInfo.IsAscending;
2807 0 : nSortMeasure = pThisLevel->GetSortMeasure();
2808 : }
2809 :
2810 : // global order is used to initialize aMembers, so it doesn't have to be looked at later
2811 4 : const ScMemberSortOrder& rGlobalOrder = pThisLevel->GetGlobalOrder();
2812 :
2813 4 : long nDimSource = pThisDim->GetDimension(); //TODO: check GetSourceDim?
2814 4 : ScDPGroupCompare aCompare( pResultData, rInitState, nDimSource );
2815 :
2816 : // Now, go through all members and initialize them.
2817 4 : ScDPMembers* pMembers = pThisLevel->GetMembersObject();
2818 4 : long nMembCount = pMembers->getCount();
2819 20 : for ( long i=0; i<nMembCount; i++ )
2820 : {
2821 16 : long nSorted = rGlobalOrder.empty() ? i : rGlobalOrder[i];
2822 :
2823 16 : ScDPMember* pMember = pMembers->getByIndex(nSorted);
2824 16 : if ( aCompare.IsIncluded( *pMember ) )
2825 : {
2826 16 : ScDPParentDimData aData( i, pThisDim, pThisLevel, pMember);
2827 16 : ScDPResultMember* pNew = AddMember( aData );
2828 :
2829 16 : rInitState.AddMember(nDimSource, pNew->GetDataId());
2830 16 : pNew->InitFrom( ppDim, ppLev, nPos+1, rInitState, bInitChild );
2831 16 : rInitState.RemoveMember();
2832 : }
2833 : }
2834 4 : bInitialized = true;
2835 : }
2836 :
2837 805 : void ScDPResultDimension::LateInitFrom(
2838 : LateInitParams& rParams, const vector<SCROW>& pItemData, size_t nPos, ScDPInitState& rInitState)
2839 : {
2840 805 : if ( rParams.IsEnd( nPos ) )
2841 0 : return;
2842 : OSL_ENSURE( nPos <= pItemData.size(), OString::number(pItemData.size()).getStr() );
2843 805 : ScDPDimension* pThisDim = rParams.GetDim( nPos );
2844 805 : ScDPLevel* pThisLevel = rParams.GetLevel( nPos );
2845 805 : SCROW rThisData = pItemData[nPos];
2846 :
2847 805 : if (!pThisDim || !pThisLevel)
2848 0 : return;
2849 :
2850 805 : long nDimSource = pThisDim->GetDimension(); //TODO: check GetSourceDim?
2851 :
2852 805 : bool bShowEmpty = pThisLevel->getShowEmpty();
2853 :
2854 805 : if ( !bInitialized )
2855 : { // init some values
2856 : // create all members at the first call (preserve order)
2857 197 : bIsDataLayout = pThisDim->getIsDataLayoutDimension();
2858 197 : aDimensionName = pThisDim->getName();
2859 :
2860 197 : const sheet::DataPilotFieldAutoShowInfo& rAutoInfo = pThisLevel->GetAutoShow();
2861 197 : if ( rAutoInfo.IsEnabled )
2862 : {
2863 1 : bAutoShow = true;
2864 1 : bAutoTopItems = ( rAutoInfo.ShowItemsMode == sheet::DataPilotFieldShowItemsMode::FROM_TOP );
2865 1 : nAutoMeasure = pThisLevel->GetAutoMeasure();
2866 1 : nAutoCount = rAutoInfo.ItemCount;
2867 : }
2868 :
2869 197 : const sheet::DataPilotFieldSortInfo& rSortInfo = pThisLevel->GetSortInfo();
2870 197 : if ( rSortInfo.Mode == sheet::DataPilotFieldSortMode::DATA )
2871 : {
2872 0 : bSortByData = true;
2873 0 : bSortAscending = rSortInfo.IsAscending;
2874 0 : nSortMeasure = pThisLevel->GetSortMeasure();
2875 : }
2876 : }
2877 :
2878 805 : bool bLateInitAllMembers= bIsDataLayout || rParams.GetInitAllChild() || bShowEmpty;
2879 :
2880 805 : if ( !bLateInitAllMembers )
2881 : {
2882 747 : ResultMembers* pMembers = pResultData->GetDimResultMembers(nDimSource, pThisDim, pThisLevel);
2883 747 : bLateInitAllMembers = pMembers->IsHasHideDetailsMembers();
2884 : #if OSL_DEBUG_LEVEL > 1
2885 : OSL_TRACE( "%s", OUStringToOString(aDimensionName, RTL_TEXTENCODING_UTF8).getStr() );
2886 : if ( pMembers->IsHasHideDetailsMembers() )
2887 : OSL_TRACE( "HasHideDetailsMembers" );
2888 : #endif
2889 747 : pMembers->SetHasHideDetailsMembers( false );
2890 : }
2891 :
2892 805 : bool bNewAllMembers = (!rParams.IsRow()) || nPos == 0 || bLateInitAllMembers;
2893 :
2894 805 : if (bNewAllMembers )
2895 : {
2896 : // global order is used to initialize aMembers, so it doesn't have to be looked at later
2897 692 : if ( !bInitialized )
2898 : { //init all members
2899 138 : const ScMemberSortOrder& rGlobalOrder = pThisLevel->GetGlobalOrder();
2900 :
2901 138 : ScDPGroupCompare aCompare( pResultData, rInitState, nDimSource );
2902 138 : ScDPMembers* pMembers = pThisLevel->GetMembersObject();
2903 138 : long nMembCount = pMembers->getCount();
2904 719 : for ( long i=0; i<nMembCount; i++ )
2905 : {
2906 581 : long nSorted = rGlobalOrder.empty() ? i : rGlobalOrder[i];
2907 :
2908 581 : ScDPMember* pMember = pMembers->getByIndex(nSorted);
2909 581 : if ( aCompare.IsIncluded( *pMember ) )
2910 : { // add all members
2911 581 : ScDPParentDimData aData( i, pThisDim, pThisLevel, pMember );
2912 581 : AddMember( aData );
2913 : }
2914 : }
2915 138 : bInitialized = true; // don't call again, even if no members were included
2916 : }
2917 : // initialize only specific member (or all if "show empty" flag is set)
2918 692 : if ( bLateInitAllMembers )
2919 : {
2920 58 : long nCount = maMemberArray.size();
2921 174 : for (long i=0; i<nCount; i++)
2922 : {
2923 116 : ScDPResultMember* pResultMember = maMemberArray[i];
2924 :
2925 : // check show empty
2926 116 : bool bAllChildren = false;
2927 116 : if( bShowEmpty )
2928 : {
2929 88 : if ( pResultMember->IsNamedItem( rThisData ) )
2930 0 : bAllChildren = false;
2931 : else
2932 88 : bAllChildren = true;
2933 : }
2934 116 : rParams.SetInitAllChildren( bAllChildren );
2935 116 : rInitState.AddMember( nDimSource, pResultMember->GetDataId() );
2936 116 : pResultMember->LateInitFrom( rParams, pItemData, nPos+1, rInitState );
2937 116 : rInitState.RemoveMember();
2938 : }
2939 : }
2940 : else
2941 : {
2942 634 : ScDPResultMember* pResultMember = FindMember( rThisData );
2943 634 : if( NULL != pResultMember )
2944 : {
2945 634 : rInitState.AddMember( nDimSource, pResultMember->GetDataId() );
2946 634 : pResultMember->LateInitFrom( rParams, pItemData, nPos+1, rInitState );
2947 634 : rInitState.RemoveMember();
2948 : }
2949 : }
2950 : }
2951 : else
2952 113 : InitWithMembers( rParams, pItemData, nPos, rInitState );
2953 : }
2954 :
2955 1303 : long ScDPResultDimension::GetSize(long nMeasure) const
2956 : {
2957 1303 : long nTotal = 0;
2958 1303 : long nMemberCount = maMemberArray.size();
2959 1303 : if (bIsDataLayout)
2960 : {
2961 : OSL_ENSURE(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1,
2962 : "DataLayout dimension twice?");
2963 : // repeat first member...
2964 68 : nTotal = nMemberCount * maMemberArray[0]->GetSize(0); // all measures have equal size
2965 : }
2966 : else
2967 : {
2968 : // add all members
2969 5790 : for (long nMem=0; nMem<nMemberCount; nMem++)
2970 4555 : nTotal += maMemberArray[nMem]->GetSize(nMeasure);
2971 : }
2972 1303 : return nTotal;
2973 : }
2974 :
2975 821 : bool ScDPResultDimension::IsValidEntry( const vector< SCROW >& aMembers ) const
2976 : {
2977 821 : if (aMembers.empty())
2978 0 : return false;
2979 :
2980 821 : const ScDPResultMember* pMember = FindMember( aMembers[0] );
2981 821 : if ( NULL != pMember )
2982 821 : return pMember->IsValidEntry( aMembers );
2983 : #if OSL_DEBUG_LEVEL > 1
2984 : OStringBuffer strTemp("IsValidEntry: Member not found, DimName = ");
2985 : strTemp.append(OUStringToOString(GetName(), RTL_TEXTENCODING_UTF8));
2986 : OSL_TRACE("%s", strTemp.getStr());
2987 : #endif
2988 0 : return false;
2989 : }
2990 :
2991 816 : void ScDPResultDimension::ProcessData( const vector< SCROW >& aMembers,
2992 : const ScDPResultDimension* pDataDim,
2993 : const vector< SCROW >& aDataMembers,
2994 : const vector<ScDPValue>& aValues ) const
2995 : {
2996 816 : if (aMembers.empty())
2997 0 : return;
2998 :
2999 816 : ScDPResultMember* pMember = FindMember( aMembers[0] );
3000 816 : if ( NULL != pMember )
3001 : {
3002 816 : vector<SCROW> aChildMembers;
3003 816 : if (aMembers.size() > 1)
3004 : {
3005 139 : vector<SCROW>::const_iterator itr = aMembers.begin();
3006 139 : aChildMembers.insert(aChildMembers.begin(), ++itr, aMembers.end());
3007 : }
3008 816 : pMember->ProcessData( aChildMembers, pDataDim, aDataMembers, aValues );
3009 816 : return;
3010 : }
3011 :
3012 : OSL_FAIL("ProcessData: Member not found");
3013 : }
3014 :
3015 189 : void ScDPResultDimension::FillMemberResults( uno::Sequence<sheet::MemberResult>* pSequences,
3016 : long nStart, long nMeasure )
3017 : {
3018 189 : long nPos = nStart;
3019 189 : long nCount = maMemberArray.size();
3020 :
3021 847 : for (long i=0; i<nCount; i++)
3022 : {
3023 658 : long nSorted = aMemberOrder.empty() ? i : aMemberOrder[i];
3024 :
3025 658 : ScDPResultMember* pMember = maMemberArray[nSorted];
3026 : // in data layout dimension, use first member with different measures/names
3027 658 : if ( bIsDataLayout )
3028 : {
3029 22 : bool bTotalResult = false;
3030 22 : OUString aMbrName = pResultData->GetMeasureDimensionName( nSorted );
3031 44 : OUString aMbrCapt = pResultData->GetMeasureString( nSorted, false, SUBTOTAL_FUNC_NONE, bTotalResult );
3032 44 : maMemberArray[0]->FillMemberResults( pSequences, nPos, nSorted, false, &aMbrName, &aMbrCapt );
3033 : }
3034 636 : else if ( pMember->IsVisible() )
3035 : {
3036 568 : pMember->FillMemberResults( pSequences, nPos, nMeasure, false, NULL, NULL );
3037 : }
3038 : // nPos is modified
3039 : }
3040 189 : }
3041 :
3042 136 : void ScDPResultDimension::FillDataResults(
3043 : const ScDPResultMember* pRefMember, ScDPResultFilterContext& rFilterCxt,
3044 : uno::Sequence< uno::Sequence<sheet::DataResult> >& rSequence, long nMeasure) const
3045 : {
3046 136 : FilterStack aFilterStack(rFilterCxt.maFilters);
3047 136 : aFilterStack.pushDimName(GetName(), bIsDataLayout);
3048 :
3049 136 : long nMemberMeasure = nMeasure;
3050 136 : long nCount = maMemberArray.size();
3051 543 : for (long i=0; i<nCount; i++)
3052 : {
3053 407 : long nSorted = aMemberOrder.empty() ? i : aMemberOrder[i];
3054 :
3055 : const ScDPResultMember* pMember;
3056 407 : if (bIsDataLayout)
3057 : {
3058 : OSL_ENSURE(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1,
3059 : "DataLayout dimension twice?");
3060 16 : pMember = maMemberArray[0];
3061 16 : nMemberMeasure = nSorted;
3062 : }
3063 : else
3064 391 : pMember = maMemberArray[nSorted];
3065 :
3066 407 : if ( pMember->IsVisible() )
3067 399 : pMember->FillDataResults(pRefMember, rFilterCxt, rSequence, nMemberMeasure);
3068 136 : }
3069 136 : }
3070 :
3071 137 : void ScDPResultDimension::UpdateDataResults( const ScDPResultMember* pRefMember, long nMeasure ) const
3072 : {
3073 137 : long nMemberMeasure = nMeasure;
3074 137 : long nCount = maMemberArray.size();
3075 549 : for (long i=0; i<nCount; i++)
3076 : {
3077 : const ScDPResultMember* pMember;
3078 412 : if (bIsDataLayout)
3079 : {
3080 : OSL_ENSURE(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1,
3081 : "DataLayout dimension twice?");
3082 16 : pMember = maMemberArray[0];
3083 16 : nMemberMeasure = i;
3084 : }
3085 : else
3086 396 : pMember = maMemberArray[i];
3087 :
3088 412 : if ( pMember->IsVisible() )
3089 404 : pMember->UpdateDataResults( pRefMember, nMemberMeasure );
3090 : }
3091 137 : }
3092 :
3093 136 : void ScDPResultDimension::SortMembers( ScDPResultMember* pRefMember )
3094 : {
3095 136 : long nCount = maMemberArray.size();
3096 :
3097 136 : if ( bSortByData )
3098 : {
3099 : // sort members
3100 :
3101 : OSL_ENSURE( aMemberOrder.empty(), "sort twice?" );
3102 0 : aMemberOrder.resize( nCount );
3103 0 : for (long nPos=0; nPos<nCount; nPos++)
3104 0 : aMemberOrder[nPos] = nPos;
3105 :
3106 0 : ScDPRowMembersOrder aComp( *this, nSortMeasure, bSortAscending );
3107 0 : ::std::sort( aMemberOrder.begin(), aMemberOrder.end(), aComp );
3108 : }
3109 :
3110 : // handle children
3111 :
3112 : // for data layout, call only once - sorting measure is always taken from settings
3113 136 : long nLoopCount = bIsDataLayout ? 1 : nCount;
3114 535 : for (long i=0; i<nLoopCount; i++)
3115 : {
3116 399 : ScDPResultMember* pMember = maMemberArray[i];
3117 399 : if ( pMember->IsVisible() )
3118 391 : pMember->SortMembers( pRefMember );
3119 : }
3120 136 : }
3121 :
3122 1 : void ScDPResultDimension::DoAutoShow( ScDPResultMember* pRefMember )
3123 : {
3124 1 : long nCount = maMemberArray.size();
3125 :
3126 : // handle children first, before changing the visible state
3127 :
3128 : // for data layout, call only once - sorting measure is always taken from settings
3129 1 : long nLoopCount = bIsDataLayout ? 1 : nCount;
3130 6 : for (long i=0; i<nLoopCount; i++)
3131 : {
3132 5 : ScDPResultMember* pMember = maMemberArray[i];
3133 5 : if ( pMember->IsVisible() )
3134 5 : pMember->DoAutoShow( pRefMember );
3135 : }
3136 :
3137 1 : if ( bAutoShow && nAutoCount > 0 && nAutoCount < nCount )
3138 : {
3139 : // establish temporary order, hide remaining members
3140 :
3141 0 : ScMemberSortOrder aAutoOrder;
3142 0 : aAutoOrder.resize( nCount );
3143 : long nPos;
3144 0 : for (nPos=0; nPos<nCount; nPos++)
3145 0 : aAutoOrder[nPos] = nPos;
3146 :
3147 0 : ScDPRowMembersOrder aComp( *this, nAutoMeasure, !bAutoTopItems );
3148 0 : ::std::sort( aAutoOrder.begin(), aAutoOrder.end(), aComp );
3149 :
3150 : // look for equal values to the last included one
3151 :
3152 0 : long nIncluded = nAutoCount;
3153 0 : const ScDPResultMember* pMember1 = maMemberArray[aAutoOrder[nIncluded - 1]];
3154 0 : const ScDPDataMember* pDataMember1 = pMember1->IsVisible() ? pMember1->GetDataRoot() : NULL;
3155 0 : bool bContinue = true;
3156 0 : while ( bContinue )
3157 : {
3158 0 : bContinue = false;
3159 0 : if ( nIncluded < nCount )
3160 : {
3161 0 : const ScDPResultMember* pMember2 = maMemberArray[aAutoOrder[nIncluded]];
3162 0 : const ScDPDataMember* pDataMember2 = pMember2->IsVisible() ? pMember2->GetDataRoot() : NULL;
3163 :
3164 0 : if ( lcl_IsEqual( pDataMember1, pDataMember2, nAutoMeasure ) )
3165 : {
3166 0 : ++nIncluded; // include more members if values are equal
3167 0 : bContinue = true;
3168 : }
3169 : }
3170 : }
3171 :
3172 : // hide the remaining members
3173 :
3174 0 : for (nPos = nIncluded; nPos < nCount; nPos++)
3175 : {
3176 0 : ScDPResultMember* pMember = maMemberArray[aAutoOrder[nPos]];
3177 0 : pMember->SetAutoHidden();
3178 0 : }
3179 : }
3180 1 : }
3181 :
3182 2 : void ScDPResultDimension::ResetResults()
3183 : {
3184 2 : long nCount = maMemberArray.size();
3185 12 : for (long i=0; i<nCount; i++)
3186 : {
3187 : // sort order doesn't matter
3188 10 : ScDPResultMember* pMember = maMemberArray[bIsDataLayout ? 0 : i];
3189 10 : pMember->ResetResults();
3190 : }
3191 2 : }
3192 :
3193 28 : long ScDPResultDimension::GetSortedIndex( long nUnsorted ) const
3194 : {
3195 28 : return aMemberOrder.empty() ? nUnsorted : aMemberOrder[nUnsorted];
3196 : }
3197 :
3198 136 : void ScDPResultDimension::UpdateRunningTotals( const ScDPResultMember* pRefMember, long nMeasure,
3199 : ScDPRunningTotalState& rRunning, ScDPRowTotals& rTotals ) const
3200 : {
3201 : const ScDPResultMember* pMember;
3202 136 : long nMemberMeasure = nMeasure;
3203 136 : long nCount = maMemberArray.size();
3204 543 : for (long i=0; i<nCount; i++)
3205 : {
3206 407 : long nSorted = aMemberOrder.empty() ? i : aMemberOrder[i];
3207 :
3208 407 : if (bIsDataLayout)
3209 : {
3210 : OSL_ENSURE(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1,
3211 : "DataLayout dimension twice?");
3212 16 : pMember = maMemberArray[0];
3213 16 : nMemberMeasure = nSorted;
3214 : }
3215 : else
3216 391 : pMember = maMemberArray[nSorted];
3217 :
3218 407 : if ( pMember->IsVisible() )
3219 : {
3220 399 : if ( bIsDataLayout )
3221 16 : rRunning.AddRowIndex( 0, 0 );
3222 : else
3223 383 : rRunning.AddRowIndex( i, nSorted );
3224 399 : pMember->UpdateRunningTotals( pRefMember, nMemberMeasure, rRunning, rTotals );
3225 399 : rRunning.RemoveRowIndex();
3226 : }
3227 : }
3228 136 : }
3229 :
3230 16 : ScDPDataMember* ScDPResultDimension::GetRowReferenceMember(
3231 : const ScDPRelativePos* pRelativePos, const OUString* pName,
3232 : const long* pRowIndexes, const long* pColIndexes ) const
3233 : {
3234 : // get named, previous/next, or first member of this dimension (first existing if pRelativePos and pName are NULL)
3235 :
3236 : OSL_ENSURE( pRelativePos == NULL || pName == NULL, "can't use position and name" );
3237 :
3238 16 : ScDPDataMember* pColMember = NULL;
3239 :
3240 16 : bool bFirstExisting = ( pRelativePos == NULL && pName == NULL );
3241 16 : long nMemberCount = maMemberArray.size();
3242 16 : long nMemberIndex = 0; // unsorted
3243 16 : long nDirection = 1; // forward if no relative position is used
3244 16 : if ( pRelativePos )
3245 : {
3246 0 : nDirection = pRelativePos->nDirection;
3247 0 : nMemberIndex = pRelativePos->nBasePos + nDirection; // bounds are handled below
3248 :
3249 : OSL_ENSURE( nDirection == 1 || nDirection == -1, "Direction must be 1 or -1" );
3250 : }
3251 16 : else if ( pName )
3252 : {
3253 : // search for named member
3254 :
3255 12 : const ScDPResultMember* pRowMember = maMemberArray[GetSortedIndex(nMemberIndex)];
3256 :
3257 : //TODO: use ScDPItemData, as in ScDPDimension::IsValidPage?
3258 24 : while ( pRowMember && pRowMember->GetName() != *pName )
3259 : {
3260 0 : ++nMemberIndex;
3261 0 : if ( nMemberIndex < nMemberCount )
3262 0 : pRowMember = maMemberArray[GetSortedIndex(nMemberIndex)];
3263 : else
3264 0 : pRowMember = NULL;
3265 : }
3266 : }
3267 :
3268 16 : bool bContinue = true;
3269 48 : while ( bContinue && nMemberIndex >= 0 && nMemberIndex < nMemberCount )
3270 : {
3271 16 : const ScDPResultMember* pRowMember = maMemberArray[GetSortedIndex(nMemberIndex)];
3272 :
3273 : // get child members by given indexes
3274 :
3275 16 : const long* pNextRowIndex = pRowIndexes;
3276 32 : while ( *pNextRowIndex >= 0 && pRowMember )
3277 : {
3278 0 : const ScDPResultDimension* pRowChild = pRowMember->GetChildDimension();
3279 0 : if ( pRowChild && *pNextRowIndex < pRowChild->GetMemberCount() )
3280 0 : pRowMember = pRowChild->GetMember( *pNextRowIndex );
3281 : else
3282 0 : pRowMember = NULL;
3283 0 : ++pNextRowIndex;
3284 : }
3285 :
3286 16 : if ( pRowMember && pRelativePos )
3287 : {
3288 : // Skip the member if it has hidden details
3289 : // (because when looking for the details, it is skipped, too).
3290 : // Also skip if the member is invisible because it has no data,
3291 : // for consistent ordering.
3292 0 : if ( pRowMember->HasHiddenDetails() || !pRowMember->IsVisible() )
3293 0 : pRowMember = NULL;
3294 : }
3295 :
3296 16 : if ( pRowMember )
3297 : {
3298 16 : pColMember = pRowMember->GetDataRoot();
3299 :
3300 16 : const long* pNextColIndex = pColIndexes;
3301 32 : while ( *pNextColIndex >= 0 && pColMember )
3302 : {
3303 0 : ScDPDataDimension* pColChild = pColMember->GetChildDimension();
3304 0 : if ( pColChild && *pNextColIndex < pColChild->GetMemberCount() )
3305 0 : pColMember = pColChild->GetMember( *pNextColIndex );
3306 : else
3307 0 : pColMember = NULL;
3308 0 : ++pNextColIndex;
3309 : }
3310 : }
3311 :
3312 : // continue searching only if looking for first existing or relative position
3313 16 : bContinue = ( pColMember == NULL && ( bFirstExisting || pRelativePos ) );
3314 16 : nMemberIndex += nDirection;
3315 : }
3316 :
3317 16 : return pColMember;
3318 : }
3319 :
3320 0 : ScDPDataMember* ScDPResultDimension::GetColReferenceMember(
3321 : const ScDPRelativePos* pRelativePos, const OUString* pName,
3322 : long nRefDimPos, const ScDPRunningTotalState& rRunning )
3323 : {
3324 : OSL_ENSURE( pRelativePos == NULL || pName == NULL, "can't use position and name" );
3325 :
3326 0 : const long* pColIndexes = &rRunning.GetColSorted()[0];
3327 0 : const long* pRowIndexes = &rRunning.GetRowSorted()[0];
3328 :
3329 : // get own row member using all indexes
3330 :
3331 0 : const ScDPResultMember* pRowMember = rRunning.GetRowResRoot();
3332 0 : ScDPDataMember* pColMember = NULL;
3333 :
3334 0 : const long* pNextRowIndex = pRowIndexes;
3335 0 : while ( *pNextRowIndex >= 0 && pRowMember )
3336 : {
3337 0 : const ScDPResultDimension* pRowChild = pRowMember->GetChildDimension();
3338 0 : if ( pRowChild && *pNextRowIndex < pRowChild->GetMemberCount() )
3339 0 : pRowMember = pRowChild->GetMember( *pNextRowIndex );
3340 : else
3341 0 : pRowMember = NULL;
3342 0 : ++pNextRowIndex;
3343 : }
3344 :
3345 : // get column (data) members before the reference field
3346 : //TODO: pass rRowParent from ScDPDataMember::UpdateRunningTotals instead
3347 :
3348 0 : if ( pRowMember )
3349 : {
3350 0 : pColMember = pRowMember->GetDataRoot();
3351 :
3352 0 : const long* pNextColIndex = pColIndexes;
3353 0 : long nColSkipped = 0;
3354 0 : while ( *pNextColIndex >= 0 && pColMember && nColSkipped < nRefDimPos )
3355 : {
3356 0 : ScDPDataDimension* pColChild = pColMember->GetChildDimension();
3357 0 : if ( pColChild && *pNextColIndex < pColChild->GetMemberCount() )
3358 0 : pColMember = pColChild->GetMember( *pNextColIndex );
3359 : else
3360 0 : pColMember = NULL;
3361 0 : ++pNextColIndex;
3362 0 : ++nColSkipped;
3363 : }
3364 : }
3365 :
3366 : // get column member for the reference field
3367 :
3368 0 : if ( pColMember )
3369 : {
3370 0 : ScDPDataDimension* pReferenceDim = pColMember->GetChildDimension();
3371 0 : if ( pReferenceDim )
3372 : {
3373 0 : long nReferenceCount = pReferenceDim->GetMemberCount();
3374 :
3375 0 : bool bFirstExisting = ( pRelativePos == NULL && pName == NULL );
3376 0 : long nMemberIndex = 0; // unsorted
3377 0 : long nDirection = 1; // forward if no relative position is used
3378 0 : pColMember = NULL; // don't use parent dimension's member if none found
3379 0 : if ( pRelativePos )
3380 : {
3381 0 : nDirection = pRelativePos->nDirection;
3382 0 : nMemberIndex = pRelativePos->nBasePos + nDirection; // bounds are handled below
3383 : }
3384 0 : else if ( pName )
3385 : {
3386 : // search for named member
3387 :
3388 0 : pColMember = pReferenceDim->GetMember( pReferenceDim->GetSortedIndex( nMemberIndex ) );
3389 :
3390 : //TODO: use ScDPItemData, as in ScDPDimension::IsValidPage?
3391 0 : while ( pColMember && pColMember->GetName() != *pName )
3392 : {
3393 0 : ++nMemberIndex;
3394 0 : if ( nMemberIndex < nReferenceCount )
3395 0 : pColMember = pReferenceDim->GetMember( pReferenceDim->GetSortedIndex( nMemberIndex ) );
3396 : else
3397 0 : pColMember = NULL;
3398 : }
3399 : }
3400 :
3401 0 : bool bContinue = true;
3402 0 : while ( bContinue && nMemberIndex >= 0 && nMemberIndex < nReferenceCount )
3403 : {
3404 0 : pColMember = pReferenceDim->GetMember( pReferenceDim->GetSortedIndex( nMemberIndex ) );
3405 :
3406 : // get column members below the reference field
3407 :
3408 0 : const long* pNextColIndex = pColIndexes + nRefDimPos + 1;
3409 0 : while ( *pNextColIndex >= 0 && pColMember )
3410 : {
3411 0 : ScDPDataDimension* pColChild = pColMember->GetChildDimension();
3412 0 : if ( pColChild && *pNextColIndex < pColChild->GetMemberCount() )
3413 0 : pColMember = pColChild->GetMember( *pNextColIndex );
3414 : else
3415 0 : pColMember = NULL;
3416 0 : ++pNextColIndex;
3417 : }
3418 :
3419 0 : if ( pColMember && pRelativePos )
3420 : {
3421 : // Skip the member if it has hidden details
3422 : // (because when looking for the details, it is skipped, too).
3423 : // Also skip if the member is invisible because it has no data,
3424 : // for consistent ordering.
3425 0 : if ( pColMember->HasHiddenDetails() || !pColMember->IsVisible() )
3426 0 : pColMember = NULL;
3427 : }
3428 :
3429 : // continue searching only if looking for first existing or relative position
3430 0 : bContinue = ( pColMember == NULL && ( bFirstExisting || pRelativePos ) );
3431 0 : nMemberIndex += nDirection;
3432 : }
3433 : }
3434 : else
3435 0 : pColMember = NULL;
3436 : }
3437 :
3438 0 : return pColMember;
3439 : }
3440 :
3441 : #if DEBUG_PIVOT_TABLE
3442 : void ScDPResultDimension::DumpState( const ScDPResultMember* pRefMember, ScDocument* pDoc, ScAddress& rPos ) const
3443 : {
3444 : OUString aDimName = bIsDataLayout ? OUString("(data layout)") : OUString(GetName());
3445 : lcl_DumpRow( OUString("ScDPResultDimension"), aDimName, NULL, pDoc, rPos );
3446 :
3447 : SCROW nStartRow = rPos.Row();
3448 :
3449 : long nCount = bIsDataLayout ? 1 : maMemberArray.size();
3450 : for (long i=0; i<nCount; i++)
3451 : {
3452 : const ScDPResultMember* pMember = maMemberArray[i];
3453 : pMember->DumpState( pRefMember, pDoc, rPos );
3454 : }
3455 :
3456 : lcl_Indent( pDoc, nStartRow, rPos );
3457 : }
3458 :
3459 : void ScDPResultDimension::Dump(int nIndent) const
3460 : {
3461 : std::string aIndent(nIndent*2, ' ');
3462 : std::cout << aIndent << "-- dimension '" << GetName() << "'" << std::endl;
3463 : MemberArray::const_iterator it = maMemberArray.begin(), itEnd = maMemberArray.end();
3464 : for (; it != itEnd; ++it)
3465 : {
3466 : const ScDPResultMember* p = *it;
3467 : p->Dump(nIndent+1);
3468 : }
3469 : }
3470 : #endif
3471 :
3472 327 : long ScDPResultDimension::GetMemberCount() const
3473 : {
3474 327 : return maMemberArray.size();
3475 : }
3476 :
3477 5503 : const ScDPResultMember* ScDPResultDimension::GetMember(long n) const
3478 : {
3479 5503 : return maMemberArray[n];
3480 : }
3481 278 : ScDPResultMember* ScDPResultDimension::GetMember(long n)
3482 : {
3483 278 : return maMemberArray[n];
3484 : }
3485 :
3486 4 : ScDPResultDimension* ScDPResultDimension::GetFirstChildDimension() const
3487 : {
3488 4 : if ( maMemberArray.size() > 0 )
3489 4 : return maMemberArray[0]->GetChildDimension();
3490 : else
3491 0 : return NULL;
3492 : }
3493 :
3494 1596 : void ScDPResultDimension::FillVisibilityData(ScDPResultVisibilityData& rData) const
3495 : {
3496 1596 : if (IsDataLayout())
3497 1596 : return;
3498 :
3499 1596 : MemberArray::const_iterator itr = maMemberArray.begin(), itrEnd = maMemberArray.end();
3500 :
3501 6916 : for (;itr != itrEnd; ++itr)
3502 : {
3503 5320 : ScDPResultMember* pMember = *itr;
3504 5320 : if (pMember->IsValid())
3505 : {
3506 5320 : ScDPItemData aItem;
3507 5320 : pMember->FillItemData(aItem);
3508 5320 : rData.addVisibleMember(GetName(), aItem);
3509 5320 : pMember->FillVisibilityData(rData);
3510 : }
3511 : }
3512 : }
3513 :
3514 323 : ScDPDataDimension::ScDPDataDimension( const ScDPResultData* pData ) :
3515 : pResultData( pData ),
3516 : pResultDimension( NULL ),
3517 323 : bIsDataLayout( false )
3518 : {
3519 323 : }
3520 :
3521 636 : ScDPDataDimension::~ScDPDataDimension()
3522 : {
3523 318 : std::for_each(maMembers.begin(), maMembers.end(), boost::checked_deleter<ScDPDataMember>());
3524 318 : }
3525 :
3526 323 : void ScDPDataDimension::InitFrom( const ScDPResultDimension* pDim )
3527 : {
3528 323 : if (!pDim)
3529 323 : return;
3530 :
3531 323 : pResultDimension = pDim;
3532 323 : bIsDataLayout = pDim->IsDataLayout();
3533 :
3534 : // Go through all result members under the given result dimension, and
3535 : // create a new data member instance for each result member.
3536 323 : long nCount = pDim->GetMemberCount();
3537 1740 : for (long i=0; i<nCount; i++)
3538 : {
3539 1417 : const ScDPResultMember* pResMem = pDim->GetMember(i);
3540 :
3541 1417 : ScDPDataMember* pNew = new ScDPDataMember( pResultData, pResMem );
3542 1417 : maMembers.push_back( pNew);
3543 :
3544 1417 : if ( !pResultData->IsLateInit() )
3545 : {
3546 : // with LateInit, pResMem hasn't necessarily been initialized yet,
3547 : // so InitFrom for the new result member is called from its ProcessData method
3548 :
3549 0 : const ScDPResultDimension* pChildDim = pResMem->GetChildDimension();
3550 0 : if ( pChildDim )
3551 0 : pNew->InitFrom( pChildDim );
3552 : }
3553 : }
3554 : }
3555 :
3556 558 : void ScDPDataDimension::ProcessData( const vector< SCROW >& aDataMembers, const vector<ScDPValue>& aValues,
3557 : const ScDPSubTotalState& rSubState )
3558 : {
3559 : // the ScDPItemData array must contain enough entries for all dimensions - this isn't checked
3560 :
3561 558 : long nCount = maMembers.size();
3562 1448 : for (long i=0; i<nCount; i++)
3563 : {
3564 1448 : ScDPDataMember* pMember = maMembers[(sal_uInt16)i];
3565 :
3566 : // always first member for data layout dim
3567 1448 : if ( bIsDataLayout || ( !aDataMembers.empty() && pMember->IsNamedItem(aDataMembers[0]) ) )
3568 : {
3569 558 : vector<SCROW> aChildDataMembers;
3570 558 : if (aDataMembers.size() > 1)
3571 : {
3572 55 : vector<SCROW>::const_iterator itr = aDataMembers.begin();
3573 55 : aChildDataMembers.insert(aChildDataMembers.begin(), ++itr, aDataMembers.end());
3574 : }
3575 558 : pMember->ProcessData( aChildDataMembers, aValues, rSubState );
3576 1116 : return;
3577 : }
3578 : }
3579 :
3580 : OSL_FAIL("ProcessData: Member not found");
3581 : }
3582 :
3583 284 : void ScDPDataDimension::FillDataRow(
3584 : const ScDPResultDimension* pRefDim, ScDPResultFilterContext& rFilterCxt,
3585 : uno::Sequence<sheet::DataResult>& rSequence, long nMeasure, bool bIsSubTotalRow,
3586 : const ScDPSubTotalState& rSubState) const
3587 : {
3588 284 : OUString aDimName;
3589 284 : bool bDataLayout = false;
3590 284 : if (pResultDimension)
3591 : {
3592 284 : aDimName = pResultDimension->GetName();
3593 284 : bDataLayout = pResultDimension->IsDataLayout();
3594 : }
3595 :
3596 568 : FilterStack aFilterStack(rFilterCxt.maFilters);
3597 284 : aFilterStack.pushDimName(aDimName, bDataLayout);
3598 :
3599 : OSL_ENSURE( pRefDim && static_cast<size_t>(pRefDim->GetMemberCount()) == maMembers.size(), "dimensions don't match" );
3600 : OSL_ENSURE( pRefDim == pResultDimension, "wrong dim" );
3601 :
3602 284 : const ScMemberSortOrder& rMemberOrder = pRefDim->GetMemberOrder();
3603 :
3604 284 : long nMemberMeasure = nMeasure;
3605 284 : long nCount = maMembers.size();
3606 1506 : for (long i=0; i<nCount; i++)
3607 : {
3608 1222 : long nSorted = rMemberOrder.empty() ? i : rMemberOrder[i];
3609 :
3610 1222 : long nMemberPos = nSorted;
3611 1222 : if (bIsDataLayout)
3612 : {
3613 : OSL_ENSURE(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1,
3614 : "DataLayout dimension twice?");
3615 38 : nMemberPos = 0;
3616 38 : nMemberMeasure = nSorted;
3617 : }
3618 :
3619 1222 : const ScDPResultMember* pRefMember = pRefDim->GetMember(nMemberPos);
3620 1222 : if ( pRefMember->IsVisible() ) //TODO: here or in ScDPDataMember::FillDataRow ???
3621 : {
3622 1102 : const ScDPDataMember* pDataMember = maMembers[(sal_uInt16)nMemberPos];
3623 1102 : pDataMember->FillDataRow(pRefMember, rFilterCxt, rSequence, nMemberMeasure, bIsSubTotalRow, rSubState);
3624 : }
3625 284 : }
3626 284 : }
3627 :
3628 329 : void ScDPDataDimension::UpdateDataRow( const ScDPResultDimension* pRefDim,
3629 : long nMeasure, bool bIsSubTotalRow,
3630 : const ScDPSubTotalState& rSubState ) const
3631 : {
3632 : OSL_ENSURE( pRefDim && static_cast<size_t>(pRefDim->GetMemberCount()) == maMembers.size(), "dimensions don't match" );
3633 : OSL_ENSURE( pRefDim == pResultDimension, "wrong dim" );
3634 :
3635 329 : long nMemberMeasure = nMeasure;
3636 329 : long nCount = maMembers.size();
3637 1776 : for (long i=0; i<nCount; i++)
3638 : {
3639 1447 : long nMemberPos = i;
3640 1447 : if (bIsDataLayout)
3641 : {
3642 : OSL_ENSURE(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1,
3643 : "DataLayout dimension twice?");
3644 38 : nMemberPos = 0;
3645 38 : nMemberMeasure = i;
3646 : }
3647 :
3648 : // Calculate must be called even if the member is not visible (for use as reference value)
3649 1447 : const ScDPResultMember* pRefMember = pRefDim->GetMember(nMemberPos);
3650 1447 : ScDPDataMember* pDataMember = maMembers[(sal_uInt16)nMemberPos];
3651 1447 : pDataMember->UpdateDataRow( pRefMember, nMemberMeasure, bIsSubTotalRow, rSubState );
3652 : }
3653 329 : }
3654 :
3655 62 : void ScDPDataDimension::SortMembers( ScDPResultDimension* pRefDim )
3656 : {
3657 62 : long nCount = maMembers.size();
3658 :
3659 62 : if ( pRefDim->IsSortByData() )
3660 : {
3661 : // sort members
3662 :
3663 0 : ScMemberSortOrder& rMemberOrder = pRefDim->GetMemberOrder();
3664 : OSL_ENSURE( rMemberOrder.empty(), "sort twice?" );
3665 0 : rMemberOrder.resize( nCount );
3666 0 : for (long nPos=0; nPos<nCount; nPos++)
3667 0 : rMemberOrder[nPos] = nPos;
3668 :
3669 0 : ScDPColMembersOrder aComp( *this, pRefDim->GetSortMeasure(), pRefDim->IsSortAscending() );
3670 0 : ::std::sort( rMemberOrder.begin(), rMemberOrder.end(), aComp );
3671 : }
3672 :
3673 : // handle children
3674 :
3675 : OSL_ENSURE( pRefDim && static_cast<size_t>(pRefDim->GetMemberCount()) == maMembers.size(), "dimensions don't match" );
3676 : OSL_ENSURE( pRefDim == pResultDimension, "wrong dim" );
3677 :
3678 : // for data layout, call only once - sorting measure is always taken from settings
3679 62 : long nLoopCount = bIsDataLayout ? 1 : nCount;
3680 331 : for (long i=0; i<nLoopCount; i++)
3681 : {
3682 269 : ScDPResultMember* pRefMember = pRefDim->GetMember(i);
3683 269 : if ( pRefMember->IsVisible() ) //TODO: here or in ScDPDataMember ???
3684 : {
3685 209 : ScDPDataMember* pDataMember = maMembers[(sal_uInt16)i];
3686 209 : pDataMember->SortMembers( pRefMember );
3687 : }
3688 : }
3689 62 : }
3690 :
3691 1 : void ScDPDataDimension::DoAutoShow( ScDPResultDimension* pRefDim )
3692 : {
3693 1 : long nCount = maMembers.size();
3694 :
3695 : // handle children first, before changing the visible state
3696 :
3697 : OSL_ENSURE( pRefDim && static_cast<size_t>(pRefDim->GetMemberCount()) == maMembers.size(), "dimensions don't match" );
3698 : OSL_ENSURE( pRefDim == pResultDimension, "wrong dim" );
3699 :
3700 : // for data layout, call only once - sorting measure is always taken from settings
3701 1 : long nLoopCount = bIsDataLayout ? 1 : nCount;
3702 6 : for (long i=0; i<nLoopCount; i++)
3703 : {
3704 5 : ScDPResultMember* pRefMember = pRefDim->GetMember(i);
3705 5 : if ( pRefMember->IsVisible() ) //TODO: here or in ScDPDataMember ???
3706 : {
3707 5 : ScDPDataMember* pDataMember = maMembers[i];
3708 5 : pDataMember->DoAutoShow( pRefMember );
3709 : }
3710 : }
3711 :
3712 1 : if ( pRefDim->IsAutoShow() && pRefDim->GetAutoCount() > 0 && pRefDim->GetAutoCount() < nCount )
3713 : {
3714 : // establish temporary order, hide remaining members
3715 :
3716 0 : ScMemberSortOrder aAutoOrder;
3717 0 : aAutoOrder.resize( nCount );
3718 : long nPos;
3719 0 : for (nPos=0; nPos<nCount; nPos++)
3720 0 : aAutoOrder[nPos] = nPos;
3721 :
3722 0 : ScDPColMembersOrder aComp( *this, pRefDim->GetAutoMeasure(), !pRefDim->IsAutoTopItems() );
3723 0 : ::std::sort( aAutoOrder.begin(), aAutoOrder.end(), aComp );
3724 :
3725 : // look for equal values to the last included one
3726 :
3727 0 : long nIncluded = pRefDim->GetAutoCount();
3728 0 : ScDPDataMember* pDataMember1 = maMembers[aAutoOrder[nIncluded - 1]];
3729 0 : if ( !pDataMember1->IsVisible() )
3730 0 : pDataMember1 = NULL;
3731 0 : bool bContinue = true;
3732 0 : while ( bContinue )
3733 : {
3734 0 : bContinue = false;
3735 0 : if ( nIncluded < nCount )
3736 : {
3737 0 : ScDPDataMember* pDataMember2 = maMembers[aAutoOrder[nIncluded]];
3738 0 : if ( !pDataMember2->IsVisible() )
3739 0 : pDataMember2 = NULL;
3740 :
3741 0 : if ( lcl_IsEqual( pDataMember1, pDataMember2, pRefDim->GetAutoMeasure() ) )
3742 : {
3743 0 : ++nIncluded; // include more members if values are equal
3744 0 : bContinue = true;
3745 : }
3746 : }
3747 : }
3748 :
3749 : // hide the remaining members
3750 :
3751 0 : for (nPos = nIncluded; nPos < nCount; nPos++)
3752 : {
3753 0 : ScDPResultMember* pMember = pRefDim->GetMember(aAutoOrder[nPos]);
3754 0 : pMember->SetAutoHidden();
3755 0 : }
3756 : }
3757 1 : }
3758 :
3759 6 : void ScDPDataDimension::ResetResults()
3760 : {
3761 6 : long nCount = maMembers.size();
3762 36 : for (long i=0; i<nCount; i++)
3763 : {
3764 : // sort order doesn't matter
3765 :
3766 30 : long nMemberPos = bIsDataLayout ? 0 : i;
3767 30 : ScDPDataMember* pDataMember = maMembers[nMemberPos];
3768 30 : pDataMember->ResetResults();
3769 : }
3770 6 : }
3771 :
3772 0 : long ScDPDataDimension::GetSortedIndex( long nUnsorted ) const
3773 : {
3774 0 : if (!pResultDimension)
3775 0 : return nUnsorted;
3776 :
3777 0 : const ScMemberSortOrder& rMemberOrder = pResultDimension->GetMemberOrder();
3778 0 : return rMemberOrder.empty() ? nUnsorted : rMemberOrder[nUnsorted];
3779 : }
3780 :
3781 323 : void ScDPDataDimension::UpdateRunningTotals( const ScDPResultDimension* pRefDim,
3782 : long nMeasure, bool bIsSubTotalRow,
3783 : const ScDPSubTotalState& rSubState, ScDPRunningTotalState& rRunning,
3784 : ScDPRowTotals& rTotals, const ScDPResultMember& rRowParent ) const
3785 : {
3786 : OSL_ENSURE( pRefDim && static_cast<size_t>(pRefDim->GetMemberCount()) == maMembers.size(), "dimensions don't match" );
3787 : OSL_ENSURE( pRefDim == pResultDimension, "wrong dim" );
3788 :
3789 323 : long nMemberMeasure = nMeasure;
3790 323 : long nCount = maMembers.size();
3791 1740 : for (long i=0; i<nCount; i++)
3792 : {
3793 1417 : const ScMemberSortOrder& rMemberOrder = pRefDim->GetMemberOrder();
3794 1417 : long nSorted = rMemberOrder.empty() ? i : rMemberOrder[i];
3795 :
3796 1417 : long nMemberPos = nSorted;
3797 1417 : if (bIsDataLayout)
3798 : {
3799 : OSL_ENSURE(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1,
3800 : "DataLayout dimension twice?");
3801 38 : nMemberPos = 0;
3802 38 : nMemberMeasure = nSorted;
3803 : }
3804 :
3805 1417 : const ScDPResultMember* pRefMember = pRefDim->GetMember(nMemberPos);
3806 1417 : if ( pRefMember->IsVisible() )
3807 : {
3808 1237 : if ( bIsDataLayout )
3809 38 : rRunning.AddColIndex( 0, 0 );
3810 : else
3811 1199 : rRunning.AddColIndex( i, nSorted );
3812 :
3813 1237 : ScDPDataMember* pDataMember = maMembers[nMemberPos];
3814 : pDataMember->UpdateRunningTotals(
3815 1237 : pRefMember, nMemberMeasure, bIsSubTotalRow, rSubState, rRunning, rTotals, rRowParent);
3816 :
3817 1237 : rRunning.RemoveColIndex();
3818 : }
3819 : }
3820 323 : }
3821 :
3822 : #if DEBUG_PIVOT_TABLE
3823 : void ScDPDataDimension::DumpState( const ScDPResultDimension* pRefDim, ScDocument* pDoc, ScAddress& rPos ) const
3824 : {
3825 : OUString aDimName = bIsDataLayout ? OUString("(data layout)") : OUString("(unknown)");
3826 : lcl_DumpRow( OUString("ScDPDataDimension"), aDimName, NULL, pDoc, rPos );
3827 :
3828 : SCROW nStartRow = rPos.Row();
3829 :
3830 : long nCount = bIsDataLayout ? 1 : maMembers.size();
3831 : for (long i=0; i<nCount; i++)
3832 : {
3833 : const ScDPResultMember* pRefMember = pRefDim->GetMember(i);
3834 : const ScDPDataMember* pDataMember = maMembers[i];
3835 : pDataMember->DumpState( pRefMember, pDoc, rPos );
3836 : }
3837 :
3838 : lcl_Indent( pDoc, nStartRow, rPos );
3839 : }
3840 :
3841 : void ScDPDataDimension::Dump(int nIndent) const
3842 : {
3843 : std::string aIndent(nIndent*2, ' ');
3844 : std::cout << aIndent << "-- data dimension '"
3845 : << (pResultDimension ? pResultDimension->GetName() : OUString()) << "'" << std::endl;
3846 : ScDPDataMembers::const_iterator it = maMembers.begin(), itEnd = maMembers.end();
3847 : for (; it != itEnd; ++it)
3848 : (*it)->Dump(nIndent+1);
3849 : }
3850 : #endif
3851 :
3852 0 : long ScDPDataDimension::GetMemberCount() const
3853 : {
3854 0 : return maMembers.size();
3855 : }
3856 :
3857 0 : const ScDPDataMember* ScDPDataDimension::GetMember(long n) const
3858 : {
3859 0 : return maMembers[n];
3860 : }
3861 :
3862 0 : ScDPDataMember* ScDPDataDimension::GetMember(long n)
3863 : {
3864 0 : return maMembers[n];
3865 : }
3866 :
3867 133 : ScDPResultVisibilityData::ScDPResultVisibilityData(
3868 : ScDPSource* pSource) :
3869 133 : mpSource(pSource)
3870 : {
3871 133 : }
3872 :
3873 133 : ScDPResultVisibilityData::~ScDPResultVisibilityData()
3874 : {
3875 133 : }
3876 :
3877 5320 : void ScDPResultVisibilityData::addVisibleMember(const OUString& rDimName, const ScDPItemData& rMemberItem)
3878 : {
3879 5320 : DimMemberType::iterator itr = maDimensions.find(rDimName);
3880 5320 : if (itr == maDimensions.end())
3881 : {
3882 : pair<DimMemberType::iterator, bool> r = maDimensions.insert(
3883 532 : DimMemberType::value_type(rDimName, VisibleMemberType()));
3884 :
3885 532 : if (!r.second)
3886 : // insertion failed.
3887 5320 : return;
3888 :
3889 532 : itr = r.first;
3890 : }
3891 5320 : VisibleMemberType& rMem = itr->second;
3892 5320 : VisibleMemberType::iterator itrMem = rMem.find(rMemberItem);
3893 5320 : if (itrMem == rMem.end())
3894 2660 : rMem.insert(rMemberItem);
3895 : }
3896 :
3897 133 : void ScDPResultVisibilityData::fillFieldFilters(vector<ScDPFilteredCache::Criterion>& rFilters) const
3898 : {
3899 : typedef std::unordered_map<OUString, long, OUStringHash> FieldNameMapType;
3900 133 : FieldNameMapType aFieldNames;
3901 133 : ScDPTableData* pData = mpSource->GetData();
3902 133 : long nColumnCount = pData->GetColumnCount();
3903 798 : for (long i = 0; i < nColumnCount; ++i)
3904 : {
3905 : aFieldNames.insert(
3906 665 : FieldNameMapType::value_type(pData->getDimensionName(i), i));
3907 : }
3908 :
3909 133 : const ScDPDimensions* pDims = mpSource->GetDimensionsObject();
3910 665 : for (DimMemberType::const_iterator itr = maDimensions.begin(), itrEnd = maDimensions.end();
3911 : itr != itrEnd; ++itr)
3912 : {
3913 532 : const OUString& rDimName = itr->first;
3914 532 : ScDPFilteredCache::Criterion aCri;
3915 532 : FieldNameMapType::const_iterator itrField = aFieldNames.find(rDimName);
3916 532 : if (itrField == aFieldNames.end())
3917 : // This should never happen!
3918 0 : continue;
3919 :
3920 532 : long nDimIndex = itrField->second;
3921 532 : aCri.mnFieldIndex = static_cast<sal_Int32>(nDimIndex);
3922 532 : aCri.mpFilter.reset(new ScDPFilteredCache::GroupFilter);
3923 :
3924 : ScDPFilteredCache::GroupFilter* pGrpFilter =
3925 532 : static_cast<ScDPFilteredCache::GroupFilter*>(aCri.mpFilter.get());
3926 :
3927 532 : const VisibleMemberType& rMem = itr->second;
3928 3192 : for (VisibleMemberType::const_iterator itrMem = rMem.begin(), itrMemEnd = rMem.end();
3929 : itrMem != itrMemEnd; ++itrMem)
3930 : {
3931 2660 : const ScDPItemData& rMemItem = *itrMem;
3932 2660 : pGrpFilter->addMatchItem(rMemItem);
3933 : }
3934 :
3935 532 : ScDPDimension* pDim = pDims->getByIndex(nDimIndex);
3936 : ScDPMembers* pMembers = pDim->GetHierarchiesObject()->getByIndex(0)->
3937 532 : GetLevelsObject()->getByIndex(0)->GetMembersObject();
3938 532 : if (pGrpFilter->getMatchItemCount() < static_cast<size_t>(pMembers->getCount()))
3939 0 : rFilters.push_back(aCri);
3940 665 : }
3941 133 : }
3942 :
3943 7980 : size_t ScDPResultVisibilityData::MemberHash::operator() (const ScDPItemData& r) const
3944 : {
3945 7980 : if (r.IsValue())
3946 7980 : return static_cast<size_t>(::rtl::math::approxFloor(r.GetValue()));
3947 : else
3948 0 : return r.GetString().hashCode();
3949 : }
3950 1476 : SCROW ScDPResultMember::GetDataId( ) const
3951 : {
3952 1476 : const ScDPMember* pMemberDesc = GetDPMember();
3953 1476 : if (pMemberDesc)
3954 1476 : return pMemberDesc->GetItemDataId();
3955 0 : return -1;
3956 : }
3957 :
3958 597 : ScDPResultMember* ScDPResultDimension::AddMember(const ScDPParentDimData &aData )
3959 : {
3960 597 : ScDPResultMember* pMember = new ScDPResultMember( pResultData, aData, false );
3961 597 : SCROW nDataIndex = pMember->GetDataId();
3962 597 : maMemberArray.push_back( pMember );
3963 :
3964 597 : if ( maMemberHash.end() == maMemberHash.find( nDataIndex ) )
3965 586 : maMemberHash.insert( std::pair< SCROW, ScDPResultMember *>( nDataIndex, pMember ) );
3966 597 : return pMember;
3967 : }
3968 :
3969 88 : ScDPResultMember* ScDPResultDimension::InsertMember(ScDPParentDimData *pMemberData)
3970 : {
3971 88 : SCROW nInsert = 0;
3972 88 : if ( !lcl_SearchMember( maMemberArray, pMemberData->mnOrder , nInsert ) )
3973 : {
3974 88 : ScDPResultMember* pNew = new ScDPResultMember( pResultData, *pMemberData, false );
3975 88 : maMemberArray.insert( maMemberArray.begin()+nInsert, pNew );
3976 :
3977 88 : SCROW nDataIndex = pMemberData->mpMemberDesc->GetItemDataId();
3978 88 : if ( maMemberHash.end() == maMemberHash.find( nDataIndex ) )
3979 88 : maMemberHash.insert( std::pair< SCROW, ScDPResultMember *>( nDataIndex, pNew ) );
3980 88 : return pNew;
3981 : }
3982 0 : return maMemberArray[ nInsert ];
3983 : }
3984 :
3985 113 : void ScDPResultDimension::InitWithMembers(
3986 : LateInitParams& rParams, const std::vector<SCROW>& pItemData, size_t nPos,
3987 : ScDPInitState& rInitState)
3988 : {
3989 113 : if ( rParams.IsEnd( nPos ) )
3990 113 : return;
3991 113 : ScDPDimension* pThisDim = rParams.GetDim( nPos );
3992 113 : ScDPLevel* pThisLevel = rParams.GetLevel( nPos );
3993 113 : SCROW nDataID = pItemData[nPos];
3994 :
3995 113 : if (pThisDim && pThisLevel)
3996 : {
3997 113 : long nDimSource = pThisDim->GetDimension(); //TODO: check GetSourceDim?
3998 :
3999 : // create all members at the first call (preserve order)
4000 113 : ResultMembers* pMembers = pResultData->GetDimResultMembers(nDimSource, pThisDim, pThisLevel);
4001 113 : ScDPGroupCompare aCompare( pResultData, rInitState, nDimSource );
4002 : // initialize only specific member (or all if "show empty" flag is set)
4003 113 : ScDPResultMember* pResultMember = NULL;
4004 113 : if ( bInitialized )
4005 54 : pResultMember = FindMember( nDataID );
4006 : else
4007 59 : bInitialized = true;
4008 :
4009 113 : if ( pResultMember == NULL )
4010 : { //only insert found item
4011 88 : ScDPParentDimData* pMemberData = pMembers->FindMember( nDataID );
4012 88 : if ( pMemberData && aCompare.IsIncluded( *( pMemberData->mpMemberDesc ) ) )
4013 88 : pResultMember = InsertMember( pMemberData );
4014 : }
4015 113 : if ( pResultMember )
4016 : {
4017 113 : rInitState.AddMember( nDimSource, pResultMember->GetDataId() );
4018 113 : pResultMember->LateInitFrom(rParams, pItemData, nPos+1, rInitState);
4019 113 : rInitState.RemoveMember();
4020 113 : }
4021 : }
4022 : }
4023 :
4024 182 : ScDPParentDimData::ScDPParentDimData() :
4025 182 : mnOrder(-1), mpParentDim(NULL), mpParentLevel(NULL), mpMemberDesc(NULL) {}
4026 :
4027 1329 : ScDPParentDimData::ScDPParentDimData(
4028 : SCROW nIndex, const ScDPDimension* pDim, const ScDPLevel* pLev, const ScDPMember* pMember) :
4029 1329 : mnOrder(nIndex), mpParentDim(pDim), mpParentLevel(pLev), mpMemberDesc(pMember) {}
4030 :
4031 820 : ScDPParentDimData* ResultMembers::FindMember( SCROW nIndex ) const
4032 : {
4033 820 : DimMemberHash::const_iterator aRes = maMemberHash.find( nIndex );
4034 820 : if( aRes != maMemberHash.end()) {
4035 88 : if ( aRes->second->mpMemberDesc && aRes->second->mpMemberDesc->GetItemDataId()==nIndex )
4036 88 : return aRes->second;
4037 : }
4038 732 : return NULL;
4039 : }
4040 732 : void ResultMembers::InsertMember( ScDPParentDimData* pNew )
4041 : {
4042 732 : if ( !pNew->mpMemberDesc->getShowDetails() )
4043 0 : mbHasHideDetailsMember = true;
4044 732 : maMemberHash.insert( std::pair< const SCROW, ScDPParentDimData *>( pNew->mpMemberDesc->GetItemDataId(), pNew ) );
4045 732 : }
4046 :
4047 164 : ResultMembers::ResultMembers():
4048 164 : mbHasHideDetailsMember( false )
4049 : {
4050 164 : }
4051 384 : ResultMembers::~ResultMembers()
4052 : {
4053 718 : for ( DimMemberHash::const_iterator iter = maMemberHash.begin(); iter != maMemberHash.end(); ++iter )
4054 590 : delete iter->second;
4055 256 : }
4056 :
4057 982 : LateInitParams::LateInitParams(
4058 : const vector<ScDPDimension*>& ppDim, const vector<ScDPLevel*>& ppLev, bool bRow, bool bInitChild, bool bAllChildren ) :
4059 : mppDim( ppDim ),
4060 : mppLev( ppLev ),
4061 : mbRow( bRow ),
4062 : mbInitChild( bInitChild ),
4063 982 : mbAllChildren( bAllChildren )
4064 : {
4065 982 : }
4066 :
4067 982 : LateInitParams::~LateInitParams()
4068 : {
4069 982 : }
4070 :
4071 2731 : bool LateInitParams::IsEnd( size_t nPos ) const
4072 : {
4073 2731 : return nPos >= mppDim.size();
4074 : }
4075 :
4076 198 : void ScDPResultDimension::CheckShowEmpty( bool bShow )
4077 : {
4078 198 : long nCount = maMemberArray.size();
4079 :
4080 198 : ScDPResultMember* pMember = NULL;
4081 877 : for (long i=0; i<nCount; i++)
4082 : {
4083 679 : pMember = maMemberArray.at(i);
4084 679 : pMember->CheckShowEmpty(bShow);
4085 : }
4086 :
4087 198 : }
4088 :
4089 861 : void ScDPResultMember::CheckShowEmpty( bool bShow )
4090 : {
4091 861 : if (bHasElements)
4092 : {
4093 782 : ScDPResultDimension* pChildDim = GetChildDimension();
4094 782 : if (pChildDim)
4095 198 : pChildDim->CheckShowEmpty();
4096 : }
4097 79 : else if (IsValid() && bInitialized)
4098 : {
4099 11 : bShow = bShow || (GetParentLevel() && GetParentLevel()->getShowEmpty());
4100 11 : if (bShow)
4101 : {
4102 9 : SetHasElements();
4103 9 : ScDPResultDimension* pChildDim = GetChildDimension();
4104 9 : if (pChildDim)
4105 0 : pChildDim->CheckShowEmpty(true);
4106 : }
4107 : }
4108 1017 : }
4109 :
4110 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|