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 <dptabsrc.hxx>
21
22 #include <algorithm>
23 #include <vector>
24
25 #include <comphelper/sequence.hxx>
26 #include <o3tl/any.hxx>
27 #include <o3tl/safeint.hxx>
28 #include <osl/diagnose.h>
29 #include <rtl/math.hxx>
30 #include <sal/log.hxx>
31 #include <svl/itemprop.hxx>
32 #include <vcl/svapp.hxx>
33
34 #include <dpcache.hxx>
35 #include <dptabres.hxx>
36 #include <dptabdat.hxx>
37 #include <global.hxx>
38 #include <miscuno.hxx>
39 #include <unonames.hxx>
40 #include <dpitemdata.hxx>
41 #include <dputil.hxx>
42 #include <dpresfilter.hxx>
43 #include <calcmacros.hxx>
44 #include <generalfunction.hxx>
45
46 #include <com/sun/star/beans/PropertyAttribute.hpp>
47 #include <com/sun/star/sheet/DataPilotFieldFilter.hpp>
48 #include <com/sun/star/sheet/DataPilotFieldReferenceType.hpp>
49 #include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
50 #include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
51 #include <com/sun/star/sheet/DataPilotFieldAutoShowInfo.hpp>
52 #include <com/sun/star/sheet/GeneralFunction2.hpp>
53 #include <com/sun/star/sheet/TableFilterField.hpp>
54
55 #include <unotools/calendarwrapper.hxx>
56 #include <com/sun/star/i18n/CalendarDisplayIndex.hpp>
57
58 using namespace com::sun::star;
59 using ::std::vector;
60 using ::com::sun::star::uno::Sequence;
61 using ::com::sun::star::uno::Any;
62 using ::com::sun::star::sheet::DataPilotFieldAutoShowInfo;
63
64 #define SC_MINCOUNT_LIMIT 1000000
65
66 SC_SIMPLE_SERVICE_INFO( ScDPSource, "ScDPSource", "com.sun.star.sheet.DataPilotSource" )
67 SC_SIMPLE_SERVICE_INFO( ScDPDimensions, "ScDPDimensions", "com.sun.star.sheet.DataPilotSourceDimensions" )
68 SC_SIMPLE_SERVICE_INFO( ScDPDimension, "ScDPDimension", "com.sun.star.sheet.DataPilotSourceDimension" )
69
70 // Typos are on purpose here, quote from Eike Rathke (see https://gerrit.libreoffice.org/c/core/+/101116):
71 // "The typo is exactly why the SC_SIMPLE_SERVICE_INFO_COMPAT() lists both service names,
72 // the old with the typo and the new corrected one. Correcting the typo in the old name
73 // will make all extensions fail that use it. This is not to be changed."
74 SC_SIMPLE_SERVICE_INFO_COMPAT( ScDPHierarchies, "ScDPHierarchies",
75 "com.sun.star.sheet.DataPilotSourceHierarchies", "com.sun.star.sheet.DataPilotSourceHierarcies" )
76 SC_SIMPLE_SERVICE_INFO_COMPAT( ScDPHierarchy, "ScDPHierarchy",
77 "com.sun.star.sheet.DataPilotSourceHierarchy", "com.sun.star.sheet.DataPilotSourceHierarcy" )
78
79 SC_SIMPLE_SERVICE_INFO( ScDPLevels, "ScDPLevels", "com.sun.star.sheet.DataPilotSourceLevels" )
80 SC_SIMPLE_SERVICE_INFO( ScDPLevel, "ScDPLevel", "com.sun.star.sheet.DataPilotSourceLevel" )
81 SC_SIMPLE_SERVICE_INFO( ScDPMembers, "ScDPMembers", "com.sun.star.sheet.DataPilotSourceMembers" )
82 SC_SIMPLE_SERVICE_INFO( ScDPMember, "ScDPMember", "com.sun.star.sheet.DataPilotSourceMember" )
83
84 // property maps for PropertySetInfo
85 // DataDescription / NumberFormat are internal
86
87 //TODO: move to a header?
lcl_GetBoolFromAny(const uno::Any & aAny)88 static bool lcl_GetBoolFromAny( const uno::Any& aAny )
89 {
90 auto b = o3tl::tryAccess<bool>(aAny);
91 return b && *b;
92 }
93
ScDPSource(ScDPTableData * pD)94 ScDPSource::ScDPSource( ScDPTableData* pD ) :
95 pData( pD ),
96 bColumnGrand( true ), // default is true
97 bRowGrand( true ),
98 bIgnoreEmptyRows( false ),
99 bRepeatIfEmpty( false ),
100 nDupCount( 0 ),
101 bResultOverflow( false ),
102 bPageFiltered( false )
103 {
104 pData->SetEmptyFlags( bIgnoreEmptyRows, bRepeatIfEmpty );
105 }
106
~ScDPSource()107 ScDPSource::~ScDPSource()
108 {
109 // free lists
110
111 pColResults.reset();
112 pRowResults.reset();
113
114 pColResRoot.reset();
115 pRowResRoot.reset();
116 pResData.reset();
117 }
118
GetGrandTotalName() const119 const std::optional<OUString> & ScDPSource::GetGrandTotalName() const
120 {
121 return mpGrandTotalName;
122 }
123
GetOrientation(sal_Int32 nColumn)124 sheet::DataPilotFieldOrientation ScDPSource::GetOrientation(sal_Int32 nColumn)
125 {
126 if (std::find(maColDims.begin(), maColDims.end(), nColumn) != maColDims.end())
127 return sheet::DataPilotFieldOrientation_COLUMN;
128
129 if (std::find(maRowDims.begin(), maRowDims.end(), nColumn) != maRowDims.end())
130 return sheet::DataPilotFieldOrientation_ROW;
131
132 if (std::find(maDataDims.begin(), maDataDims.end(), nColumn) != maDataDims.end())
133 return sheet::DataPilotFieldOrientation_DATA;
134
135 if (std::find(maPageDims.begin(), maPageDims.end(), nColumn) != maPageDims.end())
136 return sheet::DataPilotFieldOrientation_PAGE;
137
138 return sheet::DataPilotFieldOrientation_HIDDEN;
139 }
140
GetDataDimensionCount() const141 sal_Int32 ScDPSource::GetDataDimensionCount() const
142 {
143 return maDataDims.size();
144 }
145
GetDataDimension(sal_Int32 nIndex)146 ScDPDimension* ScDPSource::GetDataDimension(sal_Int32 nIndex)
147 {
148 if (nIndex < 0 || o3tl::make_unsigned(nIndex) >= maDataDims.size())
149 return nullptr;
150
151 sal_Int32 nDimIndex = maDataDims[nIndex];
152 return GetDimensionsObject()->getByIndex(nDimIndex);
153 }
154
GetDataDimName(sal_Int32 nIndex)155 OUString ScDPSource::GetDataDimName(sal_Int32 nIndex)
156 {
157 OUString aRet;
158 ScDPDimension* pDim = GetDataDimension(nIndex);
159 if (pDim)
160 aRet = pDim->getName();
161 return aRet;
162 }
163
GetPosition(sal_Int32 nColumn)164 sal_Int32 ScDPSource::GetPosition(sal_Int32 nColumn)
165 {
166 std::vector<sal_Int32>::const_iterator it, itBeg = maColDims.begin(), itEnd = maColDims.end();
167 it = std::find(itBeg, itEnd, nColumn);
168 if (it != itEnd)
169 return std::distance(itBeg, it);
170
171 itBeg = maRowDims.begin();
172 itEnd = maRowDims.end();
173 it = std::find(itBeg, itEnd, nColumn);
174 if (it != itEnd)
175 return std::distance(itBeg, it);
176
177 itBeg = maDataDims.begin();
178 itEnd = maDataDims.end();
179 it = std::find(itBeg, itEnd, nColumn);
180 if (it != itEnd)
181 return std::distance(itBeg, it);
182
183 itBeg = maPageDims.begin();
184 itEnd = maPageDims.end();
185 it = std::find(itBeg, itEnd, nColumn);
186 if (it != itEnd)
187 return std::distance(itBeg, it);
188
189 return 0;
190 }
191
192 namespace {
193
testSubTotal(bool & rAllowed,sal_Int32 nColumn,const std::vector<sal_Int32> & rDims,ScDPSource * pSource)194 bool testSubTotal( bool& rAllowed, sal_Int32 nColumn, const std::vector<sal_Int32>& rDims, ScDPSource* pSource )
195 {
196 rAllowed = true;
197 std::vector<sal_Int32>::const_iterator it = rDims.begin(), itEnd = rDims.end();
198 for (; it != itEnd; ++it)
199 {
200 if (*it != nColumn)
201 continue;
202
203 if ( pSource->IsDataLayoutDimension(nColumn) )
204 {
205 // no subtotals for data layout dim, no matter where
206 rAllowed = false;
207 return true;
208 }
209
210 // no subtotals if no other dim but data layout follows
211 ++it;
212 if (it != itEnd && pSource->IsDataLayoutDimension(*it))
213 ++it;
214 if (it == itEnd)
215 rAllowed = false;
216
217 return true; // found
218 }
219
220 return false;
221 }
222
removeDim(sal_Int32 nRemove,std::vector<sal_Int32> & rDims)223 void removeDim( sal_Int32 nRemove, std::vector<sal_Int32>& rDims )
224 {
225 std::vector<sal_Int32>::iterator it = std::find(rDims.begin(), rDims.end(), nRemove);
226 if (it != rDims.end())
227 rDims.erase(it);
228 }
229
230 }
231
SubTotalAllowed(sal_Int32 nColumn)232 bool ScDPSource::SubTotalAllowed(sal_Int32 nColumn)
233 {
234 //TODO: cache this at ScDPResultData
235 bool bAllowed = true;
236 if ( testSubTotal(bAllowed, nColumn, maColDims, this) )
237 return bAllowed;
238 if ( testSubTotal(bAllowed, nColumn, maRowDims, this) )
239 return bAllowed;
240 return bAllowed;
241 }
242
SetOrientation(sal_Int32 nColumn,sheet::DataPilotFieldOrientation nNew)243 void ScDPSource::SetOrientation(sal_Int32 nColumn, sheet::DataPilotFieldOrientation nNew)
244 {
245 //TODO: change to no-op if new orientation is equal to old?
246
247 // remove from old list
248 removeDim(nColumn, maColDims);
249 removeDim(nColumn, maRowDims);
250 removeDim(nColumn, maDataDims);
251 removeDim(nColumn, maPageDims);
252
253 // add to new list
254 switch (nNew)
255 {
256 case sheet::DataPilotFieldOrientation_COLUMN:
257 maColDims.push_back(nColumn);
258 break;
259 case sheet::DataPilotFieldOrientation_ROW:
260 maRowDims.push_back(nColumn);
261 break;
262 case sheet::DataPilotFieldOrientation_DATA:
263 maDataDims.push_back(nColumn);
264 break;
265 case sheet::DataPilotFieldOrientation_PAGE:
266 maPageDims.push_back(nColumn);
267 break;
268 // DataPilot Migration - Cache&&Performance
269 case sheet::DataPilotFieldOrientation_HIDDEN:
270 break;
271 default:
272 OSL_FAIL( "ScDPSource::SetOrientation: unexpected orientation" );
273 break;
274 }
275 }
276
IsDataLayoutDimension(sal_Int32 nDim)277 bool ScDPSource::IsDataLayoutDimension(sal_Int32 nDim)
278 {
279 return nDim == pData->GetColumnCount();
280 }
281
GetDataLayoutOrientation()282 sheet::DataPilotFieldOrientation ScDPSource::GetDataLayoutOrientation()
283 {
284 return GetOrientation(pData->GetColumnCount());
285 }
286
IsDateDimension(sal_Int32 nDim)287 bool ScDPSource::IsDateDimension(sal_Int32 nDim)
288 {
289 return pData->IsDateDimension(nDim);
290 }
291
GetDimensionsObject()292 ScDPDimensions* ScDPSource::GetDimensionsObject()
293 {
294 if (!pDimensions.is())
295 {
296 pDimensions = new ScDPDimensions(this);
297 }
298 return pDimensions.get();
299 }
300
getDimensions()301 uno::Reference<container::XNameAccess> SAL_CALL ScDPSource::getDimensions()
302 {
303 return GetDimensionsObject();
304 }
305
SetDupCount(tools::Long nNew)306 void ScDPSource::SetDupCount( tools::Long nNew )
307 {
308 nDupCount = nNew;
309 }
310
AddDuplicated(std::u16string_view rNewName)311 ScDPDimension* ScDPSource::AddDuplicated(std::u16string_view rNewName)
312 {
313 OSL_ENSURE( pDimensions.is(), "AddDuplicated without dimensions?" );
314
315 // re-use
316
317 tools::Long nOldDimCount = pDimensions->getCount();
318 for (tools::Long i=0; i<nOldDimCount; i++)
319 {
320 ScDPDimension* pDim = pDimensions->getByIndex(i);
321 if (pDim && pDim->getName() == rNewName)
322 {
323 //TODO: test if pDim is a duplicate of source
324 return pDim;
325 }
326 }
327
328 SetDupCount( nDupCount + 1 );
329 pDimensions->CountChanged(); // uses nDupCount
330
331 return pDimensions->getByIndex( pDimensions->getCount() - 1 );
332 }
333
GetSourceDim(sal_Int32 nDim)334 sal_Int32 ScDPSource::GetSourceDim(sal_Int32 nDim)
335 {
336 // original source dimension or data layout dimension?
337 if ( nDim <= pData->GetColumnCount() )
338 return nDim;
339
340 if ( nDim < pDimensions->getCount() )
341 {
342 ScDPDimension* pDimObj = pDimensions->getByIndex( nDim );
343 if ( pDimObj )
344 {
345 tools::Long nSource = pDimObj->GetSourceDim();
346 if ( nSource >= 0 )
347 return nSource;
348 }
349 }
350
351 OSL_FAIL("GetSourceDim: wrong dim");
352 return nDim;
353 }
354
getResults()355 uno::Sequence< uno::Sequence<sheet::DataResult> > SAL_CALL ScDPSource::getResults()
356 {
357 CreateRes_Impl(); // create pColResRoot and pRowResRoot
358
359 if ( bResultOverflow ) // set in CreateRes_Impl
360 {
361 // no results available
362 throw uno::RuntimeException();
363 }
364
365 sal_Int32 nColCount = pColResRoot->GetSize(pResData->GetColStartMeasure());
366 sal_Int32 nRowCount = pRowResRoot->GetSize(pResData->GetRowStartMeasure());
367
368 // allocate full sequence
369 //TODO: leave out empty rows???
370
371 uno::Sequence< uno::Sequence<sheet::DataResult> > aSeq( nRowCount );
372 uno::Sequence<sheet::DataResult>* pRowAry = aSeq.getArray();
373 for (sal_Int32 nRow = 0; nRow < nRowCount; nRow++)
374 {
375 uno::Sequence<sheet::DataResult> aColSeq( nColCount );
376 // use default values of DataResult
377 pRowAry[nRow] = aColSeq;
378 }
379
380 ScDPResultFilterContext aFilterCxt;
381 pRowResRoot->FillDataResults(
382 pColResRoot.get(), aFilterCxt, aSeq, pResData->GetRowStartMeasure());
383
384 maResFilterSet.swap(aFilterCxt.maFilterSet); // Keep this data for GETPIVOTDATA.
385
386 return aSeq;
387 }
388
getFilteredResults(const uno::Sequence<sheet::DataPilotFieldFilter> & aFilters)389 uno::Sequence<double> ScDPSource::getFilteredResults(
390 const uno::Sequence<sheet::DataPilotFieldFilter>& aFilters )
391 {
392 if (maResFilterSet.empty())
393 getResults(); // Build result tree first.
394
395 // Get result values from the tree.
396 const ScDPResultTree::ValuesType* pVals = maResFilterSet.getResults(aFilters);
397 if (pVals && !pVals->empty())
398 {
399 size_t n = pVals->size();
400 uno::Sequence<double> aRet(n);
401 for (size_t i = 0; i < n; ++i)
402 aRet[i] = (*pVals)[i];
403
404 return aRet;
405 }
406
407 if (aFilters.getLength() == 1)
408 {
409 // Try to get result from the leaf nodes.
410 double fVal = maResFilterSet.getLeafResult(aFilters[0]);
411 if (!std::isnan(fVal))
412 {
413 return { fVal };
414 }
415 }
416
417 return uno::Sequence<double>();
418 }
419
refresh()420 void SAL_CALL ScDPSource::refresh()
421 {
422 disposeData();
423 }
424
addRefreshListener(const uno::Reference<util::XRefreshListener> &)425 void SAL_CALL ScDPSource::addRefreshListener( const uno::Reference<util::XRefreshListener >& )
426 {
427 OSL_FAIL("not implemented"); //TODO: exception?
428 }
429
removeRefreshListener(const uno::Reference<util::XRefreshListener> &)430 void SAL_CALL ScDPSource::removeRefreshListener( const uno::Reference<util::XRefreshListener >& )
431 {
432 OSL_FAIL("not implemented"); //TODO: exception?
433 }
434
getDrillDownData(const Sequence<sheet::DataPilotFieldFilter> & aFilters)435 Sequence< Sequence<Any> > SAL_CALL ScDPSource::getDrillDownData(const Sequence<sheet::DataPilotFieldFilter>& aFilters)
436 {
437 sal_Int32 nColumnCount = GetData()->GetColumnCount();
438
439 vector<ScDPFilteredCache::Criterion> aFilterCriteria;
440 for (const sheet::DataPilotFieldFilter& rFilter : aFilters)
441 {
442 const OUString& aFieldName = rFilter.FieldName;
443 for (sal_Int32 nCol = 0; nCol < nColumnCount; ++nCol)
444 {
445 if (aFieldName == pData->getDimensionName(nCol))
446 {
447 ScDPDimension* pDim = GetDimensionsObject()->getByIndex( nCol );
448 ScDPMembers* pMembers = pDim->GetHierarchiesObject()->getByIndex(0)->
449 GetLevelsObject()->getByIndex(0)->GetMembersObject();
450 sal_Int32 nIndex = pMembers->GetIndexFromName( rFilter.MatchValueName );
451 if ( nIndex >= 0 )
452 {
453 ScDPItemData aItem(pMembers->getByIndex(nIndex)->FillItemData());
454 aFilterCriteria.emplace_back( );
455 aFilterCriteria.back().mnFieldIndex = nCol;
456 aFilterCriteria.back().mpFilter =
457 std::make_shared<ScDPFilteredCache::SingleFilter>(aItem);
458 }
459 }
460 }
461 }
462
463 // Take into account the visibilities of field members.
464 ScDPResultVisibilityData aResVisData(this);
465 pRowResRoot->FillVisibilityData(aResVisData);
466 pColResRoot->FillVisibilityData(aResVisData);
467 aResVisData.fillFieldFilters(aFilterCriteria);
468
469 Sequence< Sequence<Any> > aTabData;
470 std::unordered_set<sal_Int32> aCatDims;
471 GetCategoryDimensionIndices(aCatDims);
472 pData->GetDrillDownData(aFilterCriteria, aCatDims, aTabData);
473 return aTabData;
474 }
475
getDataDescription()476 OUString ScDPSource::getDataDescription()
477 {
478 CreateRes_Impl(); // create pResData
479
480 OUString aRet;
481 if ( pResData->GetMeasureCount() == 1 )
482 {
483 bool bTotalResult = false;
484 aRet = pResData->GetMeasureString(0, true, SUBTOTAL_FUNC_NONE, bTotalResult);
485 }
486
487 // empty for more than one measure
488
489 return aRet;
490 }
491
setIgnoreEmptyRows(bool bSet)492 void ScDPSource::setIgnoreEmptyRows(bool bSet)
493 {
494 bIgnoreEmptyRows = bSet;
495 pData->SetEmptyFlags( bIgnoreEmptyRows, bRepeatIfEmpty );
496 }
497
setRepeatIfEmpty(bool bSet)498 void ScDPSource::setRepeatIfEmpty(bool bSet)
499 {
500 bRepeatIfEmpty = bSet;
501 pData->SetEmptyFlags( bIgnoreEmptyRows, bRepeatIfEmpty );
502 }
503
disposeData()504 void ScDPSource::disposeData()
505 {
506 maResFilterSet.clear();
507
508 if ( pResData )
509 {
510 // reset all data...
511
512 pColResRoot.reset();
513 pRowResRoot.reset();
514 pResData.reset();
515 pColResults.reset();
516 pRowResults.reset();
517 aColLevelList.clear();
518 aRowLevelList.clear();
519 }
520
521 pDimensions.clear(); // settings have to be applied (from SaveData) again!
522 SetDupCount( 0 );
523
524 maColDims.clear();
525 maRowDims.clear();
526 maDataDims.clear();
527 maPageDims.clear();
528
529 pData->DisposeData(); // cached entries etc.
530 bPageFiltered = false;
531 bResultOverflow = false;
532 }
533
lcl_CountMinMembers(const vector<ScDPDimension * > & ppDim,const vector<ScDPLevel * > & ppLevel,tools::Long nLevels)534 static tools::Long lcl_CountMinMembers(const vector<ScDPDimension*>& ppDim, const vector<ScDPLevel*>& ppLevel, tools::Long nLevels )
535 {
536 // Calculate the product of the member count for those consecutive levels that
537 // have the "show all" flag, one following level, and the data layout dimension.
538
539 tools::Long nTotal = 1;
540 tools::Long nDataCount = 1;
541 bool bWasShowAll = true;
542 tools::Long nPos = nLevels;
543 while ( nPos > 0 )
544 {
545 --nPos;
546
547 if ( nPos+1 < nLevels && ppDim[nPos] == ppDim[nPos+1] )
548 {
549 OSL_FAIL("lcl_CountMinMembers: multiple levels from one dimension not implemented");
550 return 0;
551 }
552
553 bool bDo = false;
554 if ( ppDim[nPos]->getIsDataLayoutDimension() )
555 {
556 // data layout dim doesn't interfere with "show all" flags
557 nDataCount = ppLevel[nPos]->GetMembersObject()->getCount();
558 if ( nDataCount == 0 )
559 nDataCount = 1;
560 }
561 else if ( bWasShowAll ) // "show all" set for all following levels?
562 {
563 bDo = true;
564 if ( !ppLevel[nPos]->getShowEmpty() )
565 {
566 // this level is counted, following ones are not
567 bWasShowAll = false;
568 }
569 }
570 if ( bDo )
571 {
572 tools::Long nThisCount = ppLevel[nPos]->GetMembersObject()->getMinMembers();
573 if ( nThisCount == 0 )
574 {
575 nTotal = 1; // empty level -> start counting from here
576 //TODO: start with visible elements in this level?
577 }
578 else
579 {
580 if ( nTotal >= LONG_MAX / nThisCount )
581 return LONG_MAX; // overflow
582 nTotal *= nThisCount;
583 }
584 }
585 }
586
587 // always include data layout dim, even after restarting
588 if ( nTotal >= LONG_MAX / nDataCount )
589 return LONG_MAX; // overflow
590 nTotal *= nDataCount;
591
592 return nTotal;
593 }
594
FillCalcInfo(bool bIsRow,ScDPTableData::CalcInfo & rInfo,bool & rHasAutoShow)595 void ScDPSource::FillCalcInfo(bool bIsRow, ScDPTableData::CalcInfo& rInfo, bool &rHasAutoShow)
596 {
597 const std::vector<sal_Int32>& rDims = bIsRow ? maRowDims : maColDims;
598 for (const auto& rDimIndex : rDims)
599 {
600 ScDPDimension* pDim = GetDimensionsObject()->getByIndex(rDimIndex);
601 tools::Long nHierarchy = ScDPDimension::getUsedHierarchy();
602 if ( nHierarchy >= ScDPHierarchies::getCount() )
603 nHierarchy = 0;
604 ScDPLevels* pLevels = pDim->GetHierarchiesObject()->getByIndex(nHierarchy)->GetLevelsObject();
605 sal_Int32 nCount = pLevels->getCount();
606
607 //TODO: Test
608 if (pDim->getIsDataLayoutDimension() && maDataDims.size() < 2)
609 nCount = 0;
610 //TODO: Test
611
612 for (sal_Int32 j = 0; j < nCount; ++j)
613 {
614 ScDPLevel* pLevel = pLevels->getByIndex(j);
615 pLevel->EvaluateSortOrder();
616
617 // no layout flags for column fields, only for row fields
618 pLevel->SetEnableLayout( bIsRow );
619
620 if ( pLevel->GetAutoShow().IsEnabled )
621 rHasAutoShow = true;
622
623 if (bIsRow)
624 {
625 rInfo.aRowLevelDims.push_back(rDimIndex);
626 rInfo.aRowDims.push_back(pDim);
627 rInfo.aRowLevels.push_back(pLevel);
628 }
629 else
630 {
631 rInfo.aColLevelDims.push_back(rDimIndex);
632 rInfo.aColDims.push_back(pDim);
633 rInfo.aColLevels.push_back(pLevel);
634 }
635
636 pLevel->GetMembersObject(); // initialize for groups
637 }
638 }
639 }
640
641 namespace {
642
643 class CategoryDimInserter
644 {
645 ScDPSource& mrSource;
646 std::unordered_set<sal_Int32>& mrCatDims;
647 public:
CategoryDimInserter(ScDPSource & rSource,std::unordered_set<sal_Int32> & rCatDims)648 CategoryDimInserter(ScDPSource& rSource, std::unordered_set<sal_Int32>& rCatDims) :
649 mrSource(rSource),
650 mrCatDims(rCatDims) {}
651
operator ()(tools::Long nDim)652 void operator() (tools::Long nDim)
653 {
654 if (!mrSource.IsDataLayoutDimension(nDim))
655 mrCatDims.insert(nDim);
656 }
657 };
658
659 }
660
GetCategoryDimensionIndices(std::unordered_set<sal_Int32> & rCatDims)661 void ScDPSource::GetCategoryDimensionIndices(std::unordered_set<sal_Int32>& rCatDims)
662 {
663 std::unordered_set<sal_Int32> aCatDims;
664
665 CategoryDimInserter aInserter(*this, aCatDims);
666 std::for_each(maColDims.begin(), maColDims.end(), aInserter);
667 std::for_each(maRowDims.begin(), maRowDims.end(), aInserter);
668 std::for_each(maPageDims.begin(), maPageDims.end(), aInserter);
669
670 rCatDims.swap(aCatDims);
671 }
672
FilterCacheByPageDimensions()673 void ScDPSource::FilterCacheByPageDimensions()
674 {
675 // #i117661# Repeated calls to ScDPFilteredCache::filterByPageDimension
676 // are invalid because rows are only hidden, never shown again. If
677 // FilterCacheByPageDimensions is called again, the cache table must
678 // be re-initialized. Currently, CreateRes_Impl always uses a fresh cache
679 // because ScDBDocFunc::DataPilotUpdate calls InvalidateData.
680
681 if (bPageFiltered)
682 {
683 SAL_WARN( "sc.core","tried to apply page field filters several times");
684
685 pData->DisposeData();
686 pData->CreateCacheTable(); // re-initialize the cache table
687 bPageFiltered = false;
688 }
689
690 // filter table by page dimensions.
691 vector<ScDPFilteredCache::Criterion> aCriteria;
692 for (const auto& rDimIndex : maPageDims)
693 {
694 ScDPDimension* pDim = GetDimensionsObject()->getByIndex(rDimIndex);
695 tools::Long nField = pDim->GetDimension();
696
697 ScDPMembers* pMems = pDim->GetHierarchiesObject()->getByIndex(0)->
698 GetLevelsObject()->getByIndex(0)->GetMembersObject();
699
700 tools::Long nMemCount = pMems->getCount();
701 ScDPFilteredCache::Criterion aFilter;
702 aFilter.mnFieldIndex = static_cast<sal_Int32>(nField);
703 aFilter.mpFilter = std::make_shared<ScDPFilteredCache::GroupFilter>();
704 ScDPFilteredCache::GroupFilter* pGrpFilter =
705 static_cast<ScDPFilteredCache::GroupFilter*>(aFilter.mpFilter.get());
706 for (tools::Long j = 0; j < nMemCount; ++j)
707 {
708 ScDPMember* pMem = pMems->getByIndex(j);
709 if (pMem->isVisible())
710 {
711 ScDPItemData aData(pMem->FillItemData());
712 pGrpFilter->addMatchItem(aData);
713 }
714 }
715 if (pGrpFilter->getMatchItemCount() < o3tl::make_unsigned(nMemCount))
716 // there is at least one invisible item. Add this filter criterion to the mix.
717 aCriteria.push_back(aFilter);
718
719 if (!pDim->HasSelectedPage())
720 continue;
721
722 const ScDPItemData& rData = pDim->GetSelectedData();
723 aCriteria.emplace_back();
724 ScDPFilteredCache::Criterion& r = aCriteria.back();
725 r.mnFieldIndex = static_cast<sal_Int32>(nField);
726 r.mpFilter = std::make_shared<ScDPFilteredCache::SingleFilter>(rData);
727 }
728 if (!aCriteria.empty())
729 {
730 std::unordered_set<sal_Int32> aCatDims;
731 GetCategoryDimensionIndices(aCatDims);
732 pData->FilterCacheTable(aCriteria, aCatDims);
733 bPageFiltered = true;
734 }
735 }
736
CreateRes_Impl()737 void ScDPSource::CreateRes_Impl()
738 {
739 if (pResData)
740 return;
741
742 sheet::DataPilotFieldOrientation nDataOrient = GetDataLayoutOrientation();
743 if (maDataDims.size() > 1 && ( nDataOrient != sheet::DataPilotFieldOrientation_COLUMN &&
744 nDataOrient != sheet::DataPilotFieldOrientation_ROW ) )
745 {
746 // if more than one data dimension, data layout orientation must be set
747 SetOrientation( pData->GetColumnCount(), sheet::DataPilotFieldOrientation_ROW );
748 nDataOrient = sheet::DataPilotFieldOrientation_ROW;
749 }
750
751 // TODO: Aggregate pDataNames, pDataRefValues, nDataRefOrient, and
752 // eDataFunctions into a structure and use vector instead of static
753 // or pointer arrays.
754 vector<OUString> aDataNames;
755 vector<sheet::DataPilotFieldReference> aDataRefValues;
756 vector<ScSubTotalFunc> aDataFunctions;
757 vector<sheet::DataPilotFieldOrientation> aDataRefOrient;
758
759 ScDPTableData::CalcInfo aInfo;
760
761 // LateInit (initialize only those rows/children that are used) can be used unless
762 // any data dimension needs reference values from column/row dimensions
763 bool bLateInit = true;
764
765 // Go through all data dimensions (i.e. fields) and build their meta data
766 // so that they can be passed on to ScDPResultData instance later.
767 // TODO: aggregate all of data dimension info into a structure.
768 for (const tools::Long nDimIndex : maDataDims)
769 {
770 // Get function for each data field.
771 ScDPDimension* pDim = GetDimensionsObject()->getByIndex(nDimIndex);
772 ScGeneralFunction eUser = pDim->getFunction();
773 if (eUser == ScGeneralFunction::AUTO)
774 {
775 //TODO: test for numeric data
776 eUser = ScGeneralFunction::SUM;
777 }
778
779 // Map UNO's enum to internal enum ScSubTotalFunc.
780 aDataFunctions.push_back(ScDPUtil::toSubTotalFunc(eUser));
781
782 // Get reference field/item information.
783 aDataRefValues.push_back(pDim->GetReferenceValue());
784 sheet::DataPilotFieldOrientation nDataRefOrient = sheet::DataPilotFieldOrientation_HIDDEN; // default if not used
785 sal_Int32 eRefType = aDataRefValues.back().ReferenceType;
786 if ( eRefType == sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE ||
787 eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE ||
788 eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE ||
789 eRefType == sheet::DataPilotFieldReferenceType::RUNNING_TOTAL )
790 {
791 sal_Int32 nColumn = comphelper::findValue(
792 GetDimensionsObject()->getElementNames(), aDataRefValues.back().ReferenceField);
793 if ( nColumn >= 0 )
794 {
795 nDataRefOrient = GetOrientation(nColumn);
796 // need fully initialized results to find reference values
797 // (both in column or row dimensions), so updated values or
798 // differences to 0 can be displayed even for empty results.
799 bLateInit = false;
800 }
801 }
802
803 aDataRefOrient.push_back(nDataRefOrient);
804
805 aDataNames.push_back(pDim->getName());
806
807 //TODO: modify user visible strings as in ScDPResultData::GetMeasureString instead!
808
809 aDataNames.back() = ScDPUtil::getSourceDimensionName(aDataNames.back());
810
811 //TODO: if the name is overridden by user, a flag must be set
812 //TODO: so the user defined name replaces the function string and field name.
813
814 //TODO: the complete name (function and field) must be stored at the dimension
815
816 tools::Long nSource = pDim->GetSourceDim();
817 if (nSource >= 0)
818 aInfo.aDataSrcCols.push_back(nSource);
819 else
820 aInfo.aDataSrcCols.push_back(nDimIndex);
821 }
822
823 pResData.reset( new ScDPResultData(*this) );
824 pResData->SetMeasureData(aDataFunctions, aDataRefValues, aDataRefOrient, aDataNames);
825 pResData->SetDataLayoutOrientation(nDataOrient);
826 pResData->SetLateInit( bLateInit );
827
828 bool bHasAutoShow = false;
829
830 ScDPInitState aInitState;
831
832 // Page field selections restrict the members shown in related fields
833 // (both in column and row fields). aInitState is filled with the page
834 // field selections, they are kept across the data iterator loop.
835
836 for (const auto& rDimIndex : maPageDims)
837 {
838 ScDPDimension* pDim = GetDimensionsObject()->getByIndex(rDimIndex);
839 if ( pDim->HasSelectedPage() )
840 aInitState.AddMember(rDimIndex, GetCache()->GetIdByItemData(rDimIndex, pDim->GetSelectedData()));
841 }
842
843 // Show grand total columns only when the option is set *and* there is at
844 // least one column field. Same for the grand total rows.
845 sheet::DataPilotFieldOrientation nDataLayoutOrient = GetDataLayoutOrientation();
846 tools::Long nColDimCount2 = maColDims.size() - (nDataLayoutOrient == sheet::DataPilotFieldOrientation_COLUMN ? 1 : 0);
847 tools::Long nRowDimCount2 = maRowDims.size() - (nDataLayoutOrient == sheet::DataPilotFieldOrientation_ROW ? 1 : 0);
848 bool bShowColGrand = bColumnGrand && nColDimCount2 > 0;
849 bool bShowRowGrand = bRowGrand && nRowDimCount2 > 0;
850 pColResRoot.reset( new ScDPResultMember(pResData.get(), bShowColGrand) );
851 pRowResRoot.reset( new ScDPResultMember(pResData.get(), bShowRowGrand) );
852
853 FillCalcInfo(false, aInfo, bHasAutoShow);
854 tools::Long nColLevelCount = aInfo.aColLevels.size();
855
856 pColResRoot->InitFrom( aInfo.aColDims, aInfo.aColLevels, 0, aInitState );
857 pColResRoot->SetHasElements();
858
859 FillCalcInfo(true, aInfo, bHasAutoShow);
860 tools::Long nRowLevelCount = aInfo.aRowLevels.size();
861
862 if ( nRowLevelCount > 0 )
863 {
864 // disable layout flags for the innermost row field (level)
865 aInfo.aRowLevels[nRowLevelCount-1]->SetEnableLayout( false );
866 }
867
868 pRowResRoot->InitFrom( aInfo.aRowDims, aInfo.aRowLevels, 0, aInitState );
869 pRowResRoot->SetHasElements();
870
871 // initialize members object also for all page dimensions (needed for numeric groups)
872 for (const auto& rDimIndex : maPageDims)
873 {
874 ScDPDimension* pDim = GetDimensionsObject()->getByIndex(rDimIndex);
875 tools::Long nHierarchy = ScDPDimension::getUsedHierarchy();
876 if ( nHierarchy >= ScDPHierarchies::getCount() )
877 nHierarchy = 0;
878
879 ScDPLevels* pLevels = pDim->GetHierarchiesObject()->getByIndex(nHierarchy)->GetLevelsObject();
880 tools::Long nCount = pLevels->getCount();
881 for (tools::Long j=0; j<nCount; j++)
882 pLevels->getByIndex(j)->GetMembersObject(); // initialize for groups
883 }
884
885 // pre-check: calculate minimum number of result columns / rows from
886 // levels that have the "show all" flag set
887
888 tools::Long nMinColMembers = lcl_CountMinMembers( aInfo.aColDims, aInfo.aColLevels, nColLevelCount );
889 tools::Long nMinRowMembers = lcl_CountMinMembers( aInfo.aRowDims, aInfo.aRowLevels, nRowLevelCount );
890
891 if ( nMinColMembers > MAXCOLCOUNT/*SC_MINCOUNT_LIMIT*/ || nMinRowMembers > SC_MINCOUNT_LIMIT )
892 {
893 // resulting table is too big -> abort before calculating
894 // (this relies on late init, so no members are allocated in InitFrom above)
895
896 bResultOverflow = true;
897 return;
898 }
899
900 FilterCacheByPageDimensions();
901
902 aInfo.aPageDims = maPageDims;
903 aInfo.pInitState = &aInitState;
904 aInfo.pColRoot = pColResRoot.get();
905 aInfo.pRowRoot = pRowResRoot.get();
906 pData->CalcResults(aInfo, false);
907
908 pColResRoot->CheckShowEmpty();
909 pRowResRoot->CheckShowEmpty();
910
911 // With all data processed, calculate the final results:
912
913 // UpdateDataResults calculates all original results from the collected values,
914 // and stores them as reference values if needed.
915 pRowResRoot->UpdateDataResults( pColResRoot.get(), pResData->GetRowStartMeasure() );
916
917 if ( bHasAutoShow ) // do the double calculation only if AutoShow is used
918 {
919 // Find the desired members and set bAutoHidden flag for the others
920 pRowResRoot->DoAutoShow( pColResRoot.get() );
921
922 // Reset all results to empty, so they can be built again with data for the
923 // desired members only.
924 pColResRoot->ResetResults();
925 pRowResRoot->ResetResults();
926 pData->CalcResults(aInfo, true);
927
928 // Call UpdateDataResults again, with the new (limited) values.
929 pRowResRoot->UpdateDataResults( pColResRoot.get(), pResData->GetRowStartMeasure() );
930 }
931
932 // SortMembers does the sorting by a result dimension, using the original results,
933 // but not running totals etc.
934 pRowResRoot->SortMembers( pColResRoot.get() );
935
936 // UpdateRunningTotals calculates running totals along column/row dimensions,
937 // differences from other members (named or relative), and column/row percentages
938 // or index values.
939 // Running totals and relative differences need to be done using the sorted values.
940 // Column/row percentages and index values must be done after sorting, because the
941 // results may no longer be in the right order (row total for percentage of row is
942 // always 1).
943 ScDPRunningTotalState aRunning( pColResRoot.get(), pRowResRoot.get() );
944 ScDPRowTotals aTotals;
945 pRowResRoot->UpdateRunningTotals( pColResRoot.get(), pResData->GetRowStartMeasure(), aRunning, aTotals );
946
947 #if DUMP_PIVOT_TABLE
948 DumpResults();
949 #endif
950 }
951
FillLevelList(sheet::DataPilotFieldOrientation nOrientation,std::vector<ScDPLevel * > & rList)952 void ScDPSource::FillLevelList( sheet::DataPilotFieldOrientation nOrientation, std::vector<ScDPLevel*> &rList )
953 {
954 rList.clear();
955
956 std::vector<sal_Int32>* pDimIndex = nullptr;
957 switch (nOrientation)
958 {
959 case sheet::DataPilotFieldOrientation_COLUMN:
960 pDimIndex = &maColDims;
961 break;
962 case sheet::DataPilotFieldOrientation_ROW:
963 pDimIndex = &maRowDims;
964 break;
965 case sheet::DataPilotFieldOrientation_DATA:
966 pDimIndex = &maDataDims;
967 break;
968 case sheet::DataPilotFieldOrientation_PAGE:
969 pDimIndex = &maPageDims;
970 break;
971 default:
972 OSL_FAIL( "ScDPSource::FillLevelList: unexpected orientation" );
973 break;
974 }
975 if (!pDimIndex)
976 {
977 OSL_FAIL("invalid orientation");
978 return;
979 }
980
981 ScDPDimensions* pDims = GetDimensionsObject();
982 for (const auto& rIndex : *pDimIndex)
983 {
984 ScDPDimension* pDim = pDims->getByIndex(rIndex);
985 OSL_ENSURE( pDim->getOrientation() == nOrientation, "orientations are wrong" );
986
987 ScDPHierarchies* pHiers = pDim->GetHierarchiesObject();
988 sal_Int32 nHierarchy = ScDPDimension::getUsedHierarchy();
989 if ( nHierarchy >= ScDPHierarchies::getCount() )
990 nHierarchy = 0;
991 ScDPHierarchy* pHier = pHiers->getByIndex(nHierarchy);
992 ScDPLevels* pLevels = pHier->GetLevelsObject();
993 sal_Int32 nLevCount = pLevels->getCount();
994 for (sal_Int32 nLev=0; nLev<nLevCount; nLev++)
995 {
996 ScDPLevel* pLevel = pLevels->getByIndex(nLev);
997 rList.push_back(pLevel);
998 }
999 }
1000 }
1001
FillMemberResults()1002 void ScDPSource::FillMemberResults()
1003 {
1004 if ( pColResults || pRowResults )
1005 return;
1006
1007 CreateRes_Impl();
1008
1009 if ( bResultOverflow ) // set in CreateRes_Impl
1010 {
1011 // no results available -> abort (leave empty)
1012 // exception is thrown in ScDPSource::getResults
1013 return;
1014 }
1015
1016 FillLevelList( sheet::DataPilotFieldOrientation_COLUMN, aColLevelList );
1017 sal_Int32 nColLevelCount = aColLevelList.size();
1018 if (nColLevelCount)
1019 {
1020 tools::Long nColDimSize = pColResRoot->GetSize(pResData->GetColStartMeasure());
1021 pColResults.reset(new uno::Sequence<sheet::MemberResult>[nColLevelCount]);
1022 for (tools::Long i=0; i<nColLevelCount; i++)
1023 pColResults[i].realloc(nColDimSize);
1024
1025 tools::Long nPos = 0;
1026 pColResRoot->FillMemberResults( pColResults.get(), nPos, pResData->GetColStartMeasure(),
1027 true, nullptr, nullptr );
1028 }
1029
1030 FillLevelList( sheet::DataPilotFieldOrientation_ROW, aRowLevelList );
1031 tools::Long nRowLevelCount = aRowLevelList.size();
1032 if (nRowLevelCount)
1033 {
1034 tools::Long nRowDimSize = pRowResRoot->GetSize(pResData->GetRowStartMeasure());
1035 pRowResults.reset( new uno::Sequence<sheet::MemberResult>[nRowLevelCount] );
1036 for (tools::Long i=0; i<nRowLevelCount; i++)
1037 pRowResults[i].realloc(nRowDimSize);
1038
1039 tools::Long nPos = 0;
1040 pRowResRoot->FillMemberResults( pRowResults.get(), nPos, pResData->GetRowStartMeasure(),
1041 true, nullptr, nullptr );
1042 }
1043 }
1044
GetMemberResults(const ScDPLevel * pLevel)1045 const uno::Sequence<sheet::MemberResult>* ScDPSource::GetMemberResults( const ScDPLevel* pLevel )
1046 {
1047 FillMemberResults();
1048
1049 sal_Int32 i = 0;
1050 sal_Int32 nColCount = aColLevelList.size();
1051 for (i=0; i<nColCount; i++)
1052 {
1053 ScDPLevel* pColLevel = aColLevelList[i];
1054 if ( pColLevel == pLevel )
1055 return &pColResults[i];
1056 }
1057 sal_Int32 nRowCount = aRowLevelList.size();
1058 for (i=0; i<nRowCount; i++)
1059 {
1060 ScDPLevel* pRowLevel = aRowLevelList[i];
1061 if ( pRowLevel == pLevel )
1062 return &pRowResults[i];
1063 }
1064 return nullptr;
1065 }
1066
1067 // XPropertySet
1068
getPropertySetInfo()1069 uno::Reference<beans::XPropertySetInfo> SAL_CALL ScDPSource::getPropertySetInfo()
1070 {
1071 SolarMutexGuard aGuard;
1072 using beans::PropertyAttribute::READONLY;
1073
1074 static const SfxItemPropertyMapEntry aDPSourceMap_Impl[] =
1075 {
1076 { u"" SC_UNO_DP_COLGRAND, 0, cppu::UnoType<bool>::get(), 0, 0 },
1077 { u"" SC_UNO_DP_DATADESC, 0, cppu::UnoType<OUString>::get(), beans::PropertyAttribute::READONLY, 0 },
1078 { u"" SC_UNO_DP_IGNOREEMPTY, 0, cppu::UnoType<bool>::get(), 0, 0 }, // for sheet data only
1079 { u"" SC_UNO_DP_REPEATEMPTY, 0, cppu::UnoType<bool>::get(), 0, 0 }, // for sheet data only
1080 { u"" SC_UNO_DP_ROWGRAND, 0, cppu::UnoType<bool>::get(), 0, 0 },
1081 { u"" SC_UNO_DP_ROWFIELDCOUNT, 0, cppu::UnoType<sal_Int32>::get(), READONLY, 0 },
1082 { u"" SC_UNO_DP_COLUMNFIELDCOUNT, 0, cppu::UnoType<sal_Int32>::get(), READONLY, 0 },
1083 { u"" SC_UNO_DP_DATAFIELDCOUNT, 0, cppu::UnoType<sal_Int32>::get(), READONLY, 0 },
1084 { u"" SC_UNO_DP_GRANDTOTAL_NAME, 0, cppu::UnoType<OUString>::get(), 0, 0 },
1085 { u"", 0, css::uno::Type(), 0, 0 }
1086 };
1087 static uno::Reference<beans::XPropertySetInfo> aRef =
1088 new SfxItemPropertySetInfo( aDPSourceMap_Impl );
1089 return aRef;
1090 }
1091
setPropertyValue(const OUString & aPropertyName,const uno::Any & aValue)1092 void SAL_CALL ScDPSource::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue )
1093 {
1094 if (aPropertyName == SC_UNO_DP_COLGRAND)
1095 bColumnGrand = lcl_GetBoolFromAny(aValue);
1096 else if (aPropertyName == SC_UNO_DP_ROWGRAND)
1097 bRowGrand = lcl_GetBoolFromAny(aValue);
1098 else if (aPropertyName == SC_UNO_DP_IGNOREEMPTY)
1099 setIgnoreEmptyRows( lcl_GetBoolFromAny( aValue ) );
1100 else if (aPropertyName == SC_UNO_DP_REPEATEMPTY)
1101 setRepeatIfEmpty( lcl_GetBoolFromAny( aValue ) );
1102 else if (aPropertyName == SC_UNO_DP_GRANDTOTAL_NAME)
1103 {
1104 OUString aName;
1105 if (aValue >>= aName)
1106 mpGrandTotalName = aName;
1107 }
1108 else
1109 {
1110 OSL_FAIL("unknown property");
1111 //TODO: THROW( UnknownPropertyException() );
1112 }
1113 }
1114
getPropertyValue(const OUString & aPropertyName)1115 uno::Any SAL_CALL ScDPSource::getPropertyValue( const OUString& aPropertyName )
1116 {
1117 uno::Any aRet;
1118 if ( aPropertyName == SC_UNO_DP_COLGRAND )
1119 aRet <<= bColumnGrand;
1120 else if ( aPropertyName == SC_UNO_DP_ROWGRAND )
1121 aRet <<= bRowGrand;
1122 else if ( aPropertyName == SC_UNO_DP_IGNOREEMPTY )
1123 aRet <<= bIgnoreEmptyRows;
1124 else if ( aPropertyName == SC_UNO_DP_REPEATEMPTY )
1125 aRet <<= bRepeatIfEmpty;
1126 else if ( aPropertyName == SC_UNO_DP_DATADESC ) // read-only
1127 aRet <<= getDataDescription();
1128 else if ( aPropertyName == SC_UNO_DP_ROWFIELDCOUNT ) // read-only
1129 aRet <<= static_cast<sal_Int32>(maRowDims.size());
1130 else if ( aPropertyName == SC_UNO_DP_COLUMNFIELDCOUNT ) // read-only
1131 aRet <<= static_cast<sal_Int32>(maColDims.size());
1132 else if ( aPropertyName == SC_UNO_DP_DATAFIELDCOUNT ) // read-only
1133 aRet <<= static_cast<sal_Int32>(maDataDims.size());
1134 else if (aPropertyName == SC_UNO_DP_GRANDTOTAL_NAME)
1135 {
1136 if (mpGrandTotalName)
1137 aRet <<= *mpGrandTotalName;
1138 }
1139 else
1140 {
1141 OSL_FAIL("unknown property");
1142 //TODO: THROW( UnknownPropertyException() );
1143 }
1144 return aRet;
1145 }
1146
1147 #if DUMP_PIVOT_TABLE
DumpResults() const1148 void ScDPSource::DumpResults() const
1149 {
1150 std::cout << "+++++ column root" << std::endl;
1151 pColResRoot->Dump(1);
1152 std::cout << "+++++ row root" << std::endl;
1153 pRowResRoot->Dump(1);
1154 }
1155 #endif
1156
SC_IMPL_DUMMY_PROPERTY_LISTENER(ScDPSource)1157 SC_IMPL_DUMMY_PROPERTY_LISTENER( ScDPSource )
1158
1159 ScDPDimensions::ScDPDimensions( ScDPSource* pSrc ) :
1160 pSource( pSrc )
1161 {
1162 //TODO: hold pSource
1163
1164 // include data layout dimension and duplicated dimensions
1165 nDimCount = pSource->GetData()->GetColumnCount() + 1 + pSource->GetDupCount();
1166 }
1167
~ScDPDimensions()1168 ScDPDimensions::~ScDPDimensions()
1169 {
1170 //TODO: release pSource
1171 }
1172
CountChanged()1173 void ScDPDimensions::CountChanged()
1174 {
1175 // include data layout dimension and duplicated dimensions
1176 sal_Int32 nNewCount = pSource->GetData()->GetColumnCount() + 1 + pSource->GetDupCount();
1177 if ( ppDims )
1178 {
1179 sal_Int32 i;
1180 sal_Int32 nCopy = std::min( nNewCount, nDimCount );
1181 rtl::Reference<ScDPDimension>* ppNew = new rtl::Reference<ScDPDimension>[nNewCount];
1182
1183 for (i=0; i<nCopy; i++) // copy existing dims
1184 ppNew[i] = ppDims[i];
1185 for (i=nCopy; i<nNewCount; i++) // clear additional pointers
1186 ppNew[i] = nullptr;
1187
1188 ppDims.reset( ppNew );
1189 }
1190 nDimCount = nNewCount;
1191 }
1192
1193 // very simple XNameAccess implementation using getCount/getByIndex
1194
getByName(const OUString & aName)1195 uno::Any SAL_CALL ScDPDimensions::getByName( const OUString& aName )
1196 {
1197 sal_Int32 nCount = getCount();
1198 for (sal_Int32 i=0; i<nCount; i++)
1199 if ( getByIndex(i)->getName() == aName )
1200 {
1201 uno::Reference<container::XNamed> xNamed = getByIndex(i);
1202 uno::Any aRet;
1203 aRet <<= xNamed;
1204 return aRet;
1205 }
1206
1207 throw container::NoSuchElementException();
1208 // return uno::Any();
1209 }
1210
getElementNames()1211 uno::Sequence<OUString> SAL_CALL ScDPDimensions::getElementNames()
1212 {
1213 tools::Long nCount = getCount();
1214 uno::Sequence<OUString> aSeq(nCount);
1215 OUString* pArr = aSeq.getArray();
1216 for (tools::Long i=0; i<nCount; i++)
1217 pArr[i] = getByIndex(i)->getName();
1218 return aSeq;
1219 }
1220
hasByName(const OUString & aName)1221 sal_Bool SAL_CALL ScDPDimensions::hasByName( const OUString& aName )
1222 {
1223 tools::Long nCount = getCount();
1224 for (tools::Long i=0; i<nCount; i++)
1225 if ( getByIndex(i)->getName() == aName )
1226 return true;
1227 return false;
1228 }
1229
getElementType()1230 uno::Type SAL_CALL ScDPDimensions::getElementType()
1231 {
1232 return cppu::UnoType<container::XNamed>::get();
1233 }
1234
hasElements()1235 sal_Bool SAL_CALL ScDPDimensions::hasElements()
1236 {
1237 return ( getCount() > 0 );
1238 }
1239
1240 // end of XNameAccess implementation
1241
getCount() const1242 tools::Long ScDPDimensions::getCount() const
1243 {
1244 // in tabular data, every column of source data is a dimension
1245
1246 return nDimCount;
1247 }
1248
getByIndex(tools::Long nIndex) const1249 ScDPDimension* ScDPDimensions::getByIndex(tools::Long nIndex) const
1250 {
1251 if ( nIndex >= 0 && nIndex < nDimCount )
1252 {
1253 if ( !ppDims )
1254 {
1255 const_cast<ScDPDimensions*>(this)->ppDims.reset(new rtl::Reference<ScDPDimension>[nDimCount] );
1256 for (tools::Long i=0; i<nDimCount; i++)
1257 ppDims[i] = nullptr;
1258 }
1259 if ( !ppDims[nIndex].is() )
1260 {
1261 ppDims[nIndex] = new ScDPDimension( pSource, nIndex );
1262 }
1263
1264 return ppDims[nIndex].get();
1265 }
1266
1267 return nullptr; //TODO: exception?
1268 }
1269
ScDPDimension(ScDPSource * pSrc,tools::Long nD)1270 ScDPDimension::ScDPDimension( ScDPSource* pSrc, tools::Long nD ) :
1271 pSource( pSrc ),
1272 nDim( nD ),
1273 nFunction( ScGeneralFunction::SUM ), // sum is default
1274 nSourceDim( -1 ),
1275 bHasSelectedPage( false ),
1276 mbHasHiddenMember(false)
1277 {
1278 //TODO: hold pSource
1279 }
1280
~ScDPDimension()1281 ScDPDimension::~ScDPDimension()
1282 {
1283 //TODO: release pSource
1284 }
1285
GetHierarchiesObject()1286 ScDPHierarchies* ScDPDimension::GetHierarchiesObject()
1287 {
1288 if (!mxHierarchies.is())
1289 {
1290 mxHierarchies = new ScDPHierarchies( pSource, nDim );
1291 }
1292 return mxHierarchies.get();
1293 }
1294
GetLayoutName() const1295 const std::optional<OUString> & ScDPDimension::GetLayoutName() const
1296 {
1297 return mpLayoutName;
1298 }
1299
GetSubtotalName() const1300 const std::optional<OUString> & ScDPDimension::GetSubtotalName() const
1301 {
1302 return mpSubtotalName;
1303 }
1304
getHierarchies()1305 uno::Reference<container::XNameAccess> SAL_CALL ScDPDimension::getHierarchies()
1306 {
1307 return GetHierarchiesObject();
1308 }
1309
getName()1310 OUString SAL_CALL ScDPDimension::getName()
1311 {
1312 if (!aName.isEmpty())
1313 return aName;
1314 else
1315 return pSource->GetData()->getDimensionName( nDim );
1316 }
1317
setName(const OUString & rNewName)1318 void SAL_CALL ScDPDimension::setName( const OUString& rNewName )
1319 {
1320 // used after cloning
1321 aName = rNewName;
1322 }
1323
getOrientation() const1324 sheet::DataPilotFieldOrientation ScDPDimension::getOrientation() const
1325 {
1326 return pSource->GetOrientation( nDim );
1327 }
1328
getIsDataLayoutDimension() const1329 bool ScDPDimension::getIsDataLayoutDimension() const
1330 {
1331 return pSource->GetData()->getIsDataLayoutDimension( nDim );
1332 }
1333
setFunction(ScGeneralFunction nNew)1334 void ScDPDimension::setFunction(ScGeneralFunction nNew)
1335 {
1336 nFunction = nNew;
1337 }
1338
CreateCloneObject()1339 ScDPDimension* ScDPDimension::CreateCloneObject()
1340 {
1341 OSL_ENSURE( nSourceDim < 0, "recursive duplicate - not implemented" );
1342
1343 //TODO: set new name here, or temporary name ???
1344 OUString aNewName = aName;
1345
1346 ScDPDimension* pNew = pSource->AddDuplicated( aNewName );
1347
1348 pNew->aName = aNewName; //TODO: here or in source?
1349 pNew->nSourceDim = nDim; //TODO: recursive?
1350
1351 return pNew;
1352 }
1353
createClone()1354 uno::Reference<util::XCloneable> SAL_CALL ScDPDimension::createClone()
1355 {
1356 return CreateCloneObject();
1357 }
1358
GetSelectedData()1359 const ScDPItemData& ScDPDimension::GetSelectedData()
1360 {
1361 if ( !pSelectedData )
1362 {
1363 // find the named member to initialize pSelectedData from it, with name and value
1364
1365 tools::Long nLevel = 0;
1366
1367 tools::Long nHierarchy = getUsedHierarchy();
1368 if ( nHierarchy >= ScDPHierarchies::getCount() )
1369 nHierarchy = 0;
1370 ScDPLevels* pLevels = GetHierarchiesObject()->getByIndex(nHierarchy)->GetLevelsObject();
1371 tools::Long nLevCount = pLevels->getCount();
1372 if ( nLevel < nLevCount )
1373 {
1374 ScDPMembers* pMembers = pLevels->getByIndex(nLevel)->GetMembersObject();
1375
1376 //TODO: merge with ScDPMembers::getByName
1377 tools::Long nCount = pMembers->getCount();
1378 for (tools::Long i=0; i<nCount && !pSelectedData; i++)
1379 {
1380 ScDPMember* pMember = pMembers->getByIndex(i);
1381 if (aSelectedPage == pMember->GetNameStr(false))
1382 {
1383 pSelectedData.reset( new ScDPItemData(pMember->FillItemData()) );
1384 }
1385 }
1386 }
1387
1388 if ( !pSelectedData )
1389 pSelectedData.reset( new ScDPItemData(aSelectedPage) ); // default - name only
1390 }
1391
1392 return *pSelectedData;
1393 }
1394
1395 // XPropertySet
1396
getPropertySetInfo()1397 uno::Reference<beans::XPropertySetInfo> SAL_CALL ScDPDimension::getPropertySetInfo()
1398 {
1399 SolarMutexGuard aGuard;
1400
1401 static const SfxItemPropertyMapEntry aDPDimensionMap_Impl[] =
1402 {
1403 { u"" SC_UNO_DP_FILTER, 0, cppu::UnoType<uno::Sequence<sheet::TableFilterField>>::get(), 0, 0 },
1404 { u"" SC_UNO_DP_FLAGS, 0, cppu::UnoType<sal_Int32>::get(), beans::PropertyAttribute::READONLY, 0 },
1405 { u"" SC_UNO_DP_FUNCTION, 0, cppu::UnoType<sheet::GeneralFunction>::get(), 0, 0 },
1406 { u"" SC_UNO_DP_FUNCTION2, 0, cppu::UnoType<sal_Int16>::get(), 0, 0 },
1407 { u"" SC_UNO_DP_ISDATALAYOUT, 0, cppu::UnoType<bool>::get(), beans::PropertyAttribute::READONLY, 0 },
1408 { u"" SC_UNO_DP_NUMBERFO, 0, cppu::UnoType<sal_Int32>::get(), beans::PropertyAttribute::READONLY, 0 },
1409 { u"" SC_UNO_DP_ORIENTATION, 0, cppu::UnoType<sheet::DataPilotFieldOrientation>::get(), 0, 0 },
1410 { u"" SC_UNO_DP_ORIGINAL, 0, cppu::UnoType<container::XNamed>::get(), beans::PropertyAttribute::READONLY, 0 },
1411 { u"" SC_UNO_DP_ORIGINAL_POS, 0, cppu::UnoType<sal_Int32>::get(), 0, 0 },
1412 { u"" SC_UNO_DP_POSITION, 0, cppu::UnoType<sal_Int32>::get(), 0, 0 },
1413 { u"" SC_UNO_DP_REFVALUE, 0, cppu::UnoType<sheet::DataPilotFieldReference>::get(), 0, 0 },
1414 { u"" SC_UNO_DP_USEDHIERARCHY, 0, cppu::UnoType<sal_Int32>::get(), 0, 0 },
1415 { u"" SC_UNO_DP_LAYOUTNAME, 0, cppu::UnoType<OUString>::get(), 0, 0 },
1416 { u"" SC_UNO_DP_FIELD_SUBTOTALNAME, 0, cppu::UnoType<OUString>::get(), 0, 0 },
1417 { u"" SC_UNO_DP_HAS_HIDDEN_MEMBER, 0, cppu::UnoType<bool>::get(), 0, 0 },
1418 { u"", 0, css::uno::Type(), 0, 0 }
1419 };
1420 static uno::Reference<beans::XPropertySetInfo> aRef =
1421 new SfxItemPropertySetInfo( aDPDimensionMap_Impl );
1422 return aRef;
1423 }
1424
setPropertyValue(const OUString & aPropertyName,const uno::Any & aValue)1425 void SAL_CALL ScDPDimension::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue )
1426 {
1427 if ( aPropertyName == SC_UNO_DP_USEDHIERARCHY )
1428 {
1429 // #i52547# don't use the incomplete date hierarchy implementation - ignore the call
1430 }
1431 else if ( aPropertyName == SC_UNO_DP_ORIENTATION )
1432 {
1433 sheet::DataPilotFieldOrientation eEnum;
1434 if (aValue >>= eEnum)
1435 pSource->SetOrientation( nDim, eEnum );
1436 }
1437 else if ( aPropertyName == SC_UNO_DP_FUNCTION )
1438 {
1439 sheet::GeneralFunction eEnum;
1440 if (aValue >>= eEnum)
1441 setFunction( static_cast<ScGeneralFunction>(eEnum) );
1442 }
1443 else if ( aPropertyName == SC_UNO_DP_FUNCTION2 )
1444 {
1445 sal_Int16 eEnum;
1446 if (aValue >>= eEnum)
1447 setFunction( static_cast<ScGeneralFunction>(eEnum) );
1448 }
1449 else if ( aPropertyName == SC_UNO_DP_REFVALUE )
1450 aValue >>= aReferenceValue;
1451 else if ( aPropertyName == SC_UNO_DP_FILTER )
1452 {
1453 bool bDone = false;
1454 uno::Sequence<sheet::TableFilterField> aSeq;
1455 if (aValue >>= aSeq)
1456 {
1457 sal_Int32 nLength = aSeq.getLength();
1458 if ( nLength == 0 )
1459 {
1460 aSelectedPage.clear();
1461 bHasSelectedPage = false;
1462 bDone = true;
1463 }
1464 else if ( nLength == 1 )
1465 {
1466 const sheet::TableFilterField& rField = aSeq[0];
1467 if ( rField.Field == 0 && rField.Operator == sheet::FilterOperator_EQUAL && !rField.IsNumeric )
1468 {
1469 aSelectedPage = rField.StringValue;
1470 bHasSelectedPage = true;
1471 bDone = true;
1472 }
1473 }
1474 }
1475 if ( !bDone )
1476 {
1477 OSL_FAIL("Filter property is not a single string");
1478 throw lang::IllegalArgumentException();
1479 }
1480 pSelectedData.reset(); // invalid after changing aSelectedPage
1481 }
1482 else if (aPropertyName == SC_UNO_DP_LAYOUTNAME)
1483 {
1484 OUString aTmpName;
1485 if (aValue >>= aTmpName)
1486 mpLayoutName = aTmpName;
1487 }
1488 else if (aPropertyName == SC_UNO_DP_FIELD_SUBTOTALNAME)
1489 {
1490 OUString aTmpName;
1491 if (aValue >>= aTmpName)
1492 mpSubtotalName = aTmpName;
1493 }
1494 else if (aPropertyName == SC_UNO_DP_HAS_HIDDEN_MEMBER)
1495 {
1496 bool b = false;
1497 aValue >>= b;
1498 mbHasHiddenMember = b;
1499 }
1500 else
1501 {
1502 OSL_FAIL("unknown property");
1503 //TODO: THROW( UnknownPropertyException() );
1504 }
1505 }
1506
getPropertyValue(const OUString & aPropertyName)1507 uno::Any SAL_CALL ScDPDimension::getPropertyValue( const OUString& aPropertyName )
1508 {
1509 uno::Any aRet;
1510 if ( aPropertyName == SC_UNO_DP_POSITION )
1511 aRet <<= pSource->GetPosition( nDim );
1512 else if ( aPropertyName == SC_UNO_DP_USEDHIERARCHY )
1513 aRet <<= static_cast<sal_Int32>(getUsedHierarchy());
1514 else if ( aPropertyName == SC_UNO_DP_ORIENTATION )
1515 {
1516 sheet::DataPilotFieldOrientation eVal = getOrientation();
1517 aRet <<= eVal;
1518 }
1519 else if ( aPropertyName == SC_UNO_DP_FUNCTION )
1520 {
1521 ScGeneralFunction nVal = getFunction();
1522 if (nVal == ScGeneralFunction::MEDIAN)
1523 nVal = ScGeneralFunction::NONE;
1524 const int nValAsInt = static_cast<int>(nVal);
1525 assert(nValAsInt >= int(css::sheet::GeneralFunction_NONE) &&
1526 nValAsInt <= int(css::sheet::GeneralFunction_VARP));
1527 aRet <<= static_cast<sheet::GeneralFunction>(nValAsInt);
1528 }
1529 else if ( aPropertyName == SC_UNO_DP_FUNCTION2 )
1530 {
1531 ScGeneralFunction eVal = getFunction();
1532 aRet <<= static_cast<sal_Int16>(eVal);
1533 }
1534 else if ( aPropertyName == SC_UNO_DP_REFVALUE )
1535 aRet <<= aReferenceValue;
1536 else if ( aPropertyName == SC_UNO_DP_ISDATALAYOUT ) // read-only properties
1537 aRet <<= getIsDataLayoutDimension();
1538 else if ( aPropertyName == SC_UNO_DP_NUMBERFO )
1539 {
1540 sal_Int32 nFormat = 0;
1541 ScGeneralFunction eFunc = getFunction();
1542 // #i63745# don't use source format for "count"
1543 if ( eFunc != ScGeneralFunction::COUNT && eFunc != ScGeneralFunction::COUNTNUMS )
1544 nFormat = pSource->GetData()->GetNumberFormat( ( nSourceDim >= 0 ) ? nSourceDim : nDim );
1545
1546 switch ( aReferenceValue.ReferenceType )
1547 {
1548 case sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE:
1549 case sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE:
1550 case sheet::DataPilotFieldReferenceType::ROW_PERCENTAGE:
1551 case sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE:
1552 case sheet::DataPilotFieldReferenceType::TOTAL_PERCENTAGE:
1553 nFormat = pSource->GetData()->GetNumberFormatByIdx( NF_PERCENT_DEC2 );
1554 break;
1555 case sheet::DataPilotFieldReferenceType::INDEX:
1556 nFormat = pSource->GetData()->GetNumberFormatByIdx( NF_NUMBER_SYSTEM );
1557 break;
1558 default:
1559 break;
1560 }
1561
1562 aRet <<= nFormat;
1563 }
1564 else if ( aPropertyName == SC_UNO_DP_ORIGINAL )
1565 {
1566 uno::Reference<container::XNamed> xOriginal;
1567 if (nSourceDim >= 0)
1568 xOriginal = pSource->GetDimensionsObject()->getByIndex(nSourceDim);
1569 aRet <<= xOriginal;
1570 }
1571 else if (aPropertyName == SC_UNO_DP_ORIGINAL_POS)
1572 {
1573 aRet <<= nSourceDim;
1574 }
1575 else if ( aPropertyName == SC_UNO_DP_FILTER )
1576 {
1577 if ( bHasSelectedPage )
1578 {
1579 // single filter field: first field equal to selected string
1580 sheet::TableFilterField aField( sheet::FilterConnection_AND, 0,
1581 sheet::FilterOperator_EQUAL, false, 0.0, aSelectedPage );
1582 aRet <<= uno::Sequence<sheet::TableFilterField>( &aField, 1 );
1583 }
1584 else
1585 aRet <<= uno::Sequence<sheet::TableFilterField>(0);
1586 }
1587 else if (aPropertyName == SC_UNO_DP_LAYOUTNAME)
1588 aRet <<= mpLayoutName ? *mpLayoutName : OUString();
1589 else if (aPropertyName == SC_UNO_DP_FIELD_SUBTOTALNAME)
1590 aRet <<= mpSubtotalName ? *mpSubtotalName : OUString();
1591 else if (aPropertyName == SC_UNO_DP_HAS_HIDDEN_MEMBER)
1592 aRet <<= mbHasHiddenMember;
1593 else if (aPropertyName == SC_UNO_DP_FLAGS)
1594 {
1595 aRet <<= sal_Int32(0); // tabular data: all orientations are possible
1596 }
1597 else
1598 {
1599 OSL_FAIL("unknown property");
1600 //TODO: THROW( UnknownPropertyException() );
1601 }
1602 return aRet;
1603 }
1604
SC_IMPL_DUMMY_PROPERTY_LISTENER(ScDPDimension)1605 SC_IMPL_DUMMY_PROPERTY_LISTENER( ScDPDimension )
1606
1607 ScDPHierarchies::ScDPHierarchies( ScDPSource* pSrc, tools::Long nD ) :
1608 pSource( pSrc ),
1609 nDim( nD )
1610 {
1611 //TODO: hold pSource
1612 }
1613
~ScDPHierarchies()1614 ScDPHierarchies::~ScDPHierarchies()
1615 {
1616 //TODO: release pSource
1617 }
1618
1619 // very simple XNameAccess implementation using getCount/getByIndex
1620
getByName(const OUString & aName)1621 uno::Any SAL_CALL ScDPHierarchies::getByName( const OUString& aName )
1622 {
1623 tools::Long nCount = getCount();
1624 for (tools::Long i=0; i<nCount; i++)
1625 if ( getByIndex(i)->getName() == aName )
1626 {
1627 uno::Reference<container::XNamed> xNamed = getByIndex(i);
1628 uno::Any aRet;
1629 aRet <<= xNamed;
1630 return aRet;
1631 }
1632
1633 throw container::NoSuchElementException();
1634 }
1635
getElementNames()1636 uno::Sequence<OUString> SAL_CALL ScDPHierarchies::getElementNames()
1637 {
1638 tools::Long nCount = getCount();
1639 uno::Sequence<OUString> aSeq(nCount);
1640 OUString* pArr = aSeq.getArray();
1641 for (tools::Long i=0; i<nCount; i++)
1642 pArr[i] = getByIndex(i)->getName();
1643 return aSeq;
1644 }
1645
hasByName(const OUString & aName)1646 sal_Bool SAL_CALL ScDPHierarchies::hasByName( const OUString& aName )
1647 {
1648 tools::Long nCount = getCount();
1649 for (tools::Long i=0; i<nCount; i++)
1650 if ( getByIndex(i)->getName() == aName )
1651 return true;
1652 return false;
1653 }
1654
getElementType()1655 uno::Type SAL_CALL ScDPHierarchies::getElementType()
1656 {
1657 return cppu::UnoType<container::XNamed>::get();
1658 }
1659
hasElements()1660 sal_Bool SAL_CALL ScDPHierarchies::hasElements()
1661 {
1662 return ( getCount() > 0 );
1663 }
1664
1665 // end of XNameAccess implementation
1666
getCount()1667 sal_Int32 ScDPHierarchies::getCount()
1668 {
1669 return nHierCount;
1670 }
1671
getByIndex(tools::Long nIndex) const1672 ScDPHierarchy* ScDPHierarchies::getByIndex(tools::Long nIndex) const
1673 {
1674 // pass hierarchy index to new object in case the implementation
1675 // will be extended to more than one hierarchy
1676
1677 if ( nIndex >= 0 && nIndex < nHierCount )
1678 {
1679 if ( !ppHiers )
1680 {
1681 const_cast<ScDPHierarchies*>(this)->ppHiers.reset( new rtl::Reference<ScDPHierarchy>[nHierCount] );
1682 for (sal_Int32 i=0; i<nHierCount; i++)
1683 ppHiers[i] = nullptr;
1684 }
1685 if ( !ppHiers[nIndex].is() )
1686 {
1687 ppHiers[nIndex] = new ScDPHierarchy( pSource, nDim, nIndex );
1688 }
1689
1690 return ppHiers[nIndex].get();
1691 }
1692
1693 return nullptr; //TODO: exception?
1694 }
1695
ScDPHierarchy(ScDPSource * pSrc,sal_Int32 nD,sal_Int32 nH)1696 ScDPHierarchy::ScDPHierarchy( ScDPSource* pSrc, sal_Int32 nD, sal_Int32 nH ) :
1697 pSource( pSrc ),
1698 nDim( nD ),
1699 nHier( nH )
1700 {
1701 //TODO: hold pSource
1702 }
1703
~ScDPHierarchy()1704 ScDPHierarchy::~ScDPHierarchy()
1705 {
1706 //TODO: release pSource
1707 }
1708
GetLevelsObject()1709 ScDPLevels* ScDPHierarchy::GetLevelsObject()
1710 {
1711 if (!mxLevels.is())
1712 {
1713 mxLevels = new ScDPLevels( pSource, nDim, nHier );
1714 }
1715 return mxLevels.get();
1716 }
1717
getLevels()1718 uno::Reference<container::XNameAccess> SAL_CALL ScDPHierarchy::getLevels()
1719 {
1720 return GetLevelsObject();
1721 }
1722
getName()1723 OUString SAL_CALL ScDPHierarchy::getName()
1724 {
1725 OUString aRet; //TODO: globstr-ID !!!!
1726 switch (nHier)
1727 {
1728 case SC_DAPI_HIERARCHY_FLAT:
1729 aRet = "flat";
1730 break; //TODO: name ???????
1731 case SC_DAPI_HIERARCHY_QUARTER:
1732 aRet = "Quarter";
1733 break; //TODO: name ???????
1734 case SC_DAPI_HIERARCHY_WEEK:
1735 aRet = "Week";
1736 break; //TODO: name ???????
1737 default:
1738 OSL_FAIL( "ScDPHierarchy::getName: unexpected hierarchy" );
1739 break;
1740 }
1741 return aRet;
1742 }
1743
setName(const OUString &)1744 void SAL_CALL ScDPHierarchy::setName( const OUString& /* rNewName */ )
1745 {
1746 OSL_FAIL("not implemented"); //TODO: exception?
1747 }
1748
ScDPLevels(ScDPSource * pSrc,sal_Int32 nD,sal_Int32 nH)1749 ScDPLevels::ScDPLevels( ScDPSource* pSrc, sal_Int32 nD, sal_Int32 nH ) :
1750 pSource( pSrc ),
1751 nDim( nD ),
1752 nHier( nH )
1753 {
1754 //TODO: hold pSource
1755
1756 // text columns have only one level
1757
1758 tools::Long nSrcDim = pSource->GetSourceDim( nDim );
1759 if ( pSource->IsDateDimension( nSrcDim ) )
1760 {
1761 switch ( nHier )
1762 {
1763 case SC_DAPI_HIERARCHY_FLAT: nLevCount = SC_DAPI_FLAT_LEVELS; break;
1764 case SC_DAPI_HIERARCHY_QUARTER: nLevCount = SC_DAPI_QUARTER_LEVELS; break;
1765 case SC_DAPI_HIERARCHY_WEEK: nLevCount = SC_DAPI_WEEK_LEVELS; break;
1766 default:
1767 OSL_FAIL("wrong hierarchy");
1768 nLevCount = 0;
1769 }
1770 }
1771 else
1772 nLevCount = 1;
1773 }
1774
~ScDPLevels()1775 ScDPLevels::~ScDPLevels()
1776 {
1777 //TODO: release pSource
1778 }
1779
1780 // very simple XNameAccess implementation using getCount/getByIndex
1781
getByName(const OUString & aName)1782 uno::Any SAL_CALL ScDPLevels::getByName( const OUString& aName )
1783 {
1784 tools::Long nCount = getCount();
1785 for (tools::Long i=0; i<nCount; i++)
1786 if ( getByIndex(i)->getName() == aName )
1787 {
1788 uno::Reference<container::XNamed> xNamed = getByIndex(i);
1789 uno::Any aRet;
1790 aRet <<= xNamed;
1791 return aRet;
1792 }
1793
1794 throw container::NoSuchElementException();
1795 }
1796
getElementNames()1797 uno::Sequence<OUString> SAL_CALL ScDPLevels::getElementNames()
1798 {
1799 tools::Long nCount = getCount();
1800 uno::Sequence<OUString> aSeq(nCount);
1801 OUString* pArr = aSeq.getArray();
1802 for (tools::Long i=0; i<nCount; i++)
1803 pArr[i] = getByIndex(i)->getName();
1804 return aSeq;
1805 }
1806
hasByName(const OUString & aName)1807 sal_Bool SAL_CALL ScDPLevels::hasByName( const OUString& aName )
1808 {
1809 tools::Long nCount = getCount();
1810 for (tools::Long i=0; i<nCount; i++)
1811 if ( getByIndex(i)->getName() == aName )
1812 return true;
1813 return false;
1814 }
1815
getElementType()1816 uno::Type SAL_CALL ScDPLevels::getElementType()
1817 {
1818 return cppu::UnoType<container::XNamed>::get();
1819 }
1820
hasElements()1821 sal_Bool SAL_CALL ScDPLevels::hasElements()
1822 {
1823 return ( getCount() > 0 );
1824 }
1825
1826 // end of XNameAccess implementation
1827
getCount() const1828 sal_Int32 ScDPLevels::getCount() const
1829 {
1830 return nLevCount;
1831 }
1832
getByIndex(sal_Int32 nIndex) const1833 ScDPLevel* ScDPLevels::getByIndex(sal_Int32 nIndex) const
1834 {
1835 if ( nIndex >= 0 && nIndex < nLevCount )
1836 {
1837 if ( !ppLevs )
1838 {
1839 const_cast<ScDPLevels*>(this)->ppLevs.reset(new rtl::Reference<ScDPLevel>[nLevCount] );
1840 for (tools::Long i=0; i<nLevCount; i++)
1841 ppLevs[i] = nullptr;
1842 }
1843 if ( !ppLevs[nIndex].is() )
1844 {
1845 ppLevs[nIndex] = new ScDPLevel( pSource, nDim, nHier, nIndex );
1846 }
1847
1848 return ppLevs[nIndex].get();
1849 }
1850
1851 return nullptr; //TODO: exception?
1852 }
1853
1854 namespace {
1855
1856 class ScDPGlobalMembersOrder
1857 {
1858 ScDPLevel& rLevel;
1859 bool bAscending;
1860
1861 public:
ScDPGlobalMembersOrder(ScDPLevel & rLev,bool bAsc)1862 ScDPGlobalMembersOrder( ScDPLevel& rLev, bool bAsc ) :
1863 rLevel(rLev),
1864 bAscending(bAsc)
1865 {}
1866
1867 bool operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const;
1868 };
1869
1870 }
1871
operator ()(sal_Int32 nIndex1,sal_Int32 nIndex2) const1872 bool ScDPGlobalMembersOrder::operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const
1873 {
1874 sal_Int32 nCompare = 0;
1875 // seems that some ::std::sort() implementations pass the same index twice
1876 if( nIndex1 != nIndex2 )
1877 {
1878 ScDPMembers* pMembers = rLevel.GetMembersObject();
1879 ScDPMember* pMember1 = pMembers->getByIndex(nIndex1);
1880 ScDPMember* pMember2 = pMembers->getByIndex(nIndex2);
1881 nCompare = pMember1->Compare( *pMember2 );
1882 }
1883 return bAscending ? (nCompare < 0) : (nCompare > 0);
1884 }
1885
ScDPLevel(ScDPSource * pSrc,sal_Int32 nD,sal_Int32 nH,sal_Int32 nL)1886 ScDPLevel::ScDPLevel( ScDPSource* pSrc, sal_Int32 nD, sal_Int32 nH, sal_Int32 nL ) :
1887 pSource( pSrc ),
1888 nDim( nD ),
1889 nHier( nH ),
1890 nLev( nL ),
1891 aSortInfo( EMPTY_OUSTRING, true, sheet::DataPilotFieldSortMode::NAME ), // default: sort by name
1892 nSortMeasure( 0 ),
1893 nAutoMeasure( 0 ),
1894 bShowEmpty( false ),
1895 bEnableLayout( false ),
1896 bRepeatItemLabels( false )
1897 {
1898 //TODO: hold pSource
1899 // aSubTotals is empty
1900 }
1901
~ScDPLevel()1902 ScDPLevel::~ScDPLevel()
1903 {
1904 //TODO: release pSource
1905 }
1906
EvaluateSortOrder()1907 void ScDPLevel::EvaluateSortOrder()
1908 {
1909 switch (aSortInfo.Mode)
1910 {
1911 case sheet::DataPilotFieldSortMode::DATA:
1912 {
1913 // find index of measure (index among data dimensions)
1914
1915 tools::Long nMeasureCount = pSource->GetDataDimensionCount();
1916 for (tools::Long nMeasure=0; nMeasure<nMeasureCount; nMeasure++)
1917 {
1918 if (pSource->GetDataDimName(nMeasure) == aSortInfo.Field)
1919 {
1920 nSortMeasure = nMeasure;
1921 break;
1922 }
1923 }
1924
1925 //TODO: error if not found?
1926 }
1927 break;
1928 case sheet::DataPilotFieldSortMode::MANUAL:
1929 case sheet::DataPilotFieldSortMode::NAME:
1930 {
1931 ScDPMembers* pLocalMembers = GetMembersObject();
1932 tools::Long nCount = pLocalMembers->getCount();
1933
1934 aGlobalOrder.resize( nCount );
1935 for (tools::Long nPos=0; nPos<nCount; nPos++)
1936 aGlobalOrder[nPos] = nPos;
1937
1938 // allow manual or name (manual is always ascending)
1939 bool bAscending = ( aSortInfo.Mode == sheet::DataPilotFieldSortMode::MANUAL || aSortInfo.IsAscending );
1940 ScDPGlobalMembersOrder aComp( *this, bAscending );
1941 ::std::sort( aGlobalOrder.begin(), aGlobalOrder.end(), aComp );
1942 }
1943 break;
1944 }
1945
1946 if ( !aAutoShowInfo.IsEnabled )
1947 return;
1948
1949 // find index of measure (index among data dimensions)
1950
1951 tools::Long nMeasureCount = pSource->GetDataDimensionCount();
1952 for (tools::Long nMeasure=0; nMeasure<nMeasureCount; nMeasure++)
1953 {
1954 if (pSource->GetDataDimName(nMeasure) == aAutoShowInfo.DataField)
1955 {
1956 nAutoMeasure = nMeasure;
1957 break;
1958 }
1959 }
1960
1961 //TODO: error if not found?
1962 }
1963
SetEnableLayout(bool bSet)1964 void ScDPLevel::SetEnableLayout(bool bSet)
1965 {
1966 bEnableLayout = bSet;
1967 }
1968
GetMembersObject()1969 ScDPMembers* ScDPLevel::GetMembersObject()
1970 {
1971 if (!mxMembers.is())
1972 {
1973 mxMembers = new ScDPMembers( pSource, nDim, nHier, nLev );
1974 }
1975 return mxMembers.get();
1976 }
1977
getMembers()1978 uno::Reference<sheet::XMembersAccess> SAL_CALL ScDPLevel::getMembers()
1979 {
1980 return GetMembersObject();
1981 }
1982
getResults()1983 uno::Sequence<sheet::MemberResult> SAL_CALL ScDPLevel::getResults()
1984 {
1985 const uno::Sequence<sheet::MemberResult>* pRes = pSource->GetMemberResults( this );
1986 if (pRes)
1987 return *pRes;
1988
1989 return {}; //TODO: Error?
1990 }
1991
getName()1992 OUString SAL_CALL ScDPLevel::getName()
1993 {
1994 tools::Long nSrcDim = pSource->GetSourceDim( nDim );
1995 if ( pSource->IsDateDimension( nSrcDim ) )
1996 {
1997 OUString aRet; //TODO: globstr-ID !!!!
1998
1999 if ( nHier == SC_DAPI_HIERARCHY_QUARTER )
2000 {
2001 switch ( nLev )
2002 {
2003 case SC_DAPI_LEVEL_YEAR:
2004 aRet = "Year";
2005 break;
2006 case SC_DAPI_LEVEL_QUARTER:
2007 aRet = "Quarter";
2008 break;
2009 case SC_DAPI_LEVEL_MONTH:
2010 aRet = "Month";
2011 break;
2012 case SC_DAPI_LEVEL_DAY:
2013 aRet = "Day";
2014 break;
2015 default:
2016 OSL_FAIL( "ScDPLevel::getName: unexpected level" );
2017 break;
2018 }
2019 }
2020 else if ( nHier == SC_DAPI_HIERARCHY_WEEK )
2021 {
2022 switch ( nLev )
2023 {
2024 case SC_DAPI_LEVEL_YEAR:
2025 aRet = "Year";
2026 break;
2027 case SC_DAPI_LEVEL_WEEK:
2028 aRet = "Week";
2029 break;
2030 case SC_DAPI_LEVEL_WEEKDAY:
2031 aRet = "Weekday";
2032 break;
2033 default:
2034 OSL_FAIL( "ScDPLevel::getName: unexpected level" );
2035 break;
2036 }
2037 }
2038 if (!aRet.isEmpty())
2039 return aRet;
2040 }
2041
2042 ScDPDimension* pDim = pSource->GetDimensionsObject()->getByIndex(nSrcDim);
2043 if (!pDim)
2044 return OUString();
2045
2046 return pDim->getName();
2047 }
2048
setName(const OUString &)2049 void SAL_CALL ScDPLevel::setName( const OUString& /* rNewName */ )
2050 {
2051 OSL_FAIL("not implemented"); //TODO: exception?
2052 }
2053
getSubTotals() const2054 uno::Sequence<sal_Int16> ScDPLevel::getSubTotals() const
2055 {
2056 //TODO: separate functions for settings and evaluation?
2057
2058 tools::Long nSrcDim = pSource->GetSourceDim( nDim );
2059 if ( !pSource->SubTotalAllowed( nSrcDim ) )
2060 return {};
2061
2062 return aSubTotals;
2063 }
2064
2065 // XPropertySet
2066
getPropertySetInfo()2067 uno::Reference<beans::XPropertySetInfo> SAL_CALL ScDPLevel::getPropertySetInfo()
2068 {
2069 SolarMutexGuard aGuard;
2070
2071 static const SfxItemPropertyMapEntry aDPLevelMap_Impl[] =
2072 {
2073 //TODO: change type of AutoShow/Layout/Sorting to API struct when available
2074 { u"" SC_UNO_DP_AUTOSHOW, 0, cppu::UnoType<sheet::DataPilotFieldAutoShowInfo>::get(), 0, 0 },
2075 { u"" SC_UNO_DP_LAYOUT, 0, cppu::UnoType<sheet::DataPilotFieldLayoutInfo>::get(), 0, 0 },
2076 { u"" SC_UNO_DP_SHOWEMPTY, 0, cppu::UnoType<bool>::get(), 0, 0 },
2077 { u"" SC_UNO_DP_REPEATITEMLABELS, 0, cppu::UnoType<bool>::get(), 0, 0 },
2078 { u"" SC_UNO_DP_SORTING, 0, cppu::UnoType<sheet::DataPilotFieldSortInfo>::get(), 0, 0 },
2079 { u"" SC_UNO_DP_SUBTOTAL, 0, cppu::UnoType<uno::Sequence<sheet::GeneralFunction>>::get(), 0, 0 },
2080 { u"" SC_UNO_DP_SUBTOTAL2, 0, cppu::UnoType<uno::Sequence<sal_Int16>>::get(), 0, 0 },
2081 { u"", 0, css::uno::Type(), 0, 0 }
2082 };
2083 static uno::Reference<beans::XPropertySetInfo> aRef =
2084 new SfxItemPropertySetInfo( aDPLevelMap_Impl );
2085 return aRef;
2086 }
2087
setPropertyValue(const OUString & aPropertyName,const uno::Any & aValue)2088 void SAL_CALL ScDPLevel::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue )
2089 {
2090 if ( aPropertyName == SC_UNO_DP_SHOWEMPTY )
2091 bShowEmpty = lcl_GetBoolFromAny(aValue);
2092 else if ( aPropertyName == SC_UNO_DP_REPEATITEMLABELS )
2093 bRepeatItemLabels = lcl_GetBoolFromAny(aValue);
2094 else if ( aPropertyName == SC_UNO_DP_SUBTOTAL )
2095 {
2096 uno::Sequence<sheet::GeneralFunction> aSeq;
2097 aValue >>= aSeq;
2098 aSubTotals.realloc(aSeq.getLength());
2099 std::transform(aSeq.begin(), aSeq.end(), aSubTotals.begin(),
2100 [](const sheet::GeneralFunction& rFunc) -> sal_Int16 {
2101 return static_cast<sal_Int16>(rFunc); });
2102 }
2103 else if ( aPropertyName == SC_UNO_DP_SUBTOTAL2 )
2104 aValue >>= aSubTotals;
2105 else if ( aPropertyName == SC_UNO_DP_SORTING )
2106 aValue >>= aSortInfo;
2107 else if ( aPropertyName == SC_UNO_DP_AUTOSHOW )
2108 aValue >>= aAutoShowInfo;
2109 else if ( aPropertyName == SC_UNO_DP_LAYOUT )
2110 aValue >>= aLayoutInfo;
2111 else
2112 {
2113 OSL_FAIL("unknown property");
2114 }
2115 }
2116
getPropertyValue(const OUString & aPropertyName)2117 uno::Any SAL_CALL ScDPLevel::getPropertyValue( const OUString& aPropertyName )
2118 {
2119 uno::Any aRet;
2120 if ( aPropertyName == SC_UNO_DP_SHOWEMPTY )
2121 aRet <<= bShowEmpty;
2122 else if ( aPropertyName == SC_UNO_DP_REPEATITEMLABELS )
2123 aRet <<= bRepeatItemLabels;
2124 else if ( aPropertyName == SC_UNO_DP_SUBTOTAL )
2125 {
2126 const uno::Sequence<sal_Int16> aSeq = getSubTotals();
2127 uno::Sequence<sheet::GeneralFunction> aNewSeq;
2128 aNewSeq.realloc(aSeq.getLength());
2129 std::transform(aSeq.begin(), aSeq.end(), aNewSeq.begin(),
2130 [](const sal_Int16 nFunc) -> sheet::GeneralFunction {
2131 if (nFunc == sheet::GeneralFunction2::MEDIAN)
2132 return sheet::GeneralFunction_NONE;
2133 return static_cast<sheet::GeneralFunction>(nFunc);
2134 });
2135
2136 aRet <<= aNewSeq;
2137 }
2138 else if ( aPropertyName == SC_UNO_DP_SUBTOTAL2 )
2139 {
2140 uno::Sequence<sal_Int16> aSeq = getSubTotals(); //TODO: avoid extra copy?
2141 aRet <<= aSeq;
2142 }
2143 else if ( aPropertyName == SC_UNO_DP_SORTING )
2144 aRet <<= aSortInfo;
2145 else if ( aPropertyName == SC_UNO_DP_AUTOSHOW )
2146 aRet <<= aAutoShowInfo;
2147 else if ( aPropertyName == SC_UNO_DP_LAYOUT )
2148 aRet <<= aLayoutInfo;
2149 else if (aPropertyName == SC_UNO_DP_LAYOUTNAME)
2150 {
2151 // read only property
2152 tools::Long nSrcDim = pSource->GetSourceDim(nDim);
2153 ScDPDimension* pDim = pSource->GetDimensionsObject()->getByIndex(nSrcDim);
2154 if (!pDim)
2155 return aRet;
2156
2157 const std::optional<OUString> & pLayoutName = pDim->GetLayoutName();
2158 if (!pLayoutName)
2159 return aRet;
2160
2161 aRet <<= *pLayoutName;
2162 }
2163 else
2164 {
2165 OSL_FAIL("unknown property");
2166 }
2167 return aRet;
2168 }
2169
SC_IMPL_DUMMY_PROPERTY_LISTENER(ScDPLevel)2170 SC_IMPL_DUMMY_PROPERTY_LISTENER( ScDPLevel )
2171
2172 ScDPMembers::ScDPMembers( ScDPSource* pSrc, sal_Int32 nD, sal_Int32 nH, sal_Int32 nL ) :
2173 pSource( pSrc ),
2174 nDim( nD ),
2175 nHier( nH ),
2176 nLev( nL )
2177 {
2178 //TODO: hold pSource
2179
2180 tools::Long nSrcDim = pSource->GetSourceDim( nDim );
2181 if ( pSource->IsDataLayoutDimension(nSrcDim) )
2182 nMbrCount = pSource->GetDataDimensionCount();
2183 else if ( nHier != SC_DAPI_HIERARCHY_FLAT && pSource->IsDateDimension( nSrcDim ) )
2184 {
2185 nMbrCount = 0;
2186 if ( nHier == SC_DAPI_HIERARCHY_QUARTER )
2187 {
2188 switch (nLev)
2189 {
2190 case SC_DAPI_LEVEL_YEAR:
2191 {
2192 const ScDPItemData* pLastNumData = nullptr;
2193 for ( SCROW n = 0; n < static_cast<SCROW>(pSource->GetData()->GetColumnEntries(nDim).size()); n-- )
2194 {
2195 const ScDPItemData* pData = GetSrcItemDataByIndex( n );
2196 if ( pData && pData->HasStringData() )
2197 break;
2198 else
2199 pLastNumData = pData;
2200 }
2201
2202 if ( pLastNumData )
2203 {
2204 const ScDPItemData* pFirstData = GetSrcItemDataByIndex( 0 );
2205 double fFirstVal = pFirstData->GetValue();
2206 double fLastVal = pLastNumData->GetValue();
2207
2208 tools::Long nFirstYear = pSource->GetData()->GetDatePart(
2209 static_cast<tools::Long>(::rtl::math::approxFloor( fFirstVal )),
2210 nHier, nLev );
2211 tools::Long nLastYear = pSource->GetData()->GetDatePart(
2212 static_cast<tools::Long>(::rtl::math::approxFloor( fLastVal )),
2213 nHier, nLev );
2214
2215 nMbrCount = nLastYear + 1 - nFirstYear;
2216 }
2217 else
2218 nMbrCount = 0; // no values
2219 }
2220 break;
2221 case SC_DAPI_LEVEL_QUARTER: nMbrCount = 4; break;
2222 case SC_DAPI_LEVEL_MONTH: nMbrCount = 12; break;
2223 case SC_DAPI_LEVEL_DAY: nMbrCount = 31; break;
2224 default:
2225 OSL_FAIL( "ScDPMembers::ScDPMembers: unexpected level" );
2226 break;
2227 }
2228 }
2229 else if ( nHier == SC_DAPI_HIERARCHY_WEEK )
2230 {
2231 switch (nLev)
2232 {
2233 case SC_DAPI_LEVEL_YEAR: nMbrCount = 1; break; //TODO: get years from source
2234 case SC_DAPI_LEVEL_WEEK: nMbrCount = 53; break;
2235 case SC_DAPI_LEVEL_WEEKDAY: nMbrCount = 7; break;
2236 default:
2237 OSL_FAIL( "ScDPMembers::ScDPMembers: unexpected level" );
2238 break;
2239 }
2240 }
2241 }
2242 else
2243 nMbrCount = pSource->GetData()->GetMembersCount( nSrcDim );
2244 }
2245
~ScDPMembers()2246 ScDPMembers::~ScDPMembers()
2247 {
2248 }
2249
2250 // XNameAccess implementation using getCount/getByIndex
2251
GetIndexFromName(const OUString & rName) const2252 sal_Int32 ScDPMembers::GetIndexFromName( const OUString& rName ) const
2253 {
2254 if ( aHashMap.empty() )
2255 {
2256 // store the index for each name
2257
2258 sal_Int32 nCount = getCount();
2259 for (sal_Int32 i=0; i<nCount; i++)
2260 aHashMap[ getByIndex(i)->getName() ] = i;
2261 }
2262
2263 ScDPMembersHashMap::const_iterator aIter = aHashMap.find( rName );
2264 if ( aIter != aHashMap.end() )
2265 return aIter->second; // found index
2266 else
2267 return -1; // not found
2268 }
2269
getByName(const OUString & aName)2270 uno::Any SAL_CALL ScDPMembers::getByName( const OUString& aName )
2271 {
2272 sal_Int32 nIndex = GetIndexFromName( aName );
2273 if ( nIndex >= 0 )
2274 {
2275 uno::Reference<container::XNamed> xNamed = getByIndex(nIndex);
2276 uno::Any aRet;
2277 aRet <<= xNamed;
2278 return aRet;
2279 }
2280
2281 throw container::NoSuchElementException();
2282 }
2283
getElementNames()2284 uno::Sequence<OUString> SAL_CALL ScDPMembers::getElementNames()
2285 {
2286 return getElementNames( false );
2287 }
2288
hasByName(const OUString & aName)2289 sal_Bool SAL_CALL ScDPMembers::hasByName( const OUString& aName )
2290 {
2291 return ( GetIndexFromName( aName ) >= 0 );
2292 }
2293
getElementType()2294 uno::Type SAL_CALL ScDPMembers::getElementType()
2295 {
2296 return cppu::UnoType<container::XNamed>::get();
2297 }
2298
hasElements()2299 sal_Bool SAL_CALL ScDPMembers::hasElements()
2300 {
2301 return ( getCount() > 0 );
2302 }
2303
2304 // end of XNameAccess implementation
2305
2306 // XMembersAccess implementation
2307
getLocaleIndependentElementNames()2308 uno::Sequence<OUString> SAL_CALL ScDPMembers::getLocaleIndependentElementNames()
2309 {
2310 return getElementNames( true );
2311 }
2312
2313 // end of XMembersAccess implementation
2314
getElementNames(bool bLocaleIndependent) const2315 uno::Sequence<OUString> ScDPMembers::getElementNames( bool bLocaleIndependent ) const
2316 {
2317 // Return list of names in sorted order,
2318 // so it's displayed in that order in the field options dialog.
2319 // Sorting is done at the level object (parent of this).
2320
2321 ScDPLevel* pLevel = pSource->GetDimensionsObject()->getByIndex(nDim)->
2322 GetHierarchiesObject()->getByIndex(nHier)->GetLevelsObject()->getByIndex(nLev);
2323 pLevel->EvaluateSortOrder();
2324 const std::vector<sal_Int32>& rGlobalOrder = pLevel->GetGlobalOrder();
2325 bool bSort = !rGlobalOrder.empty();
2326
2327 tools::Long nCount = getCount();
2328 uno::Sequence<OUString> aSeq(nCount);
2329 OUString* pArr = aSeq.getArray();
2330 for (tools::Long i=0; i<nCount; i++)
2331 pArr[i] = getByIndex(bSort ? rGlobalOrder[i] : i)->GetNameStr( bLocaleIndependent);
2332 return aSeq;
2333 }
2334
getMinMembers() const2335 sal_Int32 ScDPMembers::getMinMembers() const
2336 {
2337 // used in lcl_CountMinMembers
2338
2339 sal_Int32 nVisCount = 0;
2340 if (!maMembers.empty())
2341 {
2342 nVisCount = std::count_if(maMembers.begin(), maMembers.end(), [](const rtl::Reference<ScDPMember>& pMbr) {
2343 // count only visible with details (default is true for both)
2344 return !pMbr || (pMbr->isVisible() && pMbr->getShowDetails()); });
2345 }
2346 else
2347 nVisCount = nMbrCount; // default for all
2348
2349 return nVisCount;
2350 }
2351
getByIndex(sal_Int32 nIndex) const2352 ScDPMember* ScDPMembers::getByIndex(sal_Int32 nIndex) const
2353 {
2354 // result of GetColumnEntries must not change between ScDPMembers ctor
2355 // and all calls to getByIndex
2356
2357 if ( nIndex >= 0 && nIndex < nMbrCount )
2358 {
2359 if (maMembers.empty())
2360 maMembers.resize(nMbrCount);
2361
2362 if (!maMembers[nIndex])
2363 {
2364 rtl::Reference<ScDPMember> pNew;
2365 sal_Int32 nSrcDim = pSource->GetSourceDim( nDim );
2366 if ( pSource->IsDataLayoutDimension(nSrcDim) )
2367 {
2368 // empty name (never shown, not used for lookup)
2369 pNew.set(new ScDPMember(pSource, nDim, nHier, nLev, 0));
2370 }
2371 else if ( nHier != SC_DAPI_HIERARCHY_FLAT && pSource->IsDateDimension( nSrcDim ) )
2372 {
2373 sal_Int32 nGroupBy = 0;
2374 sal_Int32 nVal = 0;
2375 OUString aName;
2376
2377 if ( nLev == SC_DAPI_LEVEL_YEAR ) // YEAR is in both hierarchies
2378 {
2379 //TODO: cache year range here!
2380
2381 double fFirstVal = pSource->GetData()->GetMemberByIndex( nSrcDim, 0 )->GetValue();
2382 tools::Long nFirstYear = pSource->GetData()->GetDatePart(
2383 static_cast<tools::Long>(::rtl::math::approxFloor( fFirstVal )),
2384 nHier, nLev );
2385
2386 nVal = nFirstYear + nIndex;
2387 }
2388 else if ( nHier == SC_DAPI_HIERARCHY_WEEK && nLev == SC_DAPI_LEVEL_WEEKDAY )
2389 {
2390 nVal = nIndex; // DayOfWeek is 0-based
2391 aName = ScGlobal::GetCalendar()->getDisplayName(
2392 css::i18n::CalendarDisplayIndex::DAY,
2393 sal::static_int_cast<sal_Int16>(nVal), 0 );
2394 }
2395 else if ( nHier == SC_DAPI_HIERARCHY_QUARTER && nLev == SC_DAPI_LEVEL_MONTH )
2396 {
2397 nVal = nIndex; // Month is 0-based
2398 aName = ScGlobal::GetCalendar()->getDisplayName(
2399 css::i18n::CalendarDisplayIndex::MONTH,
2400 sal::static_int_cast<sal_Int16>(nVal), 0 );
2401 }
2402 else
2403 nVal = nIndex + 1; // Quarter, Day, Week are 1-based
2404
2405 switch (nLev)
2406 {
2407 case SC_DAPI_LEVEL_YEAR:
2408 nGroupBy = sheet::DataPilotFieldGroupBy::YEARS;
2409 break;
2410 case SC_DAPI_LEVEL_QUARTER:
2411 case SC_DAPI_LEVEL_WEEK:
2412 nGroupBy = sheet::DataPilotFieldGroupBy::QUARTERS;
2413 break;
2414 case SC_DAPI_LEVEL_MONTH:
2415 case SC_DAPI_LEVEL_WEEKDAY:
2416 nGroupBy = sheet::DataPilotFieldGroupBy::MONTHS;
2417 break;
2418 case SC_DAPI_LEVEL_DAY:
2419 nGroupBy = sheet::DataPilotFieldGroupBy::DAYS;
2420 break;
2421 default:
2422 ;
2423 }
2424 if (aName.isEmpty())
2425 aName = OUString::number(nVal);
2426
2427 ScDPItemData aData(nGroupBy, nVal);
2428 SCROW nId = pSource->GetCache()->GetIdByItemData(nDim, aData);
2429 pNew.set(new ScDPMember(pSource, nDim, nHier, nLev, nId));
2430 }
2431 else
2432 {
2433 const std::vector<SCROW>& memberIndexs = pSource->GetData()->GetColumnEntries(nSrcDim);
2434 pNew.set(new ScDPMember(pSource, nDim, nHier, nLev, memberIndexs[nIndex]));
2435 }
2436 maMembers[nIndex] = pNew;
2437 }
2438
2439 return maMembers[nIndex].get();
2440 }
2441
2442 return nullptr; //TODO: exception?
2443 }
2444
ScDPMember(ScDPSource * pSrc,sal_Int32 nD,sal_Int32 nH,sal_Int32 nL,SCROW nIndex)2445 ScDPMember::ScDPMember(
2446 ScDPSource* pSrc, sal_Int32 nD, sal_Int32 nH, sal_Int32 nL, SCROW nIndex) :
2447 pSource( pSrc ),
2448 nDim( nD ),
2449 nHier( nH ),
2450 nLev( nL ),
2451 mnDataId( nIndex ),
2452 nPosition( -1 ),
2453 bVisible( true ),
2454 bShowDet( true )
2455 {
2456 //TODO: hold pSource
2457 }
2458
~ScDPMember()2459 ScDPMember::~ScDPMember()
2460 {
2461 //TODO: release pSource
2462 }
2463
IsNamedItem(SCROW nIndex) const2464 bool ScDPMember::IsNamedItem(SCROW nIndex) const
2465 {
2466 sal_Int32 nSrcDim = pSource->GetSourceDim( nDim );
2467 if ( nHier != SC_DAPI_HIERARCHY_FLAT && pSource->IsDateDimension( nSrcDim ) )
2468 {
2469 const ScDPItemData* pData = pSource->GetCache()->GetItemDataById(nDim, nIndex);
2470 if (pData->IsValue())
2471 {
2472 tools::Long nComp = pSource->GetData()->GetDatePart(
2473 static_cast<tools::Long>(::rtl::math::approxFloor( pData->GetValue() )),
2474 nHier, nLev );
2475 // fValue is converted from integer, so simple comparison works
2476 const ScDPItemData* pData2 = GetItemData();
2477 return pData2 && nComp == pData2->GetValue();
2478 }
2479 }
2480
2481 return nIndex == mnDataId;
2482 }
2483
Compare(const ScDPMember & rOther) const2484 sal_Int32 ScDPMember::Compare( const ScDPMember& rOther ) const
2485 {
2486 if ( nPosition >= 0 )
2487 {
2488 if ( rOther.nPosition >= 0 )
2489 {
2490 OSL_ENSURE( nPosition != rOther.nPosition, "same position for two members" );
2491 return ( nPosition < rOther.nPosition ) ? -1 : 1;
2492 }
2493 else
2494 {
2495 // only this has a position - members with specified positions come before those without
2496 return -1;
2497 }
2498 }
2499 else if ( rOther.nPosition >= 0 )
2500 {
2501 // only rOther has a position
2502 return 1;
2503 }
2504
2505 // no positions set - compare names
2506 return pSource->GetData()->Compare( pSource->GetSourceDim(nDim),mnDataId,rOther.GetItemDataId());
2507 }
2508
FillItemData() const2509 ScDPItemData ScDPMember::FillItemData() const
2510 {
2511 //TODO: handle date hierarchy...
2512
2513 const ScDPItemData* pData = GetItemData();
2514 return (pData ? *pData : ScDPItemData());
2515 }
2516
GetLayoutName() const2517 const std::optional<OUString> & ScDPMember::GetLayoutName() const
2518 {
2519 return mpLayoutName;
2520 }
2521
GetNameStr(bool bLocaleIndependent) const2522 OUString ScDPMember::GetNameStr( bool bLocaleIndependent ) const
2523 {
2524 const ScDPItemData* pData = GetItemData();
2525 if (pData)
2526 return pSource->GetData()->GetFormattedString(nDim, *pData, bLocaleIndependent);
2527 return OUString();
2528 }
2529
getName()2530 OUString SAL_CALL ScDPMember::getName()
2531 {
2532 return GetNameStr( false );
2533 }
2534
setName(const OUString &)2535 void SAL_CALL ScDPMember::setName( const OUString& /* rNewName */ )
2536 {
2537 OSL_FAIL("not implemented"); //TODO: exception?
2538 }
2539
2540 // XPropertySet
2541
getPropertySetInfo()2542 uno::Reference<beans::XPropertySetInfo> SAL_CALL ScDPMember::getPropertySetInfo()
2543 {
2544 SolarMutexGuard aGuard;
2545
2546 static const SfxItemPropertyMapEntry aDPMemberMap_Impl[] =
2547 {
2548 { u"" SC_UNO_DP_ISVISIBLE, 0, cppu::UnoType<bool>::get(), 0, 0 },
2549 { u"" SC_UNO_DP_POSITION, 0, cppu::UnoType<sal_Int32>::get(), 0, 0 },
2550 { u"" SC_UNO_DP_SHOWDETAILS, 0, cppu::UnoType<bool>::get(), 0, 0 },
2551 { u"" SC_UNO_DP_LAYOUTNAME, 0, cppu::UnoType<OUString>::get(), 0, 0 },
2552 { u"", 0, css::uno::Type(), 0, 0 }
2553 };
2554 static uno::Reference<beans::XPropertySetInfo> aRef =
2555 new SfxItemPropertySetInfo( aDPMemberMap_Impl );
2556 return aRef;
2557 }
2558
setPropertyValue(const OUString & aPropertyName,const uno::Any & aValue)2559 void SAL_CALL ScDPMember::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue )
2560 {
2561 if ( aPropertyName == SC_UNO_DP_ISVISIBLE )
2562 bVisible = lcl_GetBoolFromAny(aValue);
2563 else if ( aPropertyName == SC_UNO_DP_SHOWDETAILS )
2564 bShowDet = lcl_GetBoolFromAny(aValue);
2565 else if ( aPropertyName == SC_UNO_DP_POSITION )
2566 aValue >>= nPosition;
2567 else if (aPropertyName == SC_UNO_DP_LAYOUTNAME)
2568 {
2569 OUString aName;
2570 if (aValue >>= aName)
2571 mpLayoutName = aName;
2572 }
2573 else
2574 {
2575 OSL_FAIL("unknown property");
2576 }
2577 }
2578
getPropertyValue(const OUString & aPropertyName)2579 uno::Any SAL_CALL ScDPMember::getPropertyValue( const OUString& aPropertyName )
2580 {
2581 uno::Any aRet;
2582 if ( aPropertyName == SC_UNO_DP_ISVISIBLE )
2583 aRet <<= bVisible;
2584 else if ( aPropertyName == SC_UNO_DP_SHOWDETAILS )
2585 aRet <<= bShowDet;
2586 else if ( aPropertyName == SC_UNO_DP_POSITION )
2587 aRet <<= nPosition;
2588 else if (aPropertyName == SC_UNO_DP_LAYOUTNAME)
2589 aRet <<= mpLayoutName ? *mpLayoutName : OUString();
2590 else
2591 {
2592 OSL_FAIL("unknown property");
2593 }
2594 return aRet;
2595 }
2596
SC_IMPL_DUMMY_PROPERTY_LISTENER(ScDPMember) const2597 SC_IMPL_DUMMY_PROPERTY_LISTENER( ScDPMember )
2598
2599 const ScDPCache* ScDPSource::GetCache()
2600 {
2601 OSL_ENSURE( GetData() , "empty ScDPTableData pointer");
2602 return ( GetData()!=nullptr ) ? &GetData()->GetCacheTable().getCache() : nullptr ;
2603 }
2604
GetItemData() const2605 const ScDPItemData* ScDPMember::GetItemData() const
2606 {
2607 const ScDPItemData* pData = pSource->GetItemDataById(nDim, mnDataId);
2608 SAL_WARN_IF( !pData, "sc.core", "ScDPMember::GetItemData: what data? nDim " << nDim << ", mnDataId " << mnDataId);
2609 return pData;
2610 }
2611
GetItemDataById(sal_Int32 nDim,sal_Int32 nId)2612 const ScDPItemData* ScDPSource::GetItemDataById(sal_Int32 nDim, sal_Int32 nId)
2613 {
2614 return GetData()->GetMemberById(nDim, nId);
2615 }
2616
GetSrcItemDataByIndex(SCROW nIndex)2617 const ScDPItemData* ScDPMembers::GetSrcItemDataByIndex(SCROW nIndex)
2618 {
2619 const std::vector< SCROW >& memberIds = pSource->GetData()->GetColumnEntries( nDim );
2620 if ( nIndex >= static_cast<tools::Long>(memberIds.size()) || nIndex < 0 )
2621 return nullptr;
2622 SCROW nId = memberIds[ nIndex ];
2623 return pSource->GetItemDataById( nDim, nId );
2624 }
2625
2626 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2627