Gtk-WARNING gtktreestore.c:1047: Invalid column number 1 added to iter
[LibreOffice.git] / sc / source / core / data / dpobject.cxx
blobc96ccaa7de74146bec085ca0e7027b7ffdc42d98
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <docsh.hxx>
21 #include <dpcache.hxx>
22 #include <dpobject.hxx>
23 #include <dptabsrc.hxx>
24 #include <dpsave.hxx>
25 #include <dpdimsave.hxx>
26 #include <dpoutput.hxx>
27 #include <dpshttab.hxx>
28 #include <dpsdbtab.hxx>
29 #include <dpgroup.hxx>
30 #include <document.hxx>
31 #include <pivot.hxx>
32 #include <dapiuno.hxx>
33 #include <miscuno.hxx>
34 #include <refupdat.hxx>
35 #include <attrib.hxx>
36 #include <scitems.hxx>
37 #include <unonames.hxx>
38 #include <dpglobal.hxx>
39 #include <globstr.hrc>
40 #include <queryentry.hxx>
41 #include <dputil.hxx>
43 #include <com/sun/star/beans/XPropertySet.hpp>
44 #include <com/sun/star/sdb/XCompletedExecution.hpp>
45 #include <com/sun/star/sdbc/DataType.hpp>
46 #include <com/sun/star/sdbc/SQLException.hpp>
47 #include <com/sun/star/sdbc/XResultSetMetaData.hpp>
48 #include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp>
49 #include <com/sun/star/sdbc/XRow.hpp>
50 #include <com/sun/star/sdbc/XRowSet.hpp>
51 #include <com/sun/star/sheet/GeneralFunction2.hpp>
52 #include <com/sun/star/sheet/DataPilotFieldFilter.hpp>
53 #include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
54 #include <com/sun/star/sheet/DataPilotFieldReferenceType.hpp>
55 #include <com/sun/star/sheet/DataPilotTableHeaderData.hpp>
56 #include <com/sun/star/sheet/DataPilotTablePositionData.hpp>
57 #include <com/sun/star/sheet/DataPilotTablePositionType.hpp>
58 #include <com/sun/star/sheet/DimensionFlags.hpp>
59 #include <com/sun/star/task/InteractionHandler.hpp>
60 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
61 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
62 #include <com/sun/star/lang/XSingleComponentFactory.hpp>
63 #include <com/sun/star/lang/XInitialization.hpp>
64 #include <com/sun/star/container/XContentEnumerationAccess.hpp>
65 #include <com/sun/star/sheet/XDrillDownDataSupplier.hpp>
67 #include <unotools/charclass.hxx>
68 #include <comphelper/processfactory.hxx>
69 #include <comphelper/string.hxx>
70 #include <comphelper/types.hxx>
71 #include <o3tl/safeint.hxx>
72 #include <sal/macros.h>
73 #include <svl/numformat.hxx>
74 #include <comphelper/diagnose_ex.hxx>
75 #include <svl/zforlist.hxx>
76 #include <utility>
77 #include <vcl/svapp.hxx>
78 #include <vcl/weld.hxx>
80 #include <vector>
81 #include <memory>
82 #include <algorithm>
84 using namespace com::sun::star;
85 using ::std::vector;
86 using ::std::shared_ptr;
87 using ::com::sun::star::uno::Sequence;
88 using ::com::sun::star::uno::Reference;
89 using ::com::sun::star::uno::UNO_QUERY;
90 using ::com::sun::star::uno::Any;
91 using ::com::sun::star::uno::Exception;
92 using ::com::sun::star::lang::XComponent;
93 using ::com::sun::star::sheet::DataPilotTableHeaderData;
94 using ::com::sun::star::sheet::DataPilotTablePositionData;
95 using ::com::sun::star::sheet::XDimensionsSupplier;
96 using ::com::sun::star::beans::XPropertySet;
98 constexpr OUStringLiteral SC_SERVICE_ROWSET = u"com.sun.star.sdb.RowSet";
100 constexpr OUStringLiteral SC_DBPROP_DATASOURCENAME = u"DataSourceName";
101 constexpr OUStringLiteral SC_DBPROP_COMMAND = u"Command";
102 constexpr OUStringLiteral SC_DBPROP_COMMANDTYPE = u"CommandType";
104 constexpr OUString SCDPSOURCE_SERVICE = u"com.sun.star.sheet.DataPilotSource"_ustr;
106 namespace {
109 * Database connection implementation for UNO database API. Note that in
110 * the UNO database API, column index is 1-based, whereas the interface
111 * requires that column index be 0-based.
113 class DBConnector : public ScDPCache::DBConnector
115 ScDPCache& mrCache;
117 uno::Reference<sdbc::XRowSet> mxRowSet;
118 uno::Reference<sdbc::XRow> mxRow;
119 uno::Reference<sdbc::XResultSetMetaData> mxMetaData;
120 Date maNullDate;
122 public:
123 DBConnector(ScDPCache& rCache, uno::Reference<sdbc::XRowSet> xRowSet, const Date& rNullDate);
125 bool isValid() const;
127 virtual void getValue(tools::Long nCol, ScDPItemData &rData, SvNumFormatType& rNumType) const override;
128 virtual OUString getColumnLabel(tools::Long nCol) const override;
129 virtual tools::Long getColumnCount() const override;
130 virtual bool first() override;
131 virtual bool next() override;
132 virtual void finish() override;
135 DBConnector::DBConnector(ScDPCache& rCache, uno::Reference<sdbc::XRowSet> xRowSet, const Date& rNullDate) :
136 mrCache(rCache), mxRowSet(std::move(xRowSet)), maNullDate(rNullDate)
138 Reference<sdbc::XResultSetMetaDataSupplier> xMetaSupp(mxRowSet, UNO_QUERY);
139 if (xMetaSupp.is())
140 mxMetaData = xMetaSupp->getMetaData();
142 mxRow.set(mxRowSet, UNO_QUERY);
145 bool DBConnector::isValid() const
147 return mxRowSet.is() && mxRow.is() && mxMetaData.is();
150 bool DBConnector::first()
152 return mxRowSet->first();
155 bool DBConnector::next()
157 return mxRowSet->next();
160 void DBConnector::finish()
162 mxRowSet->beforeFirst();
165 tools::Long DBConnector::getColumnCount() const
167 return mxMetaData->getColumnCount();
170 OUString DBConnector::getColumnLabel(tools::Long nCol) const
172 return mxMetaData->getColumnLabel(nCol+1);
175 void DBConnector::getValue(tools::Long nCol, ScDPItemData &rData, SvNumFormatType& rNumType) const
177 rNumType = SvNumFormatType::NUMBER;
178 sal_Int32 nType = mxMetaData->getColumnType(nCol+1);
182 double fValue = 0.0;
183 switch (nType)
185 case sdbc::DataType::BIT:
186 case sdbc::DataType::BOOLEAN:
188 rNumType = SvNumFormatType::LOGICAL;
189 fValue = mxRow->getBoolean(nCol+1) ? 1 : 0;
190 rData.SetValue(fValue);
191 break;
193 case sdbc::DataType::TINYINT:
194 case sdbc::DataType::SMALLINT:
195 case sdbc::DataType::INTEGER:
196 case sdbc::DataType::BIGINT:
197 case sdbc::DataType::FLOAT:
198 case sdbc::DataType::REAL:
199 case sdbc::DataType::DOUBLE:
200 case sdbc::DataType::NUMERIC:
201 case sdbc::DataType::DECIMAL:
203 //TODO: do the conversion here?
204 fValue = mxRow->getDouble(nCol+1);
205 rData.SetValue(fValue);
206 break;
208 case sdbc::DataType::DATE:
210 rNumType = SvNumFormatType::DATE;
212 util::Date aDate = mxRow->getDate(nCol+1);
213 fValue = Date(aDate.Day, aDate.Month, aDate.Year) - maNullDate;
214 rData.SetValue(fValue);
215 break;
217 case sdbc::DataType::TIME:
219 rNumType = SvNumFormatType::TIME;
221 util::Time aTime = mxRow->getTime(nCol+1);
222 fValue = aTime.Hours / static_cast<double>(::tools::Time::hourPerDay) +
223 aTime.Minutes / static_cast<double>(::tools::Time::minutePerDay) +
224 aTime.Seconds / static_cast<double>(::tools::Time::secondPerDay) +
225 aTime.NanoSeconds / static_cast<double>(::tools::Time::nanoSecPerDay);
226 rData.SetValue(fValue);
227 break;
229 case sdbc::DataType::TIMESTAMP:
231 rNumType = SvNumFormatType::DATETIME;
233 util::DateTime aStamp = mxRow->getTimestamp(nCol+1);
234 fValue = ( Date( aStamp.Day, aStamp.Month, aStamp.Year ) - maNullDate ) +
235 aStamp.Hours / static_cast<double>(::tools::Time::hourPerDay) +
236 aStamp.Minutes / static_cast<double>(::tools::Time::minutePerDay) +
237 aStamp.Seconds / static_cast<double>(::tools::Time::secondPerDay) +
238 aStamp.NanoSeconds / static_cast<double>(::tools::Time::nanoSecPerDay);
239 rData.SetValue(fValue);
240 break;
242 case sdbc::DataType::CHAR:
243 case sdbc::DataType::VARCHAR:
244 case sdbc::DataType::LONGVARCHAR:
245 case sdbc::DataType::SQLNULL:
246 case sdbc::DataType::BINARY:
247 case sdbc::DataType::VARBINARY:
248 case sdbc::DataType::LONGVARBINARY:
249 default:
250 // nCol is 0-based, and the left-most column always has nCol == 0.
251 rData.SetStringInterned(
252 mrCache.InternString(nCol, mxRow->getString(nCol+1)));
255 catch (uno::Exception&)
257 rData.SetEmpty();
263 static sheet::DataPilotFieldOrientation lcl_GetDataGetOrientation( const uno::Reference<sheet::XDimensionsSupplier>& xSource )
265 sheet::DataPilotFieldOrientation nRet = sheet::DataPilotFieldOrientation_HIDDEN;
266 if ( xSource.is() )
268 uno::Reference<container::XNameAccess> xDimNameAccess = xSource->getDimensions();
269 const uno::Sequence<OUString> aDimNames = xDimNameAccess->getElementNames();
270 for (const OUString& rDimName : aDimNames)
272 uno::Reference<beans::XPropertySet> xDimProp(xDimNameAccess->getByName(rDimName),
273 uno::UNO_QUERY);
274 if ( xDimProp.is() )
276 const bool bFound = ScUnoHelpFunctions::GetBoolProperty( xDimProp,
277 SC_UNO_DP_ISDATALAYOUT );
278 //TODO: error checking -- is "IsDataLayoutDimension" property required??
279 if (bFound)
281 nRet = ScUnoHelpFunctions::GetEnumProperty(
282 xDimProp, SC_UNO_DP_ORIENTATION,
283 sheet::DataPilotFieldOrientation_HIDDEN );
284 break;
289 return nRet;
292 ScDPServiceDesc::ScDPServiceDesc(
293 OUString aServ, OUString aSrc, OUString aNam,
294 OUString aUser, OUString aPass ) :
295 aServiceName(std::move( aServ )),
296 aParSource(std::move( aSrc )),
297 aParName(std::move( aNam )),
298 aParUser(std::move( aUser )),
299 aParPass(std::move( aPass )) {}
301 bool ScDPServiceDesc::operator== ( const ScDPServiceDesc& rOther ) const
303 return aServiceName == rOther.aServiceName &&
304 aParSource == rOther.aParSource &&
305 aParName == rOther.aParName &&
306 aParUser == rOther.aParUser &&
307 aParPass == rOther.aParPass;
310 ScDPObject::ScDPObject(ScDocument* pDocument)
311 : mpDocument(pDocument)
312 , mnHeaderRows(0)
313 , mbHeaderLayout(false)
314 , mbAllowMove(false)
315 , mbSettingsChanged(false)
316 , mbEnableGetPivotData(true)
317 , mbHideHeader(false)
321 ScDPObject::ScDPObject(const ScDPObject& rOther)
322 : mpDocument(rOther.mpDocument)
323 , maTableName(rOther.maTableName)
324 , maTableTag(rOther.maTableTag)
325 , maOutputRange(rOther.maOutputRange)
326 , maInteropGrabBag(rOther.maInteropGrabBag)
327 , mnHeaderRows(rOther.mnHeaderRows)
328 , mbHeaderLayout(rOther.mbHeaderLayout)
329 , mbAllowMove(false)
330 , mbSettingsChanged(false)
331 , mbEnableGetPivotData(rOther.mbEnableGetPivotData)
332 , mbHideHeader(rOther.mbHideHeader)
334 if (rOther.mpSaveData)
335 mpSaveData.reset(new ScDPSaveData(*rOther.mpSaveData));
336 if (rOther.mpSheetDescription)
337 mpSheetDescription.reset(new ScSheetSourceDesc(*rOther.mpSheetDescription));
338 if (rOther.mpImportDescription)
339 mpImportDescription.reset(new ScImportSourceDesc(*rOther.mpImportDescription));
340 if (rOther.mpServiceDescription)
341 mpServiceDescription.reset(new ScDPServiceDesc(*rOther.mpServiceDescription));
342 // mxSource (and mpOutput) is not copied
345 ScDPObject::~ScDPObject()
347 Clear();
350 ScDPObject& ScDPObject::operator= (const ScDPObject& rOther)
352 if (this != &rOther)
354 Clear();
356 mpDocument = rOther.mpDocument;
357 maTableName = rOther.maTableName;
358 maTableTag = rOther.maTableTag;
359 maOutputRange = rOther.maOutputRange;
360 maInteropGrabBag = rOther.maInteropGrabBag;
361 mnHeaderRows = rOther.mnHeaderRows;
362 mbHeaderLayout =rOther.mbHeaderLayout;
363 mbAllowMove = false;
364 mbSettingsChanged = false;
365 mbEnableGetPivotData = rOther.mbEnableGetPivotData;
366 mbHideHeader = rOther.mbHideHeader;
368 if (rOther.mpSaveData)
369 mpSaveData.reset(new ScDPSaveData(*rOther.mpSaveData));
370 if (rOther.mpSheetDescription)
371 mpSheetDescription.reset(new ScSheetSourceDesc(*rOther.mpSheetDescription));
372 if (rOther.mpImportDescription)
373 mpImportDescription.reset(new ScImportSourceDesc(*rOther.mpImportDescription));
374 if (rOther.mpServiceDescription)
375 mpServiceDescription.reset(new ScDPServiceDesc(*rOther.mpServiceDescription));
377 return *this;
380 void ScDPObject::EnableGetPivotData(bool b)
382 mbEnableGetPivotData = b;
385 void ScDPObject::SetAllowMove(bool bSet)
387 mbAllowMove = bSet;
390 void ScDPObject::SetSaveData(const ScDPSaveData& rData)
392 if (mpSaveData.get() != &rData) // API implementation modifies the original SaveData object
394 mpSaveData.reset(new ScDPSaveData(rData));
397 InvalidateData(); // re-init source from SaveData
400 void ScDPObject::SetHeaderLayout (bool bUseGrid)
402 mbHeaderLayout = bUseGrid;
405 void ScDPObject::SetHideHeader(bool bHideHeader) { mbHideHeader = bHideHeader; }
407 void ScDPObject::SetOutRange(const ScRange& rRange)
409 maOutputRange = rRange;
411 if (mpOutput)
412 mpOutput->SetPosition( rRange.aStart );
415 const ScRange& ScDPObject::GetOutRange() const
417 return maOutputRange;
420 void ScDPObject::SetSheetDesc(const ScSheetSourceDesc& rDesc)
422 if (mpSheetDescription && rDesc == *mpSheetDescription)
423 return; // nothing to do
425 mpImportDescription.reset();
426 mpServiceDescription.reset();
428 mpSheetDescription.reset( new ScSheetSourceDesc(rDesc) );
430 // make valid QueryParam
432 const ScRange& rSrcRange = mpSheetDescription->GetSourceRange();
433 ScQueryParam aParam = mpSheetDescription->GetQueryParam();
434 aParam.nCol1 = rSrcRange.aStart.Col();
435 aParam.nRow1 = rSrcRange.aStart.Row();
436 aParam.nCol2 = rSrcRange.aEnd.Col();
437 aParam.nRow2 = rSrcRange.aEnd.Row();
438 aParam.bHasHeader = true;
439 mpSheetDescription->SetQueryParam(aParam);
441 ClearTableData(); // new source must be created
444 void ScDPObject::SetImportDesc(const ScImportSourceDesc& rDesc)
446 if (mpImportDescription && rDesc == *mpImportDescription)
447 return; // nothing to do
449 mpSheetDescription.reset();
450 mpServiceDescription.reset();
452 mpImportDescription.reset(new ScImportSourceDesc(rDesc));
454 ClearTableData(); // new source must be created
457 void ScDPObject::SetServiceData(const ScDPServiceDesc& rDesc)
459 if (mpServiceDescription && rDesc == *mpServiceDescription)
460 return; // nothing to do
462 mpSheetDescription.reset();
463 mpImportDescription.reset();
465 mpServiceDescription.reset(new ScDPServiceDesc(rDesc));
467 ClearTableData(); // new source must be created
470 void ScDPObject::WriteSourceDataTo( ScDPObject& rDest ) const
472 if (mpSheetDescription)
473 rDest.SetSheetDesc(*mpSheetDescription);
474 else if (mpImportDescription)
475 rDest.SetImportDesc(*mpImportDescription);
476 else if (mpServiceDescription)
477 rDest.SetServiceData(*mpServiceDescription);
479 // name/tag are not source data, but needed along with source data
481 rDest.maTableName = maTableName;
482 rDest.maTableTag = maTableTag;
485 void ScDPObject::WriteTempDataTo( ScDPObject& rDest ) const
487 rDest.mnHeaderRows = mnHeaderRows;
490 bool ScDPObject::IsSheetData() const
492 return mpSheetDescription != nullptr;
495 void ScDPObject::SetName(const OUString& rNew)
497 maTableName = rNew;
500 void ScDPObject::SetTag(const OUString& rNew)
502 maTableTag = rNew;
505 bool ScDPObject::IsDataDescriptionCell(const ScAddress& rPos)
507 if (!mpSaveData)
508 return false;
510 tools::Long nDataDimCount = mpSaveData->GetDataDimensionCount();
511 if (nDataDimCount != 1)
512 // There has to be exactly one data dimension for the description to
513 // appear at top-left corner.
514 return false;
516 CreateOutput();
517 ScRange aTabRange = mpOutput->GetOutputRange(sheet::DataPilotOutputRangeType::TABLE);
518 return (rPos == aTabRange.aStart);
521 uno::Reference<sheet::XDimensionsSupplier> const & ScDPObject::GetSource()
523 CreateObjects();
524 return mxSource;
527 void ScDPObject::CreateOutput()
529 CreateObjects();
530 if (mpOutput)
531 return;
533 bool bFilterButton = IsSheetData() && mpSaveData && mpSaveData->GetFilterButton();
534 bool bExpandCollapse = mpSaveData ? mpSaveData->GetExpandCollapse() : false;
536 mpOutput.reset(new ScDPOutput(mpDocument, mxSource, maOutputRange.aStart, bFilterButton, bExpandCollapse, *this, mbHideHeader));
537 mpOutput->SetHeaderLayout(mbHeaderLayout);
538 if (mpSaveData->hasFormats())
539 mpOutput->setFormats(mpSaveData->getFormats());
541 sal_Int32 nOldRows = mnHeaderRows;
542 mnHeaderRows = mpOutput->GetHeaderRows();
544 if (!(mbAllowMove && mnHeaderRows != nOldRows))
545 return;
547 sal_Int32 nDiff = nOldRows - mnHeaderRows;
548 if ( nOldRows == 0 )
549 --nDiff;
550 if (mnHeaderRows == 0)
551 ++nDiff;
553 sal_Int32 nNewRow = maOutputRange.aStart.Row() + nDiff;
554 if ( nNewRow < 0 )
555 nNewRow = 0;
557 ScAddress aStart(maOutputRange.aStart);
558 aStart.SetRow(nNewRow);
559 mpOutput->SetPosition( aStart );
561 //TODO: modify maOutputRange?
563 mbAllowMove = false; // use only once
566 namespace {
568 class DisableGetPivotData
570 ScDPObject& mrDPObj;
571 bool mbOldState;
572 public:
573 DisableGetPivotData(ScDPObject& rObj, bool bOld) : mrDPObj(rObj), mbOldState(bOld)
575 mrDPObj.EnableGetPivotData(false);
578 ~DisableGetPivotData()
580 mrDPObj.EnableGetPivotData(mbOldState);
584 class FindIntersectingTable
586 ScRange maRange;
587 public:
588 explicit FindIntersectingTable(const ScRange& rRange) : maRange(rRange) {}
590 bool operator() (const std::unique_ptr<ScDPObject>& rObj) const
592 return maRange.Intersects(rObj->GetOutRange());
596 class FindIntersectingTableByColumns
598 SCCOL mnCol1;
599 SCCOL mnCol2;
600 SCROW mnRow;
601 SCTAB mnTab;
602 public:
603 FindIntersectingTableByColumns(SCCOL nCol1, SCCOL nCol2, SCROW nRow, SCTAB nTab) :
604 mnCol1(nCol1), mnCol2(nCol2), mnRow(nRow), mnTab(nTab) {}
606 bool operator() (const std::unique_ptr<ScDPObject>& rObj) const
608 const ScRange& rRange = rObj->GetOutRange();
609 if (mnTab != rRange.aStart.Tab())
610 // Not on this sheet.
611 return false;
613 if (rRange.aEnd.Row() < mnRow)
614 // This table is above the row. It's safe.
615 return false;
617 if (mnCol1 <= rRange.aStart.Col() && rRange.aEnd.Col() <= mnCol2)
618 // This table is fully enclosed in this column range.
619 return false;
621 if (rRange.aEnd.Col() < mnCol1 || mnCol2 < rRange.aStart.Col())
622 // This table is entirely outside this column range.
623 return false;
625 // This table must be intersected by this column range.
626 return true;
630 class FindIntersectingTableByRows
632 SCCOL mnCol;
633 SCROW mnRow1;
634 SCROW mnRow2;
635 SCTAB mnTab;
636 public:
637 FindIntersectingTableByRows(SCCOL nCol, SCROW nRow1, SCROW nRow2, SCTAB nTab) :
638 mnCol(nCol), mnRow1(nRow1), mnRow2(nRow2), mnTab(nTab) {}
640 bool operator() (const std::unique_ptr<ScDPObject>& rObj) const
642 const ScRange& rRange = rObj->GetOutRange();
643 if (mnTab != rRange.aStart.Tab())
644 // Not on this sheet.
645 return false;
647 if (rRange.aEnd.Col() < mnCol)
648 // This table is to the left of the column. It's safe.
649 return false;
651 if (mnRow1 <= rRange.aStart.Row() && rRange.aEnd.Row() <= mnRow2)
652 // This table is fully enclosed in this row range.
653 return false;
655 if (rRange.aEnd.Row() < mnRow1 || mnRow2 < rRange.aStart.Row())
656 // This table is entirely outside this row range.
657 return false;
659 // This table must be intersected by this row range.
660 return true;
664 class AccumulateOutputRanges
666 ScRangeList maRanges;
667 SCTAB mnTab;
668 public:
669 explicit AccumulateOutputRanges(SCTAB nTab) : mnTab(nTab) {}
670 AccumulateOutputRanges(const AccumulateOutputRanges& r) : maRanges(r.maRanges), mnTab(r.mnTab) {}
672 void operator() (const std::unique_ptr<ScDPObject>& rObj)
674 const ScRange& rRange = rObj->GetOutRange();
675 if (mnTab != rRange.aStart.Tab())
676 // Not on this sheet.
677 return;
679 maRanges.Join(rRange);
682 const ScRangeList& getRanges() const { return maRanges; }
687 ScDPTableData* ScDPObject::GetTableData()
689 if (!mpTableData)
691 shared_ptr<ScDPTableData> pData;
692 const ScDPDimensionSaveData* pDimData = mpSaveData ? mpSaveData->GetExistingDimensionData() : nullptr;
694 if (mpImportDescription)
696 // database data
697 const ScDPCache* pCache = mpImportDescription->CreateCache(pDimData);
698 if (pCache)
700 pCache->AddReference(this);
701 pData = std::make_shared<ScDatabaseDPData>(mpDocument, *pCache);
704 else
706 // cell data
707 if (!mpSheetDescription)
709 OSL_FAIL("no source descriptor");
710 mpSheetDescription.reset(new ScSheetSourceDesc(mpDocument)); // dummy defaults
714 // Temporarily disable GETPIVOTDATA to avoid having
715 // GETPIVOTDATA called onto itself from within the source
716 // range.
717 DisableGetPivotData aSwitch(*this, mbEnableGetPivotData);
718 const ScDPCache* pCache = mpSheetDescription->CreateCache(pDimData);
719 if (pCache)
721 pCache->AddReference(this);
722 pData = std::make_shared<ScSheetDPData>(mpDocument, *mpSheetDescription, *pCache);
727 // grouping (for cell or database data)
728 if (pData && pDimData)
730 auto pGroupData = std::make_shared<ScDPGroupTableData>(pData, mpDocument);
731 pDimData->WriteToData(*pGroupData);
732 pData = pGroupData;
735 mpTableData = std::move(pData); // after SetCacheId
738 return mpTableData.get();
741 void ScDPObject::CreateObjects()
743 if (!mxSource.is())
745 mpOutput.reset(); // not valid when mxSource is changed
747 if (mpServiceDescription)
749 mxSource = CreateSource(*mpServiceDescription);
752 if (!mxSource.is()) // database or sheet data, or error in CreateSource
754 OSL_ENSURE(!mpServiceDescription, "DPSource could not be created");
755 ScDPTableData* pData = GetTableData();
756 if (pData)
758 if (mpSaveData)
759 // Make sure to transfer these flags to the table data
760 // since they may have changed.
761 pData->SetEmptyFlags(mpSaveData->GetIgnoreEmptyRows(), mpSaveData->GetRepeatIfEmpty());
763 pData->ReloadCacheTable();
764 mxSource = new ScDPSource( pData );
768 if (mpSaveData)
769 mpSaveData->WriteToSource(mxSource);
771 else if (mbSettingsChanged)
773 mpOutput.reset(); // not valid when mxSource is changed
775 uno::Reference<util::XRefreshable> xRef(mxSource, uno::UNO_QUERY);
776 if (xRef.is())
780 xRef->refresh();
782 catch(uno::Exception&)
784 TOOLS_WARN_EXCEPTION( "sc", "exception in refresh");
788 if (mpSaveData)
789 mpSaveData->WriteToSource(mxSource);
791 mbSettingsChanged = false;
794 void ScDPObject::InvalidateData()
796 mbSettingsChanged = true;
799 void ScDPObject::Clear()
801 mpOutput.reset();
802 mpSaveData.reset();
803 mpSheetDescription.reset();
804 mpImportDescription.reset();
805 mpServiceDescription.reset();
806 ClearTableData();
807 maInteropGrabBag.clear();
810 void ScDPObject::ClearTableData()
812 ClearSource();
814 if (mpTableData)
815 mpTableData->GetCacheTable().getCache().RemoveReference(this);
816 mpTableData.reset();
819 void ScDPObject::ReloadGroupTableData()
821 ClearSource();
823 if (!mpTableData)
824 // Table data not built yet. No need to reload the group data.
825 return;
827 if (!mpSaveData)
828 // How could it not have the save data... but whatever.
829 return;
831 const ScDPDimensionSaveData* pDimData = mpSaveData->GetExistingDimensionData();
832 if (!pDimData || !pDimData->HasGroupDimensions())
834 // No group dimensions exist. Check if it currently has group
835 // dimensions, and if so, remove all of them.
836 ScDPGroupTableData* pData = dynamic_cast<ScDPGroupTableData*>(mpTableData.get());
837 if (pData)
839 // Replace the existing group table data with the source data.
840 mpTableData = pData->GetSourceTableData();
842 return;
845 ScDPGroupTableData* pData = dynamic_cast<ScDPGroupTableData*>(mpTableData.get());
846 if (pData)
848 // This is already a group table data. Salvage the source data and
849 // re-create a new group data.
850 const shared_ptr<ScDPTableData>& pSource = pData->GetSourceTableData();
851 auto pGroupData = std::make_shared<ScDPGroupTableData>(pSource, mpDocument);
852 pDimData->WriteToData(*pGroupData);
853 mpTableData = pGroupData;
855 else
857 // This is a source data. Create a group data based on it.
858 auto pGroupData = std::make_shared<ScDPGroupTableData>(mpTableData, mpDocument);
859 pDimData->WriteToData(*pGroupData);
860 mpTableData = pGroupData;
863 mbSettingsChanged = true;
866 void ScDPObject::ClearSource()
868 uno::Reference<XComponent> xObjectComp(mxSource, UNO_QUERY);
869 if (xObjectComp.is())
873 xObjectComp->dispose();
875 catch( const Exception& )
877 DBG_UNHANDLED_EXCEPTION("sc.core");
880 mxSource = nullptr;
883 ScRange ScDPObject::GetNewOutputRange( bool& rOverflow )
885 CreateOutput(); // create mxSource and mpOutput if not already done
887 rOverflow = mpOutput->HasError(); // range overflow or exception from source
888 if ( rOverflow )
889 return ScRange(maOutputRange.aStart);
890 else
892 // don't store the result in maOutputRange, because nothing has been output yet
893 return mpOutput->GetOutputRange();
897 void ScDPObject::Output( const ScAddress& rPos )
899 // clear old output area
900 mpDocument->DeleteAreaTab(maOutputRange.aStart.Col(), maOutputRange.aStart.Row(),
901 maOutputRange.aEnd.Col(), maOutputRange.aEnd.Row(),
902 maOutputRange.aStart.Tab(), InsertDeleteFlags::ALL );
903 mpDocument->RemoveFlagsTab( maOutputRange.aStart.Col(), maOutputRange.aStart.Row(),
904 maOutputRange.aEnd.Col(), maOutputRange.aEnd.Row(),
905 maOutputRange.aStart.Tab(), ScMF::Auto );
907 CreateOutput(); // create mxSource and mpOutput if not already done
909 mpOutput->SetPosition( rPos );
911 mpOutput->Output();
913 // maOutputRange is always the range that was last output to the document
914 maOutputRange = mpOutput->GetOutputRange();
915 const ScAddress& s = maOutputRange.aStart;
916 const ScAddress& e = maOutputRange.aEnd;
917 mpDocument->ApplyFlagsTab(s.Col(), s.Row(), e.Col(), e.Row(), s.Tab(), ScMF::DpTable);
920 ScRange ScDPObject::GetOutputRangeByType( sal_Int32 nType )
922 CreateOutput();
924 if (mpOutput->HasError())
925 return ScRange(maOutputRange.aStart);
927 return mpOutput->GetOutputRange(nType);
930 ScRange ScDPObject::GetOutputRangeByType( sal_Int32 nType ) const
932 if (!mpOutput || mpOutput->HasError())
933 return ScRange(ScAddress::INITIALIZE_INVALID);
935 return mpOutput->GetOutputRange(nType);
938 static bool lcl_HasButton( const ScDocument* pDoc, SCCOL nCol, SCROW nRow, SCTAB nTab )
940 return pDoc->GetAttr( nCol, nRow, nTab, ATTR_MERGE_FLAG )->HasPivotButton();
943 void ScDPObject::RefreshAfterLoad()
945 // apply drop-down attribute, initialize mnHeaderRows, without accessing the source
946 // (button attribute must be present)
948 // simple test: block of button cells at the top, followed by an empty cell
950 SCCOL nFirstCol = maOutputRange.aStart.Col();
951 SCROW nFirstRow = maOutputRange.aStart.Row();
952 SCTAB nTab = maOutputRange.aStart.Tab();
954 SCROW nInitial = 0;
955 SCROW nOutRows = maOutputRange.aEnd.Row() + 1 - maOutputRange.aStart.Row();
956 while (nInitial + 1 < nOutRows && lcl_HasButton(mpDocument, nFirstCol, nFirstRow + nInitial, nTab))
957 ++nInitial;
959 if ( nInitial + 1 < nOutRows &&
960 mpDocument->IsBlockEmpty( nFirstCol, nFirstRow + nInitial, nFirstCol, nFirstRow + nInitial, nTab ) &&
961 maOutputRange.aEnd.Col() > nFirstCol )
963 mnHeaderRows = nInitial;
965 else
966 mnHeaderRows = 0; // nothing found, no drop-down lists
969 void ScDPObject::BuildAllDimensionMembers()
971 if (!mpSaveData)
972 return;
974 // #i111857# don't always create empty mpTableData for external service.
975 if (mpServiceDescription)
976 return;
978 ScDPTableData* pTableData = GetTableData();
979 if(pTableData)
980 mpSaveData->BuildAllDimensionMembers(pTableData);
983 bool ScDPObject::SyncAllDimensionMembers()
985 if (!mpSaveData)
986 return false;
988 // #i111857# don't always create empty mpTableData for external service.
989 // Ideally, mxSource should be used instead of mpTableData.
990 if (mpServiceDescription)
991 return false;
993 ScDPTableData* pData = GetTableData();
994 if (!pData)
995 // No table data exists. This can happen when refreshing from an
996 // external source which doesn't exist.
997 return false;
999 // Refresh the cache wrapper since the cache may have changed.
1000 pData->SetEmptyFlags(mpSaveData->GetIgnoreEmptyRows(), mpSaveData->GetRepeatIfEmpty());
1001 pData->ReloadCacheTable();
1002 mpSaveData->SyncAllDimensionMembers(pData);
1003 return true;
1006 bool ScDPObject::GetMemberNames( sal_Int32 nDim, Sequence<OUString>& rNames )
1008 vector<ScDPLabelData::Member> aMembers;
1009 if (!GetMembers(nDim, GetUsedHierarchy(nDim), aMembers))
1010 return false;
1012 size_t n = aMembers.size();
1013 rNames.realloc(n);
1014 auto pNames = rNames.getArray();
1015 for (size_t i = 0; i < n; ++i)
1016 pNames[i] = aMembers[i].maName;
1018 return true;
1021 bool ScDPObject::GetMembers( sal_Int32 nDim, sal_Int32 nHier, vector<ScDPLabelData::Member>& rMembers )
1023 Reference< sheet::XMembersAccess > xMembersNA;
1024 if (!GetMembersNA( nDim, nHier, xMembersNA ))
1025 return false;
1027 Reference<container::XIndexAccess> xMembersIA( new ScNameToIndexAccess(xMembersNA) );
1028 sal_Int32 nCount = xMembersIA->getCount();
1029 vector<ScDPLabelData::Member> aMembers;
1030 aMembers.reserve(nCount);
1032 for (sal_Int32 i = 0; i < nCount; ++i)
1034 Reference<container::XNamed> xMember;
1037 xMember = Reference<container::XNamed>(xMembersIA->getByIndex(i), UNO_QUERY);
1039 catch (const container::NoSuchElementException&)
1041 TOOLS_WARN_EXCEPTION("sc", "ScNameToIndexAccess getByIndex failed");
1044 ScDPLabelData::Member aMem;
1046 if (xMember.is())
1047 aMem.maName = xMember->getName();
1049 Reference<beans::XPropertySet> xMemProp(xMember, UNO_QUERY);
1050 if (xMemProp.is())
1052 aMem.mbVisible = ScUnoHelpFunctions::GetBoolProperty(xMemProp, SC_UNO_DP_ISVISIBLE);
1053 aMem.mbShowDetails = ScUnoHelpFunctions::GetBoolProperty(xMemProp, SC_UNO_DP_SHOWDETAILS);
1055 aMem.maLayoutName = ScUnoHelpFunctions::GetStringProperty(
1056 xMemProp, SC_UNO_DP_LAYOUTNAME, OUString());
1059 aMembers.push_back(aMem);
1061 rMembers.swap(aMembers);
1062 return true;
1065 void ScDPObject::UpdateReference( UpdateRefMode eUpdateRefMode,
1066 const ScRange& rRange, SCCOL nDx, SCROW nDy, SCTAB nDz )
1068 // Output area
1070 SCCOL nCol1 = maOutputRange.aStart.Col();
1071 SCROW nRow1 = maOutputRange.aStart.Row();
1072 SCTAB nTab1 = maOutputRange.aStart.Tab();
1073 SCCOL nCol2 = maOutputRange.aEnd.Col();
1074 SCROW nRow2 = maOutputRange.aEnd.Row();
1075 SCTAB nTab2 = maOutputRange.aEnd.Tab();
1077 ScRefUpdateRes eRes =
1078 ScRefUpdate::Update(mpDocument, eUpdateRefMode,
1079 rRange.aStart.Col(), rRange.aStart.Row(), rRange.aStart.Tab(),
1080 rRange.aEnd.Col(), rRange.aEnd.Row(), rRange.aEnd.Tab(), nDx, nDy, nDz,
1081 nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
1082 if ( eRes != UR_NOTHING )
1083 SetOutRange( ScRange( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ) );
1085 // sheet source data
1087 if (!mpSheetDescription)
1088 return;
1090 const OUString& rRangeName = mpSheetDescription->GetRangeName();
1091 if (!rRangeName.isEmpty())
1092 // Source range is a named range. No need to update.
1093 return;
1095 const ScRange& rSrcRange = mpSheetDescription->GetSourceRange();
1096 nCol1 = rSrcRange.aStart.Col();
1097 nRow1 = rSrcRange.aStart.Row();
1098 nTab1 = rSrcRange.aStart.Tab();
1099 nCol2 = rSrcRange.aEnd.Col();
1100 nRow2 = rSrcRange.aEnd.Row();
1101 nTab2 = rSrcRange.aEnd.Tab();
1103 eRes = ScRefUpdate::Update(mpDocument, eUpdateRefMode,
1104 rRange.aStart.Col(), rRange.aStart.Row(), rRange.aStart.Tab(),
1105 rRange.aEnd.Col(), rRange.aEnd.Row(), rRange.aEnd.Tab(), nDx, nDy, nDz,
1106 nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
1107 if ( eRes == UR_NOTHING )
1108 return;
1110 SCCOL nDiffX = nCol1 - mpSheetDescription->GetSourceRange().aStart.Col();
1111 SCROW nDiffY = nRow1 - mpSheetDescription->GetSourceRange().aStart.Row();
1113 ScQueryParam aParam = mpSheetDescription->GetQueryParam();
1114 aParam.nCol1 = sal::static_int_cast<SCCOL>( aParam.nCol1 + nDiffX );
1115 aParam.nCol2 = sal::static_int_cast<SCCOL>( aParam.nCol2 + nDiffX );
1116 aParam.nRow1 += nDiffY; //TODO: used?
1117 aParam.nRow2 += nDiffY; //TODO: used?
1118 SCSIZE nEC = aParam.GetEntryCount();
1119 for (SCSIZE i=0; i<nEC; i++)
1120 if (aParam.GetEntry(i).bDoQuery)
1121 aParam.GetEntry(i).nField += nDiffX;
1123 mpSheetDescription->SetQueryParam(aParam);
1124 mpSheetDescription->SetSourceRange(ScRange(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2));
1127 bool ScDPObject::RefsEqual( const ScDPObject& rOther) const
1129 if (maOutputRange != rOther.maOutputRange)
1130 return false;
1132 if (mpSheetDescription && rOther.mpSheetDescription)
1134 if (mpSheetDescription->GetSourceRange() != rOther.mpSheetDescription->GetSourceRange())
1135 return false;
1137 else if (mpSheetDescription || rOther.mpSheetDescription)
1139 OSL_FAIL("RefsEqual: SheetDesc set at only one object");
1140 return false;
1143 return true;
1146 void ScDPObject::WriteRefsTo(ScDPObject& rObject) const
1148 rObject.SetOutRange(maOutputRange);
1149 if (mpSheetDescription)
1150 rObject.SetSheetDesc(*mpSheetDescription);
1153 void ScDPObject::GetPositionData(const ScAddress& rPos, DataPilotTablePositionData& rPosData)
1155 CreateOutput();
1156 mpOutput->GetPositionData(rPos, rPosData);
1159 bool ScDPObject::GetDataFieldPositionData(
1160 const ScAddress& rPos, Sequence<sheet::DataPilotFieldFilter>& rFilters)
1162 CreateOutput();
1164 vector<sheet::DataPilotFieldFilter> aFilters;
1165 if (!mpOutput->GetDataResultPositionData(aFilters, rPos))
1166 return false;
1168 sal_Int32 n = static_cast<sal_Int32>(aFilters.size());
1169 rFilters.realloc(n);
1170 auto pFilters = rFilters.getArray();
1171 for (sal_Int32 i = 0; i < n; ++i)
1172 pFilters[i] = aFilters[i];
1174 return true;
1177 void ScDPObject::GetDrillDownData(const ScAddress& rPos, Sequence< Sequence<Any> >& rTableData)
1179 CreateOutput();
1181 uno::Reference<sheet::XDrillDownDataSupplier> xDrillDownData(mxSource, UNO_QUERY);
1182 if (!xDrillDownData.is())
1183 return;
1185 Sequence<sheet::DataPilotFieldFilter> filters;
1186 if (!GetDataFieldPositionData(rPos, filters))
1187 return;
1189 rTableData = xDrillDownData->getDrillDownData(filters);
1192 bool ScDPObject::IsDimNameInUse(std::u16string_view rName) const
1194 if (!mxSource.is())
1195 return false;
1197 Reference<container::XNameAccess> xDims = mxSource->getDimensions();
1198 const Sequence<OUString> aDimNames = xDims->getElementNames();
1199 for (const OUString& rDimName : aDimNames)
1201 if (rDimName.equalsIgnoreAsciiCase(rName))
1202 return true;
1204 Reference<beans::XPropertySet> xPropSet(xDims->getByName(rDimName), UNO_QUERY);
1205 if (!xPropSet.is())
1206 continue;
1208 OUString aLayoutName = ScUnoHelpFunctions::GetStringProperty(
1209 xPropSet, SC_UNO_DP_LAYOUTNAME, OUString());
1210 if (aLayoutName.equalsIgnoreAsciiCase(rName))
1211 return true;
1213 return false;
1216 OUString ScDPObject::GetDimName( tools::Long nDim, bool& rIsDataLayout, sal_Int32* pFlags )
1218 rIsDataLayout = false;
1219 OUString aRet;
1221 if (mxSource.is())
1223 uno::Reference<container::XNameAccess> xDimsName = mxSource->getDimensions();
1224 uno::Reference<container::XIndexAccess> xDims = new ScNameToIndexAccess( xDimsName );
1225 tools::Long nDimCount = xDims->getCount();
1226 if ( nDim < nDimCount )
1228 uno::Reference<uno::XInterface> xIntDim(xDims->getByIndex(nDim), uno::UNO_QUERY);
1229 uno::Reference<container::XNamed> xDimName( xIntDim, uno::UNO_QUERY );
1230 uno::Reference<beans::XPropertySet> xDimProp( xIntDim, uno::UNO_QUERY );
1231 if ( xDimName.is() && xDimProp.is() )
1233 bool bData = ScUnoHelpFunctions::GetBoolProperty( xDimProp,
1234 SC_UNO_DP_ISDATALAYOUT );
1235 //TODO: error checking -- is "IsDataLayoutDimension" property required??
1237 OUString aName;
1240 aName = xDimName->getName();
1242 catch(uno::Exception&)
1245 if ( bData )
1246 rIsDataLayout = true;
1247 else
1248 aRet = aName;
1250 if (pFlags)
1251 *pFlags = ScUnoHelpFunctions::GetLongProperty( xDimProp,
1252 SC_UNO_DP_FLAGS );
1256 else if (ScDPTableData* pData = GetTableData())
1258 aRet = pData->getDimensionName(nDim);
1259 rIsDataLayout = pData->getIsDataLayoutDimension(nDim);
1262 return aRet;
1265 bool ScDPObject::IsDuplicated( tools::Long nDim )
1267 bool bDuplicated = false;
1268 if (mxSource.is())
1270 uno::Reference<container::XNameAccess> xDimsName = mxSource->getDimensions();
1271 uno::Reference<container::XIndexAccess> xDims = new ScNameToIndexAccess( xDimsName );
1272 tools::Long nDimCount = xDims->getCount();
1273 if ( nDim < nDimCount )
1275 uno::Reference<beans::XPropertySet> xDimProp(xDims->getByIndex(nDim), uno::UNO_QUERY);
1276 if ( xDimProp.is() )
1280 uno::Any aOrigAny = xDimProp->getPropertyValue( SC_UNO_DP_ORIGINAL );
1281 uno::Reference<uno::XInterface> xIntOrig;
1282 if ( (aOrigAny >>= xIntOrig) && xIntOrig.is() )
1283 bDuplicated = true;
1285 catch(uno::Exception&)
1291 return bDuplicated;
1294 tools::Long ScDPObject::GetDimCount()
1296 tools::Long nRet = 0;
1297 if (mxSource.is())
1301 uno::Reference<container::XNameAccess> xDimsName = mxSource->getDimensions();
1302 if ( xDimsName.is() )
1303 nRet = xDimsName->getElementNames().getLength();
1305 catch(uno::Exception&)
1309 return nRet;
1312 void ScDPObject::GetHeaderPositionData(const ScAddress& rPos, DataPilotTableHeaderData& rData)
1314 CreateOutput(); // create mxSource and mpOutput if not already done
1316 // Reset member values to invalid state.
1317 rData.Dimension = rData.Hierarchy = rData.Level = -1;
1318 rData.Flags = 0;
1320 DataPilotTablePositionData aPosData;
1321 mpOutput->GetPositionData(rPos, aPosData);
1322 const sal_Int32 nPosType = aPosData.PositionType;
1323 if (nPosType == css::sheet::DataPilotTablePositionType::COLUMN_HEADER || nPosType == css::sheet::DataPilotTablePositionType::ROW_HEADER)
1324 aPosData.PositionData >>= rData;
1327 namespace {
1329 class FindByName
1331 OUString maName; // must be all uppercase.
1332 public:
1333 explicit FindByName(OUString aName) : maName(std::move(aName)) {}
1334 bool operator() (const ScDPSaveDimension* pDim) const
1336 // Layout name takes precedence.
1337 const std::optional<OUString> & pLayoutName = pDim->GetLayoutName();
1338 if (pLayoutName && ScGlobal::getCharClass().uppercase(*pLayoutName) == maName)
1339 return true;
1341 ScGeneralFunction eGenFunc = pDim->GetFunction();
1342 ScSubTotalFunc eFunc = ScDPUtil::toSubTotalFunc(eGenFunc);
1343 OUString aSrcName = ScDPUtil::getSourceDimensionName(pDim->GetName());
1344 OUString aFuncName = ScDPUtil::getDisplayedMeasureName(aSrcName, eFunc);
1345 if (maName == ScGlobal::getCharClass().uppercase(aFuncName))
1346 return true;
1348 return maName == ScGlobal::getCharClass().uppercase(aSrcName);
1352 class LessByDimOrder
1354 const ScDPSaveData::DimOrderType& mrDimOrder;
1356 public:
1357 explicit LessByDimOrder(const ScDPSaveData::DimOrderType& rDimOrder) : mrDimOrder(rDimOrder) {}
1359 bool operator() (const sheet::DataPilotFieldFilter& r1, const sheet::DataPilotFieldFilter& r2) const
1361 size_t nRank1 = mrDimOrder.size();
1362 size_t nRank2 = mrDimOrder.size();
1363 ScDPSaveData::DimOrderType::const_iterator it1 = mrDimOrder.find(
1364 ScGlobal::getCharClass().uppercase(r1.FieldName));
1365 if (it1 != mrDimOrder.end())
1366 nRank1 = it1->second;
1368 ScDPSaveData::DimOrderType::const_iterator it2 = mrDimOrder.find(
1369 ScGlobal::getCharClass().uppercase(r2.FieldName));
1370 if (it2 != mrDimOrder.end())
1371 nRank2 = it2->second;
1373 return nRank1 < nRank2;
1379 double ScDPObject::GetPivotData(const OUString& rDataFieldName, std::vector<sheet::DataPilotFieldFilter>& rFilters)
1381 if (!mbEnableGetPivotData)
1382 return std::numeric_limits<double>::quiet_NaN();
1384 CreateObjects();
1386 std::vector<const ScDPSaveDimension*> aDataDims;
1387 mpSaveData->GetAllDimensionsByOrientation(sheet::DataPilotFieldOrientation_DATA, aDataDims);
1388 if (aDataDims.empty())
1389 return std::numeric_limits<double>::quiet_NaN();
1391 std::vector<const ScDPSaveDimension*>::iterator it = std::find_if(
1392 aDataDims.begin(), aDataDims.end(),
1393 FindByName(ScGlobal::getCharClass().uppercase(rDataFieldName)));
1395 if (it == aDataDims.end())
1396 return std::numeric_limits<double>::quiet_NaN();
1398 size_t nDataIndex = std::distance(aDataDims.begin(), it);
1400 uno::Reference<sheet::XDataPilotResults> xDPResults(mxSource, uno::UNO_QUERY);
1401 if (!xDPResults.is())
1402 return std::numeric_limits<double>::quiet_NaN();
1404 // Dimensions must be sorted in order of appearance, and row dimensions
1405 // must come before column dimensions.
1406 std::sort(rFilters.begin(), rFilters.end(), LessByDimOrder(mpSaveData->GetDimensionSortOrder()));
1408 size_t n = rFilters.size();
1409 uno::Sequence<sheet::DataPilotFieldFilter> aFilters(n);
1410 auto aFiltersRange = asNonConstRange(aFilters);
1411 for (size_t i = 0; i < n; ++i)
1412 aFiltersRange[i] = rFilters[i];
1414 uno::Sequence<double> aRes = xDPResults->getFilteredResults(aFilters);
1415 if (nDataIndex >= o3tl::make_unsigned(aRes.getLength()))
1416 return std::numeric_limits<double>::quiet_NaN();
1418 return aRes[nDataIndex];
1421 bool ScDPObject::IsFilterButton( const ScAddress& rPos )
1423 CreateOutput(); // create mxSource and mpOutput if not already done
1425 return mpOutput->IsFilterButton( rPos );
1428 tools::Long ScDPObject::GetHeaderDim( const ScAddress& rPos, sheet::DataPilotFieldOrientation& rOrient )
1430 CreateOutput(); // create mxSource and mpOutput if not already done
1432 return mpOutput->GetHeaderDim( rPos, rOrient );
1435 bool ScDPObject::GetHeaderDrag( const ScAddress& rPos, bool bMouseLeft, bool bMouseTop, tools::Long nDragDim,
1436 tools::Rectangle& rPosRect, sheet::DataPilotFieldOrientation& rOrient, tools::Long& rDimPos )
1438 CreateOutput();// create mxSource and mpOutput if not already done
1440 return mpOutput->GetHeaderDrag( rPos, bMouseLeft, bMouseTop, nDragDim, rPosRect, rOrient, rDimPos );
1443 void ScDPObject::GetMemberResultNames(ScDPUniqueStringSet& rNames, tools::Long nDimension)
1445 CreateOutput();// create mxSource and mpOutput if not already done
1447 mpOutput->GetMemberResultNames(rNames, nDimension); // used only with table data -> level not needed
1450 OUString ScDPObject::GetFormattedString(ScDPTableData* pTableData, tools::Long nDimension, const double fValue)
1452 ScDPItemData aItemData;
1453 aItemData.SetValue(fValue);
1454 return pTableData->GetFormattedString(nDimension, aItemData, false);
1457 OUString ScDPObject::GetFormattedString(std::u16string_view rDimName, const double fValue)
1459 ScDPTableData* pTableData = GetTableData();
1460 if(!pTableData)
1461 return OUString();
1463 tools::Long nDim;
1464 for (nDim = 0; nDim < pTableData->GetColumnCount(); ++nDim)
1466 if(rDimName == pTableData->getDimensionName(nDim))
1467 break;
1470 return GetFormattedString(pTableData, nDim, fValue);
1474 namespace {
1476 bool dequote( std::u16string_view rSource, sal_Int32 nStartPos, sal_Int32& rEndPos, OUString& rResult )
1478 // nStartPos has to point to opening quote
1480 const sal_Unicode cQuote = '\'';
1482 if (rSource[nStartPos] == cQuote)
1484 OUStringBuffer aBuffer;
1485 sal_Int32 nPos = nStartPos + 1;
1486 const sal_Int32 nLen = rSource.size();
1488 while ( nPos < nLen )
1490 const sal_Unicode cNext = rSource[nPos];
1491 if ( cNext == cQuote )
1493 if (nPos+1 < nLen && rSource[nPos+1] == cQuote)
1495 // double quote is used for an embedded quote
1496 aBuffer.append( cNext ); // append one quote
1497 ++nPos; // skip the next one
1499 else
1501 // end of quoted string
1502 rResult = aBuffer.makeStringAndClear();
1503 rEndPos = nPos + 1; // behind closing quote
1504 return true;
1507 else
1508 aBuffer.append( cNext );
1510 ++nPos;
1512 // no closing quote before the end of the string -> error (bRet still false)
1515 return false;
1518 struct ScGetPivotDataFunctionEntry
1520 const char* pName;
1521 sal_Int16 eFunc;
1524 bool parseFunction( std::u16string_view rList, sal_Int32 nStartPos, sal_Int32& rEndPos, sal_Int16& rFunc )
1526 static const ScGetPivotDataFunctionEntry aFunctions[] =
1528 // our names
1529 { "Sum", sheet::GeneralFunction2::SUM },
1530 { "Count", sheet::GeneralFunction2::COUNT },
1531 { "Average", sheet::GeneralFunction2::AVERAGE },
1532 { "Max", sheet::GeneralFunction2::MAX },
1533 { "Min", sheet::GeneralFunction2::MIN },
1534 { "Product", sheet::GeneralFunction2::PRODUCT },
1535 { "CountNums", sheet::GeneralFunction2::COUNTNUMS },
1536 { "StDev", sheet::GeneralFunction2::STDEV },
1537 { "StDevp", sheet::GeneralFunction2::STDEVP },
1538 { "Var", sheet::GeneralFunction2::VAR },
1539 { "VarP", sheet::GeneralFunction2::VARP },
1540 // compatibility names
1541 { "Count Nums", sheet::GeneralFunction2::COUNTNUMS },
1542 { "StdDev", sheet::GeneralFunction2::STDEV },
1543 { "StdDevp", sheet::GeneralFunction2::STDEVP }
1546 const sal_Int32 nListLen = rList.size();
1547 while (nStartPos < nListLen && rList[nStartPos] == ' ')
1548 ++nStartPos;
1550 bool bParsed = false;
1551 bool bFound = false;
1552 OUString aFuncStr;
1553 sal_Int32 nFuncEnd = 0;
1554 if (nStartPos < nListLen && rList[nStartPos] == '\'')
1555 bParsed = dequote( rList, nStartPos, nFuncEnd, aFuncStr );
1556 else
1558 nFuncEnd = rList.find(']', nStartPos);
1559 if (nFuncEnd >= 0)
1561 aFuncStr = rList.substr(nStartPos, nFuncEnd - nStartPos);
1562 bParsed = true;
1566 if ( bParsed )
1568 aFuncStr = comphelper::string::strip(aFuncStr, ' ');
1570 const sal_Int32 nFuncCount = SAL_N_ELEMENTS(aFunctions);
1571 for ( sal_Int32 nFunc=0; nFunc<nFuncCount && !bFound; nFunc++ )
1573 if (aFuncStr.equalsIgnoreAsciiCaseAscii(aFunctions[nFunc].pName))
1575 rFunc = aFunctions[nFunc].eFunc;
1576 bFound = true;
1578 while (nFuncEnd < nListLen && rList[nFuncEnd] == ' ')
1579 ++nFuncEnd;
1580 rEndPos = nFuncEnd;
1585 return bFound;
1588 bool extractAtStart( std::u16string_view rList, sal_Int32& rMatched, bool bAllowBracket, sal_Int16* pFunc,
1589 OUString& rDequoted )
1591 size_t nMatchList = 0;
1592 sal_Unicode cFirst = rList[0];
1593 bool bParsed = false;
1594 if ( cFirst == '\'' || cFirst == '[' )
1596 // quoted string or string in brackets must match completely
1598 OUString aDequoted;
1599 sal_Int32 nQuoteEnd = 0;
1601 if ( cFirst == '\'' )
1602 bParsed = dequote( rList, 0, nQuoteEnd, aDequoted );
1603 else if ( cFirst == '[' )
1605 // skip spaces after the opening bracket
1607 sal_Int32 nStartPos = 1;
1608 const sal_Int32 nListLen = rList.size();
1609 while (nStartPos < nListLen && rList[nStartPos] == ' ')
1610 ++nStartPos;
1612 if (nStartPos < nListLen && rList[nStartPos] == '\'') // quoted within the brackets?
1614 if ( dequote( rList, nStartPos, nQuoteEnd, aDequoted ) )
1616 // after the quoted string, there must be the closing bracket, optionally preceded by spaces,
1617 // and/or a function name
1618 while (nQuoteEnd < nListLen && rList[nQuoteEnd] == ' ')
1619 ++nQuoteEnd;
1621 // semicolon separates function name
1622 if (nQuoteEnd < nListLen && rList[nQuoteEnd] == ';' && pFunc)
1624 sal_Int32 nFuncEnd = 0;
1625 if ( parseFunction( rList, nQuoteEnd + 1, nFuncEnd, *pFunc ) )
1626 nQuoteEnd = nFuncEnd;
1628 if (nQuoteEnd < nListLen && rList[nQuoteEnd] == ']')
1630 ++nQuoteEnd; // include the closing bracket for the matched length
1631 bParsed = true;
1635 else
1637 // implicit quoting to the closing bracket
1639 sal_Int32 nClosePos = rList.find(']', nStartPos);
1640 if (nClosePos >= 0)
1642 sal_Int32 nNameEnd = nClosePos;
1643 sal_Int32 nSemiPos = rList.find(';', nStartPos);
1644 if (nSemiPos >= 0 && nSemiPos < nClosePos && pFunc)
1646 sal_Int32 nFuncEnd = 0;
1647 if (parseFunction(rList, nSemiPos+1, nFuncEnd, *pFunc))
1648 nNameEnd = nSemiPos;
1651 aDequoted = rList.substr(nStartPos, nNameEnd - nStartPos);
1652 // spaces before the closing bracket or semicolon
1653 aDequoted = comphelper::string::stripEnd(aDequoted, ' ');
1654 nQuoteEnd = nClosePos + 1;
1655 bParsed = true;
1660 if ( bParsed )
1662 nMatchList = nQuoteEnd; // match count in the list string, including quotes
1663 rDequoted = aDequoted;
1667 if (bParsed)
1669 // look for following space or end of string
1671 bool bValid = false;
1672 if ( nMatchList >= rList.size() )
1673 bValid = true;
1674 else
1676 sal_Unicode cNext = rList[nMatchList];
1677 if ( cNext == ' ' || ( bAllowBracket && cNext == '[' ) )
1678 bValid = true;
1681 if ( bValid )
1683 rMatched = nMatchList;
1684 return true;
1688 return false;
1691 bool isAtStart(
1692 const OUString& rList, const OUString& rSearch, sal_Int32& rMatched,
1693 bool bAllowBracket, sal_Int16* pFunc )
1695 sal_Int32 nMatchList = 0;
1696 sal_Int32 nMatchSearch = 0;
1697 sal_Unicode cFirst = rList[0];
1698 if ( cFirst == '\'' || cFirst == '[' )
1700 OUString aDequoted;
1701 bool bParsed = extractAtStart( rList, rMatched, bAllowBracket, pFunc, aDequoted);
1702 if ( bParsed && ScGlobal::GetTransliteration().isEqual( aDequoted, rSearch ) )
1704 nMatchList = rMatched; // match count in the list string, including quotes
1705 nMatchSearch = rSearch.getLength();
1708 else
1710 // otherwise look for search string at the start of rList
1711 ScGlobal::GetTransliteration().equals(
1712 rList, 0, rList.getLength(), nMatchList, rSearch, 0, rSearch.getLength(), nMatchSearch);
1715 if (nMatchSearch == rSearch.getLength())
1717 // search string is at start of rList - look for following space or end of string
1719 bool bValid = false;
1720 if ( sal::static_int_cast<sal_Int32>(nMatchList) >= rList.getLength() )
1721 bValid = true;
1722 else
1724 sal_Unicode cNext = rList[nMatchList];
1725 if ( cNext == ' ' || ( bAllowBracket && cNext == '[' ) )
1726 bValid = true;
1729 if ( bValid )
1731 rMatched = nMatchList;
1732 return true;
1736 return false;
1739 } // anonymous namespace
1741 bool ScDPObject::ParseFilters(
1742 OUString& rDataFieldName,
1743 std::vector<sheet::DataPilotFieldFilter>& rFilters,
1744 std::vector<sal_Int16>& rFilterFuncs, std::u16string_view rFilterList )
1746 // parse the string rFilterList into parameters for GetPivotData
1748 CreateObjects(); // create mxSource if not already done
1750 std::vector<OUString> aDataNames; // data fields (source name)
1751 std::vector<OUString> aGivenNames; // data fields (compound name)
1752 std::vector<OUString> aFieldNames; // column/row/data fields
1753 std::vector< uno::Sequence<OUString> > aFieldValueNames;
1754 std::vector< uno::Sequence<OUString> > aFieldValues;
1756 // get all the field and item names
1758 uno::Reference<container::XNameAccess> xDimsName = mxSource->getDimensions();
1759 uno::Reference<container::XIndexAccess> xIntDims = new ScNameToIndexAccess( xDimsName );
1760 sal_Int32 nDimCount = xIntDims->getCount();
1761 for ( sal_Int32 nDim = 0; nDim<nDimCount; nDim++ )
1763 uno::Reference<uno::XInterface> xIntDim(xIntDims->getByIndex(nDim), uno::UNO_QUERY);
1764 uno::Reference<container::XNamed> xDim( xIntDim, uno::UNO_QUERY );
1765 uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
1766 uno::Reference<sheet::XHierarchiesSupplier> xDimSupp( xDim, uno::UNO_QUERY );
1767 bool bDataLayout = ScUnoHelpFunctions::GetBoolProperty( xDimProp,
1768 SC_UNO_DP_ISDATALAYOUT );
1769 sheet::DataPilotFieldOrientation nOrient = ScUnoHelpFunctions::GetEnumProperty(
1770 xDimProp, SC_UNO_DP_ORIENTATION,
1771 sheet::DataPilotFieldOrientation_HIDDEN );
1772 if ( !bDataLayout )
1774 if ( nOrient == sheet::DataPilotFieldOrientation_DATA )
1776 OUString aSourceName;
1777 OUString aGivenName;
1778 ScDPOutput::GetDataDimensionNames( aSourceName, aGivenName, xIntDim );
1779 aDataNames.push_back( aSourceName );
1780 aGivenNames.push_back( aGivenName );
1782 else if ( nOrient != sheet::DataPilotFieldOrientation_HIDDEN )
1784 // get level names, as in ScDPOutput
1786 uno::Reference<container::XIndexAccess> xHiers = new ScNameToIndexAccess( xDimSupp->getHierarchies() );
1787 sal_Int32 nHierarchy = ScUnoHelpFunctions::GetLongProperty( xDimProp,
1788 SC_UNO_DP_USEDHIERARCHY );
1789 if ( nHierarchy >= xHiers->getCount() )
1790 nHierarchy = 0;
1792 uno::Reference<sheet::XLevelsSupplier> xHierSupp(xHiers->getByIndex(nHierarchy),
1793 uno::UNO_QUERY);
1794 if ( xHierSupp.is() )
1796 uno::Reference<container::XIndexAccess> xLevels = new ScNameToIndexAccess( xHierSupp->getLevels() );
1797 sal_Int32 nLevCount = xLevels->getCount();
1798 for (sal_Int32 nLev=0; nLev<nLevCount; nLev++)
1800 uno::Reference<uno::XInterface> xLevel(xLevels->getByIndex(nLev),
1801 uno::UNO_QUERY);
1802 uno::Reference<container::XNamed> xLevNam( xLevel, uno::UNO_QUERY );
1803 uno::Reference<sheet::XMembersSupplier> xLevSupp( xLevel, uno::UNO_QUERY );
1804 if ( xLevNam.is() && xLevSupp.is() )
1806 uno::Reference<sheet::XMembersAccess> xMembers = xLevSupp->getMembers();
1808 OUString aFieldName( xLevNam->getName() );
1809 // getElementNames() and getLocaleIndependentElementNames()
1810 // must be consecutive calls to obtain strings in matching order.
1811 uno::Sequence<OUString> aMemberValueNames( xMembers->getElementNames() );
1812 uno::Sequence<OUString> aMemberValues( xMembers->getLocaleIndependentElementNames() );
1814 aFieldNames.push_back( aFieldName );
1815 aFieldValueNames.push_back( aMemberValueNames );
1816 aFieldValues.push_back( aMemberValues );
1824 // compare and build filters
1826 SCSIZE nDataFields = aDataNames.size();
1827 SCSIZE nFieldCount = aFieldNames.size();
1828 OSL_ENSURE( aGivenNames.size() == nDataFields && aFieldValueNames.size() == nFieldCount &&
1829 aFieldValues.size() == nFieldCount, "wrong count" );
1831 bool bError = false;
1832 bool bHasData = false;
1833 OUString aRemaining(comphelper::string::strip(rFilterList, ' '));
1834 while (!aRemaining.isEmpty() && !bError)
1836 bool bUsed = false;
1838 // look for data field name
1840 for ( SCSIZE nDataPos=0; nDataPos<nDataFields && !bUsed; nDataPos++ )
1842 OUString aFound;
1843 sal_Int32 nMatched = 0;
1844 if (isAtStart(aRemaining, aDataNames[nDataPos], nMatched, false, nullptr))
1845 aFound = aDataNames[nDataPos];
1846 else if (isAtStart(aRemaining, aGivenNames[nDataPos], nMatched, false, nullptr))
1847 aFound = aGivenNames[nDataPos];
1849 if (!aFound.isEmpty())
1851 rDataFieldName = aFound;
1852 aRemaining = aRemaining.copy(nMatched);
1853 bHasData = true;
1854 bUsed = true;
1858 // look for field name
1860 OUString aSpecField;
1861 bool bHasFieldName = false;
1862 if ( !bUsed )
1864 sal_Int32 nMatched = 0;
1865 for ( SCSIZE nField=0; nField<nFieldCount && !bHasFieldName; nField++ )
1867 if (isAtStart(aRemaining, aFieldNames[nField], nMatched, true, nullptr))
1869 aSpecField = aFieldNames[nField];
1870 aRemaining = aRemaining.copy(nMatched);
1871 aRemaining = comphelper::string::stripStart(aRemaining, ' ');
1873 // field name has to be followed by item name in brackets
1874 if (aRemaining.startsWith("["))
1876 bHasFieldName = true;
1877 // bUsed remains false - still need the item
1879 else
1881 bUsed = true;
1882 bError = true;
1888 // look for field item
1890 if ( !bUsed )
1892 bool bItemFound = false;
1893 sal_Int32 nMatched = 0;
1894 OUString aFoundName;
1895 OUString aFoundValueName;
1896 OUString aFoundValue;
1897 sal_Int16 eFunc = sheet::GeneralFunction2::NONE;
1898 sal_Int16 eFoundFunc = sheet::GeneralFunction2::NONE;
1900 OUString aQueryValueName;
1901 const bool bHasQuery = extractAtStart( aRemaining, nMatched, false, &eFunc, aQueryValueName);
1903 OUString aQueryValue = aQueryValueName;
1904 if (mpTableData)
1906 ScInterpreterContext& rContext = mpTableData->GetCacheTable().getCache().GetInterpreterContext();
1907 // Parse possible number from aQueryValueName and format
1908 // locale independent as aQueryValue.
1909 sal_uInt32 nNumFormat = 0;
1910 double fValue;
1911 if (rContext.NFIsNumberFormat(aQueryValueName, nNumFormat, fValue))
1912 aQueryValue = ScDPCache::GetLocaleIndependentFormattedString(fValue, rContext, nNumFormat);
1915 for ( SCSIZE nField=0; nField<nFieldCount; nField++ )
1917 // If a field name is given, look in that field only, otherwise in all fields.
1918 // aSpecField is initialized from aFieldNames array, so exact comparison can be used.
1919 if ( !bHasFieldName || aFieldNames[nField] == aSpecField )
1921 const uno::Sequence<OUString>& rItemNames = aFieldValueNames[nField];
1922 const uno::Sequence<OUString>& rItemValues = aFieldValues[nField];
1923 sal_Int32 nItemCount = rItemNames.getLength();
1924 assert(nItemCount == rItemValues.getLength());
1925 const OUString* pItemNamesArr = rItemNames.getConstArray();
1926 const OUString* pItemValuesArr = rItemValues.getConstArray();
1927 for ( sal_Int32 nItem=0; nItem<nItemCount; nItem++ )
1929 bool bThisItemFound;
1930 if (bHasQuery)
1932 // First check given value name against both.
1933 bThisItemFound = ScGlobal::GetTransliteration().isEqual(
1934 aQueryValueName, pItemNamesArr[nItem]);
1935 if (!bThisItemFound && pItemValuesArr[nItem] != pItemNamesArr[nItem])
1936 bThisItemFound = ScGlobal::GetTransliteration().isEqual(
1937 aQueryValueName, pItemValuesArr[nItem]);
1938 if (!bThisItemFound && aQueryValueName != aQueryValue)
1940 // Second check locale independent value
1941 // against both.
1942 /* TODO: or check only value string against
1943 * value string, not against the value name? */
1944 bThisItemFound = ScGlobal::GetTransliteration().isEqual(
1945 aQueryValue, pItemNamesArr[nItem]);
1946 if (!bThisItemFound && pItemValuesArr[nItem] != pItemNamesArr[nItem])
1947 bThisItemFound = ScGlobal::GetTransliteration().isEqual(
1948 aQueryValue, pItemValuesArr[nItem]);
1951 else
1953 bThisItemFound = isAtStart( aRemaining, pItemNamesArr[nItem], nMatched, false, &eFunc );
1954 if (!bThisItemFound && pItemValuesArr[nItem] != pItemNamesArr[nItem])
1955 bThisItemFound = isAtStart( aRemaining, pItemValuesArr[nItem], nMatched, false, &eFunc );
1956 /* TODO: this checks only the given value name,
1957 * check also locale independent value. But we'd
1958 * have to do that in each iteration of the loop
1959 * inside isAtStart() since a query could not be
1960 * extracted and a match could be on the passed
1961 * item value name string or item value string
1962 * starting at aRemaining. */
1964 if (bThisItemFound)
1966 if ( bItemFound )
1967 bError = true; // duplicate (also across fields)
1968 else
1970 aFoundName = aFieldNames[nField];
1971 aFoundValueName = pItemNamesArr[nItem];
1972 aFoundValue = pItemValuesArr[nItem];
1973 eFoundFunc = eFunc;
1974 bItemFound = true;
1975 bUsed = true;
1982 if ( bItemFound && !bError )
1984 sheet::DataPilotFieldFilter aField;
1985 aField.FieldName = aFoundName;
1986 aField.MatchValueName = aFoundValueName;
1987 aField.MatchValue = aFoundValue;
1988 rFilters.push_back(aField);
1989 rFilterFuncs.push_back(eFoundFunc);
1990 aRemaining = aRemaining.copy(nMatched);
1994 if ( !bUsed )
1995 bError = true;
1997 // remove any number of spaces between entries
1998 aRemaining = comphelper::string::stripStart(aRemaining, ' ');
2001 if ( !bError && !bHasData && aDataNames.size() == 1 )
2003 // if there's only one data field, its name need not be specified
2004 rDataFieldName = aDataNames[0];
2005 bHasData = true;
2008 return bHasData && !bError;
2011 void ScDPObject::ToggleDetails(const DataPilotTableHeaderData& rElemDesc, ScDPObject* pDestObj)
2013 CreateObjects(); // create mxSource if not already done
2015 // find dimension name
2017 uno::Reference<container::XNamed> xDim;
2018 uno::Reference<container::XNameAccess> xDimsName = mxSource->getDimensions();
2019 uno::Reference<container::XIndexAccess> xIntDims = new ScNameToIndexAccess( xDimsName );
2020 tools::Long nIntCount = xIntDims->getCount();
2021 if ( rElemDesc.Dimension < nIntCount )
2023 xDim.set(xIntDims->getByIndex(rElemDesc.Dimension), uno::UNO_QUERY);
2025 OSL_ENSURE( xDim.is(), "dimension not found" );
2026 if ( !xDim.is() ) return;
2027 OUString aDimName = xDim->getName();
2029 uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
2030 bool bDataLayout = ScUnoHelpFunctions::GetBoolProperty( xDimProp,
2031 SC_UNO_DP_ISDATALAYOUT );
2032 if (bDataLayout)
2034 // the elements of the data layout dimension can't be found by their names
2035 // -> don't change anything
2036 return;
2039 // query old state
2041 tools::Long nHierCount = 0;
2042 uno::Reference<container::XIndexAccess> xHiers;
2043 uno::Reference<sheet::XHierarchiesSupplier> xHierSupp( xDim, uno::UNO_QUERY );
2044 if ( xHierSupp.is() )
2046 uno::Reference<container::XNameAccess> xHiersName = xHierSupp->getHierarchies();
2047 xHiers = new ScNameToIndexAccess( xHiersName );
2048 nHierCount = xHiers->getCount();
2050 uno::Reference<uno::XInterface> xHier;
2051 if ( rElemDesc.Hierarchy < nHierCount )
2052 xHier.set(xHiers->getByIndex(rElemDesc.Hierarchy), uno::UNO_QUERY);
2053 OSL_ENSURE( xHier.is(), "hierarchy not found" );
2054 if ( !xHier.is() ) return;
2056 tools::Long nLevCount = 0;
2057 uno::Reference<container::XIndexAccess> xLevels;
2058 uno::Reference<sheet::XLevelsSupplier> xLevSupp( xHier, uno::UNO_QUERY );
2059 if ( xLevSupp.is() )
2061 uno::Reference<container::XNameAccess> xLevsName = xLevSupp->getLevels();
2062 xLevels = new ScNameToIndexAccess( xLevsName );
2063 nLevCount = xLevels->getCount();
2065 uno::Reference<uno::XInterface> xLevel;
2066 if ( rElemDesc.Level < nLevCount )
2067 xLevel.set(xLevels->getByIndex(rElemDesc.Level), uno::UNO_QUERY);
2068 OSL_ENSURE( xLevel.is(), "level not found" );
2069 if ( !xLevel.is() ) return;
2071 uno::Reference<sheet::XMembersAccess> xMembers;
2072 uno::Reference<sheet::XMembersSupplier> xMbrSupp( xLevel, uno::UNO_QUERY );
2073 if ( xMbrSupp.is() )
2074 xMembers = xMbrSupp->getMembers();
2076 bool bFound = false;
2077 bool bShowDetails = true;
2079 if ( xMembers.is() )
2081 if ( xMembers->hasByName(rElemDesc.MemberName) )
2083 uno::Reference<beans::XPropertySet> xMbrProp(xMembers->getByName(rElemDesc.MemberName),
2084 uno::UNO_QUERY);
2085 if ( xMbrProp.is() )
2087 bShowDetails = ScUnoHelpFunctions::GetBoolProperty( xMbrProp,
2088 SC_UNO_DP_SHOWDETAILS );
2089 //TODO: don't set bFound if property is unknown?
2090 bFound = true;
2095 OSL_ENSURE( bFound, "member not found" );
2097 //TODO: use Hierarchy and Level in SaveData !!!!
2099 // modify pDestObj if set, this object otherwise
2100 ScDPSaveData* pModifyData = pDestObj ? ( pDestObj->mpSaveData.get() ) : mpSaveData.get();
2101 OSL_ENSURE( pModifyData, "no data?" );
2102 if ( pModifyData )
2104 const OUString aName = rElemDesc.MemberName;
2105 pModifyData->GetDimensionByName(aDimName)->
2106 GetMemberByName(aName)->SetShowDetails( !bShowDetails ); // toggle
2108 if ( pDestObj )
2109 pDestObj->InvalidateData(); // re-init source from SaveData
2110 else
2111 InvalidateData(); // re-init source from SaveData
2115 static PivotFunc lcl_FirstSubTotal( const uno::Reference<beans::XPropertySet>& xDimProp ) // PIVOT_FUNC mask
2117 uno::Reference<sheet::XHierarchiesSupplier> xDimSupp( xDimProp, uno::UNO_QUERY );
2118 if ( xDimProp.is() && xDimSupp.is() )
2120 uno::Reference<container::XIndexAccess> xHiers = new ScNameToIndexAccess( xDimSupp->getHierarchies() );
2121 tools::Long nHierarchy = ScUnoHelpFunctions::GetLongProperty( xDimProp,
2122 SC_UNO_DP_USEDHIERARCHY );
2123 if ( nHierarchy >= xHiers->getCount() )
2124 nHierarchy = 0;
2126 uno::Reference<sheet::XLevelsSupplier> xHierSupp(xHiers->getByIndex(nHierarchy),
2127 uno::UNO_QUERY);
2128 if ( xHierSupp.is() )
2130 uno::Reference<container::XIndexAccess> xLevels = new ScNameToIndexAccess( xHierSupp->getLevels() );
2131 uno::Reference<uno::XInterface> xLevel(xLevels->getByIndex(0), uno::UNO_QUERY);
2132 uno::Reference<beans::XPropertySet> xLevProp( xLevel, uno::UNO_QUERY );
2133 if ( xLevProp.is() )
2135 uno::Any aSubAny;
2138 aSubAny = xLevProp->getPropertyValue( SC_UNO_DP_SUBTOTAL2 );
2140 catch(uno::Exception&)
2143 uno::Sequence<sal_Int16> aSeq;
2144 if ( aSubAny >>= aSeq )
2146 PivotFunc nMask = PivotFunc::NONE;
2147 for (const sal_Int16 nElem : aSeq)
2148 nMask |= ScDataPilotConversion::FunctionBit(nElem);
2149 return nMask;
2155 OSL_FAIL("FirstSubTotal: NULL");
2156 return PivotFunc::NONE;
2159 namespace {
2161 class FindByColumn
2163 SCCOL mnCol;
2164 PivotFunc mnMask;
2165 public:
2166 FindByColumn(SCCOL nCol, PivotFunc nMask) : mnCol(nCol), mnMask(nMask) {}
2167 bool operator() (const ScPivotField& r) const
2169 return r.nCol == mnCol && r.nFuncMask == mnMask;
2175 static void lcl_FillOldFields( ScPivotFieldVector& rFields,
2176 const uno::Reference<sheet::XDimensionsSupplier>& xSource,
2177 sheet::DataPilotFieldOrientation nOrient, bool bAddData )
2179 ScPivotFieldVector aFields;
2181 bool bDataFound = false;
2183 //TODO: merge multiple occurrences (data field with different functions)
2184 //TODO: force data field in one dimension
2186 vector<tools::Long> aPos;
2188 uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions();
2189 uno::Reference<container::XIndexAccess> xDims = new ScNameToIndexAccess( xDimsName );
2190 tools::Long nDimCount = xDims->getCount();
2191 for (tools::Long nDim = 0; nDim < nDimCount; ++nDim)
2193 // dimension properties
2194 uno::Reference<beans::XPropertySet> xDimProp(xDims->getByIndex(nDim), uno::UNO_QUERY);
2196 // dimension orientation, hidden by default.
2197 sheet::DataPilotFieldOrientation nDimOrient = ScUnoHelpFunctions::GetEnumProperty(
2198 xDimProp, SC_UNO_DP_ORIENTATION,
2199 sheet::DataPilotFieldOrientation_HIDDEN );
2201 if ( xDimProp.is() && nDimOrient == nOrient )
2203 // Let's take this dimension.
2205 // function mask.
2206 PivotFunc nMask = PivotFunc::NONE;
2207 if ( nOrient == sheet::DataPilotFieldOrientation_DATA )
2209 sal_Int16 eFunc = ScUnoHelpFunctions::GetShortProperty(
2210 xDimProp, SC_UNO_DP_FUNCTION2,
2211 sheet::GeneralFunction2::NONE );
2212 if ( eFunc == sheet::GeneralFunction2::AUTO )
2214 //TODO: test for numeric data
2215 eFunc = sheet::GeneralFunction2::SUM;
2217 nMask = ScDataPilotConversion::FunctionBit(eFunc);
2219 else
2220 nMask = lcl_FirstSubTotal( xDimProp ); // from first hierarchy
2222 // is this data layout dimension?
2223 bool bDataLayout = ScUnoHelpFunctions::GetBoolProperty(
2224 xDimProp, SC_UNO_DP_ISDATALAYOUT);
2226 // is this dimension cloned?
2227 tools::Long nDupSource = -1;
2230 uno::Any aOrigAny = xDimProp->getPropertyValue(SC_UNO_DP_ORIGINAL_POS);
2231 sal_Int32 nTmp = 0;
2232 if (aOrigAny >>= nTmp)
2233 nDupSource = nTmp;
2235 catch(uno::Exception&)
2239 sal_uInt8 nDupCount = 0;
2240 if (nDupSource >= 0)
2242 // this dimension is cloned.
2244 SCCOL nCompCol; // ID of the original dimension.
2245 if ( bDataLayout )
2246 nCompCol = PIVOT_DATA_FIELD;
2247 else
2248 nCompCol = static_cast<SCCOL>(nDupSource); //TODO: seek source column from name
2250 ScPivotFieldVector::iterator it = std::find_if(aFields.begin(), aFields.end(), FindByColumn(nCompCol, nMask));
2251 if (it != aFields.end())
2252 nDupCount = it->mnDupCount + 1;
2255 aFields.emplace_back();
2256 ScPivotField& rField = aFields.back();
2257 if (bDataLayout)
2259 rField.nCol = PIVOT_DATA_FIELD;
2260 bDataFound = true;
2262 else
2264 rField.mnOriginalDim = nDupSource;
2265 rField.nCol = static_cast<SCCOL>(nDim); //TODO: seek source column from name
2268 rField.nFuncMask = nMask;
2269 rField.mnDupCount = nDupCount;
2270 tools::Long nPos = ScUnoHelpFunctions::GetLongProperty(
2271 xDimProp, SC_UNO_DP_POSITION);
2272 aPos.push_back(nPos);
2276 if (nOrient == sheet::DataPilotFieldOrientation_DATA)
2277 xDimProp->getPropertyValue(SC_UNO_DP_REFVALUE)
2278 >>= rField.maFieldRef;
2280 catch (uno::Exception&)
2286 // sort by getPosition() value
2288 size_t nOutCount = aFields.size();
2289 if (nOutCount >= 1)
2291 for (size_t i = 0; i < nOutCount - 1; ++i)
2293 for (size_t j = 0; j + i < nOutCount - 1; ++j)
2295 if ( aPos[j+1] < aPos[j] )
2297 std::swap( aPos[j], aPos[j+1] );
2298 std::swap( aFields[j], aFields[j+1] );
2304 if (bAddData && !bDataFound)
2305 aFields.emplace_back(PIVOT_DATA_FIELD);
2307 rFields.swap(aFields);
2310 void ScDPObject::FillOldParam(ScPivotParam& rParam) const
2312 const_cast<ScDPObject*>(this)->CreateObjects(); // mxSource is needed for field numbers
2314 if (!mxSource.is())
2315 return;
2317 rParam.nCol = maOutputRange.aStart.Col();
2318 rParam.nRow = maOutputRange.aStart.Row();
2319 rParam.nTab = maOutputRange.aStart.Tab();
2320 // ppLabelArr / nLabels is not changed
2322 bool bAddData = lcl_GetDataGetOrientation(mxSource) == sheet::DataPilotFieldOrientation_HIDDEN;
2323 lcl_FillOldFields(
2324 rParam.maPageFields, mxSource, sheet::DataPilotFieldOrientation_PAGE, false);
2325 lcl_FillOldFields(
2326 rParam.maColFields, mxSource, sheet::DataPilotFieldOrientation_COLUMN, bAddData);
2327 lcl_FillOldFields(
2328 rParam.maRowFields, mxSource, sheet::DataPilotFieldOrientation_ROW, false);
2329 lcl_FillOldFields(
2330 rParam.maDataFields, mxSource, sheet::DataPilotFieldOrientation_DATA, false);
2332 uno::Reference<beans::XPropertySet> xProp(mxSource, uno::UNO_QUERY);
2333 if (!xProp.is())
2334 return;
2338 rParam.bMakeTotalCol = ScUnoHelpFunctions::GetBoolProperty( xProp,
2339 SC_UNO_DP_COLGRAND, true );
2340 rParam.bMakeTotalRow = ScUnoHelpFunctions::GetBoolProperty( xProp,
2341 SC_UNO_DP_ROWGRAND, true );
2343 // following properties may be missing for external sources
2344 rParam.bIgnoreEmptyRows = ScUnoHelpFunctions::GetBoolProperty( xProp,
2345 SC_UNO_DP_IGNOREEMPTY );
2346 rParam.bDetectCategories = ScUnoHelpFunctions::GetBoolProperty( xProp,
2347 SC_UNO_DP_REPEATEMPTY );
2349 catch(uno::Exception&)
2351 // no error
2355 static void lcl_FillLabelData( ScDPLabelData& rData, const uno::Reference< beans::XPropertySet >& xDimProp )
2357 uno::Reference<sheet::XHierarchiesSupplier> xDimSupp( xDimProp, uno::UNO_QUERY );
2358 if (!xDimProp.is() || !xDimSupp.is())
2359 return;
2361 uno::Reference<container::XIndexAccess> xHiers = new ScNameToIndexAccess( xDimSupp->getHierarchies() );
2362 tools::Long nHierarchy = ScUnoHelpFunctions::GetLongProperty(
2363 xDimProp, SC_UNO_DP_USEDHIERARCHY);
2364 if ( nHierarchy >= xHiers->getCount() )
2365 nHierarchy = 0;
2366 rData.mnUsedHier = nHierarchy;
2368 uno::Reference<sheet::XLevelsSupplier> xHierSupp(xHiers->getByIndex(nHierarchy),
2369 uno::UNO_QUERY);
2370 if (!xHierSupp.is())
2371 return;
2373 uno::Reference<container::XIndexAccess> xLevels =
2374 new ScNameToIndexAccess( xHierSupp->getLevels() );
2376 uno::Reference<beans::XPropertySet> xLevProp(xLevels->getByIndex(0), uno::UNO_QUERY);
2377 if (!xLevProp.is())
2378 return;
2380 rData.mbShowAll = ScUnoHelpFunctions::GetBoolProperty(
2381 xLevProp, SC_UNO_DP_SHOWEMPTY);
2383 rData.mbRepeatItemLabels = ScUnoHelpFunctions::GetBoolProperty(
2384 xLevProp, SC_UNO_DP_REPEATITEMLABELS);
2388 xLevProp->getPropertyValue( SC_UNO_DP_SORTING )
2389 >>= rData.maSortInfo;
2390 xLevProp->getPropertyValue( SC_UNO_DP_LAYOUT )
2391 >>= rData.maLayoutInfo;
2392 xLevProp->getPropertyValue( SC_UNO_DP_AUTOSHOW )
2393 >>= rData.maShowInfo;
2395 catch(uno::Exception&)
2400 void ScDPObject::FillLabelDataForDimension(
2401 const uno::Reference<container::XIndexAccess>& xDims, sal_Int32 nDim, ScDPLabelData& rLabelData)
2403 uno::Reference<uno::XInterface> xIntDim(xDims->getByIndex(nDim), uno::UNO_QUERY);
2404 uno::Reference<container::XNamed> xDimName( xIntDim, uno::UNO_QUERY );
2405 uno::Reference<beans::XPropertySet> xDimProp( xIntDim, uno::UNO_QUERY );
2407 if (!xDimName.is() || !xDimProp.is())
2408 return;
2410 bool bData = ScUnoHelpFunctions::GetBoolProperty(
2411 xDimProp, SC_UNO_DP_ISDATALAYOUT);
2412 //TODO: error checking -- is "IsDataLayoutDimension" property required??
2414 sal_Int32 nOrigPos = -1;
2415 OUString aFieldName;
2418 aFieldName = xDimName->getName();
2419 uno::Any aOrigAny = xDimProp->getPropertyValue(SC_UNO_DP_ORIGINAL_POS);
2420 aOrigAny >>= nOrigPos;
2422 catch(uno::Exception&)
2426 OUString aLayoutName = ScUnoHelpFunctions::GetStringProperty(
2427 xDimProp, SC_UNO_DP_LAYOUTNAME, OUString());
2429 OUString aSubtotalName = ScUnoHelpFunctions::GetStringProperty(
2430 xDimProp, SC_UNO_DP_FIELD_SUBTOTALNAME, OUString());
2432 // Name from the UNO dimension object may have trailing '*'s in which
2433 // case it's a duplicate dimension. Convert that to a duplicate index.
2435 sal_uInt8 nDupCount = ScDPUtil::getDuplicateIndex(aFieldName);
2436 aFieldName = ScDPUtil::getSourceDimensionName(aFieldName);
2438 rLabelData.maName = aFieldName;
2439 rLabelData.mnCol = static_cast<SCCOL>(nDim);
2440 rLabelData.mnDupCount = nDupCount;
2441 rLabelData.mbDataLayout = bData;
2442 rLabelData.mbIsValue = true; //TODO: check
2444 if (bData)
2445 return;
2447 rLabelData.mnOriginalDim = static_cast<tools::Long>(nOrigPos);
2448 rLabelData.maLayoutName = aLayoutName;
2449 rLabelData.maSubtotalName = aSubtotalName;
2450 if (nOrigPos >= 0)
2451 // This is a duplicated dimension. Use the original dimension index.
2452 nDim = nOrigPos;
2453 GetHierarchies(nDim, rLabelData.maHiers);
2454 GetMembers(nDim, GetUsedHierarchy(nDim), rLabelData.maMembers);
2455 lcl_FillLabelData(rLabelData, xDimProp);
2456 rLabelData.mnFlags = ScUnoHelpFunctions::GetLongProperty(
2457 xDimProp, SC_UNO_DP_FLAGS );
2460 void ScDPObject::FillLabelData(sal_Int32 nDim, ScDPLabelData& rLabels)
2462 CreateObjects();
2463 if (!mxSource.is())
2464 return;
2466 uno::Reference<container::XNameAccess> xDimsName = mxSource->getDimensions();
2467 uno::Reference<container::XIndexAccess> xDims = new ScNameToIndexAccess( xDimsName );
2468 sal_Int32 nDimCount = xDims->getCount();
2469 if (nDimCount <= 0 || nDim >= nDimCount)
2470 return;
2472 FillLabelDataForDimension(xDims, nDim, rLabels);
2475 void ScDPObject::FillLabelData(ScPivotParam& rParam)
2477 rParam.maLabelArray.clear();
2479 CreateObjects();
2480 if (!mxSource.is())
2481 return;
2483 uno::Reference<container::XNameAccess> xDimsName = mxSource->getDimensions();
2484 uno::Reference<container::XIndexAccess> xDims = new ScNameToIndexAccess( xDimsName );
2485 sal_Int32 nDimCount = xDims->getCount();
2486 if (nDimCount <= 0)
2487 return;
2489 for (sal_Int32 nDim = 0; nDim < nDimCount; ++nDim)
2491 ScDPLabelData* pNewLabel = new ScDPLabelData;
2492 FillLabelDataForDimension(xDims, nDim, *pNewLabel);
2493 rParam.maLabelArray.push_back(std::unique_ptr<ScDPLabelData>(pNewLabel));
2497 void ScDPObject::GetFieldIdsNames(sheet::DataPilotFieldOrientation nOrient, std::vector<tools::Long>& rIndices,
2498 std::vector<OUString>& rNames)
2500 CreateObjects();
2501 if (!mxSource.is())
2502 return;
2504 uno::Reference<container::XNameAccess> xDimsName = mxSource->getDimensions();
2505 uno::Reference<container::XIndexAccess> xDims = new ScNameToIndexAccess( xDimsName );
2506 tools::Long nDimCount = xDims->getCount();
2507 for (tools::Long nDim = 0; nDim < nDimCount; ++nDim)
2509 uno::Reference<uno::XInterface> xIntDim(xDims->getByIndex(nDim), uno::UNO_QUERY);
2510 uno::Reference<container::XNamed> xDimName(xIntDim, uno::UNO_QUERY);
2511 uno::Reference<beans::XPropertySet> xDimProp(xIntDim, uno::UNO_QUERY);
2513 sheet::DataPilotFieldOrientation nDimOrient = ScUnoHelpFunctions::GetEnumProperty(
2514 xDimProp, SC_UNO_DP_ORIENTATION,
2515 sheet::DataPilotFieldOrientation_HIDDEN );
2517 if ( xDimProp.is() && nDimOrient == nOrient)
2519 rIndices.push_back(nDim);
2520 rNames.push_back(xDimName->getName());
2525 bool ScDPObject::GetHierarchiesNA( sal_Int32 nDim, uno::Reference< container::XNameAccess >& xHiers )
2527 bool bRet = false;
2528 uno::Reference<container::XNameAccess> xDimsName( GetSource()->getDimensions() );
2529 uno::Reference<container::XIndexAccess> xIntDims(new ScNameToIndexAccess( xDimsName ));
2530 if( xIntDims.is() )
2532 uno::Reference<sheet::XHierarchiesSupplier> xHierSup(xIntDims->getByIndex( nDim ), uno::UNO_QUERY);
2533 if (xHierSup.is())
2535 xHiers.set( xHierSup->getHierarchies() );
2536 bRet = xHiers.is();
2539 return bRet;
2542 void ScDPObject::GetHierarchies( sal_Int32 nDim, uno::Sequence< OUString >& rHiers )
2544 uno::Reference< container::XNameAccess > xHiersNA;
2545 if( GetHierarchiesNA( nDim, xHiersNA ) )
2547 rHiers = xHiersNA->getElementNames();
2551 sal_Int32 ScDPObject::GetUsedHierarchy( sal_Int32 nDim )
2553 sal_Int32 nHier = 0;
2554 uno::Reference<container::XNameAccess> xDimsName( GetSource()->getDimensions() );
2555 uno::Reference<container::XIndexAccess> xIntDims(new ScNameToIndexAccess( xDimsName ));
2556 uno::Reference<beans::XPropertySet> xDim(xIntDims->getByIndex( nDim ), uno::UNO_QUERY);
2557 if (xDim.is())
2558 nHier = ScUnoHelpFunctions::GetLongProperty( xDim, SC_UNO_DP_USEDHIERARCHY );
2559 return nHier;
2562 bool ScDPObject::GetMembersNA( sal_Int32 nDim, uno::Reference< sheet::XMembersAccess >& xMembers )
2564 return GetMembersNA( nDim, GetUsedHierarchy( nDim ), xMembers );
2567 bool ScDPObject::GetMembersNA( sal_Int32 nDim, sal_Int32 nHier, uno::Reference< sheet::XMembersAccess >& xMembers )
2569 bool bRet = false;
2570 uno::Reference<container::XNameAccess> xDimsName( GetSource()->getDimensions() );
2571 uno::Reference<container::XIndexAccess> xIntDims(new ScNameToIndexAccess( xDimsName ));
2572 uno::Reference<beans::XPropertySet> xDim(xIntDims->getByIndex( nDim ), uno::UNO_QUERY);
2573 if (xDim.is())
2575 uno::Reference<sheet::XHierarchiesSupplier> xHierSup(xDim, uno::UNO_QUERY);
2576 if (xHierSup.is())
2578 uno::Reference<container::XIndexAccess> xHiers(new ScNameToIndexAccess(xHierSup->getHierarchies()));
2579 uno::Reference<sheet::XLevelsSupplier> xLevSupp( xHiers->getByIndex(nHier), uno::UNO_QUERY );
2580 if ( xLevSupp.is() )
2582 uno::Reference<container::XIndexAccess> xLevels(new ScNameToIndexAccess( xLevSupp->getLevels()));
2583 if (xLevels.is())
2585 sal_Int32 nLevCount = xLevels->getCount();
2586 if (nLevCount > 0)
2588 uno::Reference<sheet::XMembersSupplier> xMembSupp( xLevels->getByIndex(0), uno::UNO_QUERY );
2589 if ( xMembSupp.is() )
2591 xMembers.set(xMembSupp->getMembers());
2592 bRet = true;
2599 return bRet;
2602 // convert old pivot tables into new datapilot tables
2604 namespace {
2606 OUString lcl_GetDimName( const uno::Reference<sheet::XDimensionsSupplier>& xSource, tools::Long nDim )
2608 OUString aName;
2609 if ( xSource.is() )
2611 uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions();
2612 uno::Reference<container::XIndexAccess> xDims = new ScNameToIndexAccess( xDimsName );
2613 tools::Long nDimCount = xDims->getCount();
2614 if ( nDim < nDimCount )
2616 uno::Reference<container::XNamed> xDimName(xDims->getByIndex(nDim), uno::UNO_QUERY);
2617 if (xDimName.is())
2621 aName = xDimName->getName();
2623 catch(uno::Exception&)
2629 return aName;
2632 bool hasFieldColumn(const vector<ScPivotField>* pRefFields, SCCOL nCol)
2634 if (!pRefFields)
2635 return false;
2637 return std::any_of(pRefFields->begin(), pRefFields->end(),
2638 [&nCol](const ScPivotField& rField) {
2639 // This array of fields contains the specified column.
2640 return rField.nCol == nCol; });
2643 class FindByOriginalDim
2645 tools::Long mnDim;
2646 public:
2647 explicit FindByOriginalDim(tools::Long nDim) : mnDim(nDim) {}
2648 bool operator() (const ScPivotField& r) const
2650 return mnDim == r.getOriginalDim();
2656 void ScDPObject::ConvertOrientation(
2657 ScDPSaveData& rSaveData, const ScPivotFieldVector& rFields, sheet::DataPilotFieldOrientation nOrient,
2658 const Reference<XDimensionsSupplier>& xSource,
2659 const ScDPLabelDataVector& rLabels,
2660 const ScPivotFieldVector* pRefColFields,
2661 const ScPivotFieldVector* pRefRowFields,
2662 const ScPivotFieldVector* pRefPageFields )
2664 ScPivotFieldVector::const_iterator itr, itrBeg = rFields.begin(), itrEnd = rFields.end();
2665 for (itr = itrBeg; itr != itrEnd; ++itr)
2667 const ScPivotField& rField = *itr;
2669 tools::Long nCol = rField.getOriginalDim();
2670 PivotFunc nFuncs = rField.nFuncMask;
2671 const sheet::DataPilotFieldReference& rFieldRef = rField.maFieldRef;
2673 ScDPSaveDimension* pDim = nullptr;
2674 if ( nCol == PIVOT_DATA_FIELD )
2675 pDim = rSaveData.GetDataLayoutDimension();
2676 else
2678 OUString aDocStr = lcl_GetDimName( xSource, nCol ); // cols must start at 0
2679 if (!aDocStr.isEmpty())
2680 pDim = rSaveData.GetDimensionByName(aDocStr);
2681 else
2682 pDim = nullptr;
2685 if (!pDim)
2686 continue;
2688 if ( nOrient == sheet::DataPilotFieldOrientation_DATA ) // set summary function
2690 // generate an individual entry for each function
2691 bool bFirst = true;
2693 // if a dimension is used for column/row/page and data,
2694 // use duplicated dimensions for all data occurrences
2695 if (hasFieldColumn(pRefColFields, nCol))
2696 bFirst = false;
2698 if (bFirst && hasFieldColumn(pRefRowFields, nCol))
2699 bFirst = false;
2701 if (bFirst && hasFieldColumn(pRefPageFields, nCol))
2702 bFirst = false;
2704 if (bFirst)
2706 // if set via api, a data column may occur several times
2707 // (if the function hasn't been changed yet) -> also look for duplicate data column
2708 bFirst = std::none_of(itrBeg, itr, FindByOriginalDim(nCol));
2711 ScGeneralFunction eFunc = ScDataPilotConversion::FirstFunc(rField.nFuncMask);
2712 if (!bFirst)
2713 pDim = rSaveData.DuplicateDimension(pDim->GetName());
2714 pDim->SetOrientation(nOrient);
2715 pDim->SetFunction(eFunc);
2717 if( rFieldRef.ReferenceType == sheet::DataPilotFieldReferenceType::NONE )
2718 pDim->SetReferenceValue(nullptr);
2719 else
2720 pDim->SetReferenceValue(&rFieldRef);
2722 else // set SubTotals
2724 pDim->SetOrientation( nOrient );
2726 std::vector<ScGeneralFunction> nSubTotalFuncs;
2727 nSubTotalFuncs.reserve(16);
2728 sal_uInt16 nMask = 1;
2729 for (sal_uInt16 nBit=0; nBit<16; nBit++)
2731 if ( nFuncs & static_cast<PivotFunc>(nMask) )
2732 nSubTotalFuncs.push_back( ScDataPilotConversion::FirstFunc( static_cast<PivotFunc>(nMask) ) );
2733 nMask *= 2;
2735 pDim->SetSubTotals( std::move(nSubTotalFuncs) );
2737 // ShowEmpty was implicit in old tables,
2738 // must be set for data layout dimension (not accessible in dialog)
2739 if ( nCol == PIVOT_DATA_FIELD )
2740 pDim->SetShowEmpty( true );
2743 size_t nDimIndex = rField.nCol;
2744 pDim->RemoveLayoutName();
2745 pDim->RemoveSubtotalName();
2746 if (nDimIndex < rLabels.size())
2748 const ScDPLabelData& rLabel = *rLabels[nDimIndex];
2749 if (!rLabel.maLayoutName.isEmpty())
2750 pDim->SetLayoutName(rLabel.maLayoutName);
2751 if (!rLabel.maSubtotalName.isEmpty())
2752 pDim->SetSubtotalName(rLabel.maSubtotalName);
2757 bool ScDPObject::IsOrientationAllowed( sheet::DataPilotFieldOrientation nOrient, sal_Int32 nDimFlags )
2759 bool bAllowed = true;
2760 switch (nOrient)
2762 case sheet::DataPilotFieldOrientation_PAGE:
2763 bAllowed = ( nDimFlags & sheet::DimensionFlags::NO_PAGE_ORIENTATION ) == 0;
2764 break;
2765 case sheet::DataPilotFieldOrientation_COLUMN:
2766 bAllowed = ( nDimFlags & sheet::DimensionFlags::NO_COLUMN_ORIENTATION ) == 0;
2767 break;
2768 case sheet::DataPilotFieldOrientation_ROW:
2769 bAllowed = ( nDimFlags & sheet::DimensionFlags::NO_ROW_ORIENTATION ) == 0;
2770 break;
2771 case sheet::DataPilotFieldOrientation_DATA:
2772 bAllowed = ( nDimFlags & sheet::DimensionFlags::NO_DATA_ORIENTATION ) == 0;
2773 break;
2774 default:
2776 // allowed to remove from previous orientation
2779 return bAllowed;
2782 bool ScDPObject::HasRegisteredSources()
2784 bool bFound = false;
2786 uno::Reference<lang::XMultiServiceFactory> xManager = comphelper::getProcessServiceFactory();
2787 uno::Reference<container::XContentEnumerationAccess> xEnAc( xManager, uno::UNO_QUERY );
2788 if ( xEnAc.is() )
2790 uno::Reference<container::XEnumeration> xEnum = xEnAc->createContentEnumeration(
2791 SCDPSOURCE_SERVICE );
2792 if ( xEnum.is() && xEnum->hasMoreElements() )
2793 bFound = true;
2796 return bFound;
2799 std::vector<OUString> ScDPObject::GetRegisteredSources()
2801 std::vector<OUString> aVec;
2803 // use implementation names...
2805 uno::Reference<lang::XMultiServiceFactory> xManager = comphelper::getProcessServiceFactory();
2806 uno::Reference<container::XContentEnumerationAccess> xEnAc( xManager, uno::UNO_QUERY );
2807 if ( xEnAc.is() )
2809 uno::Reference<container::XEnumeration> xEnum = xEnAc->createContentEnumeration(
2810 SCDPSOURCE_SERVICE );
2811 if ( xEnum.is() )
2813 while ( xEnum->hasMoreElements() )
2815 uno::Any aAddInAny = xEnum->nextElement();
2816 // if ( aAddInAny.getReflection()->getTypeClass() == TypeClass_INTERFACE )
2818 uno::Reference<uno::XInterface> xIntFac;
2819 aAddInAny >>= xIntFac;
2820 if ( xIntFac.is() )
2822 uno::Reference<lang::XServiceInfo> xInfo( xIntFac, uno::UNO_QUERY );
2823 if ( xInfo.is() )
2825 OUString sName = xInfo->getImplementationName();
2826 aVec.push_back( sName );
2834 return aVec;
2837 uno::Reference<sheet::XDimensionsSupplier> ScDPObject::CreateSource( const ScDPServiceDesc& rDesc )
2839 OUString aImplName = rDesc.aServiceName;
2840 uno::Reference<sheet::XDimensionsSupplier> xRet;
2842 uno::Reference<lang::XMultiServiceFactory> xManager = comphelper::getProcessServiceFactory();
2843 uno::Reference<container::XContentEnumerationAccess> xEnAc(xManager, uno::UNO_QUERY);
2844 if (!xEnAc.is())
2845 return xRet;
2847 uno::Reference<container::XEnumeration> xEnum =
2848 xEnAc->createContentEnumeration(SCDPSOURCE_SERVICE);
2849 if (!xEnum.is())
2850 return xRet;
2852 while (xEnum->hasMoreElements() && !xRet.is())
2854 uno::Any aAddInAny = xEnum->nextElement();
2855 uno::Reference<uno::XInterface> xIntFac;
2856 aAddInAny >>= xIntFac;
2857 if (!xIntFac.is())
2858 continue;
2860 uno::Reference<lang::XServiceInfo> xInfo(xIntFac, uno::UNO_QUERY);
2861 if (!xInfo.is() || xInfo->getImplementationName() != aImplName)
2862 continue;
2866 // #i113160# try XSingleComponentFactory in addition to (old) XSingleServiceFactory,
2867 // passing the context to the component (see ScUnoAddInCollection::Initialize)
2869 uno::Reference<uno::XInterface> xInterface;
2870 uno::Reference<uno::XComponentContext> xCtx(
2871 comphelper::getComponentContext(xManager));
2872 uno::Reference<lang::XSingleComponentFactory> xCFac( xIntFac, uno::UNO_QUERY );
2873 if (xCFac.is())
2874 xInterface = xCFac->createInstanceWithContext(xCtx);
2876 if (!xInterface.is())
2878 uno::Reference<lang::XSingleServiceFactory> xFac( xIntFac, uno::UNO_QUERY );
2879 if ( xFac.is() )
2880 xInterface = xFac->createInstance();
2883 uno::Reference<lang::XInitialization> xInit( xInterface, uno::UNO_QUERY );
2884 if (xInit.is())
2886 // initialize
2887 uno::Sequence<uno::Any> aSeq(4);
2888 uno::Any* pArray = aSeq.getArray();
2889 pArray[0] <<= rDesc.aParSource;
2890 pArray[1] <<= rDesc.aParName;
2891 pArray[2] <<= rDesc.aParUser;
2892 pArray[3] <<= rDesc.aParPass;
2893 xInit->initialize( aSeq );
2895 xRet.set( xInterface, uno::UNO_QUERY );
2897 catch(uno::Exception&)
2902 return xRet;
2905 #if DUMP_PIVOT_TABLE
2907 void ScDPObject::Dump() const
2909 if (mpSaveData)
2910 mpSaveData->Dump();
2912 if (mpTableData)
2913 mpTableData->Dump();
2916 void ScDPObject::DumpCache() const
2918 if (!mpTableData)
2919 return;
2921 const ScDPCache &rCache = mpTableData->GetCacheTable().getCache();
2923 rCache.Dump();
2925 #endif
2927 ScDPCollection::SheetCaches::SheetCaches(ScDocument& rDoc) : mrDoc(rDoc) {}
2929 namespace {
2931 struct FindInvalidRange
2933 bool operator() (const ScRange& r) const
2935 return !r.IsValid();
2939 void setGroupItemsToCache( ScDPCache& rCache, const o3tl::sorted_vector<ScDPObject*>& rRefs )
2941 // Go through all referencing pivot tables, and re-fill the group dimension info.
2942 for (const ScDPObject* pObj : rRefs)
2944 const ScDPSaveData* pSave = pObj->GetSaveData();
2945 if (!pSave)
2946 continue;
2948 const ScDPDimensionSaveData* pGroupDims = pSave->GetExistingDimensionData();
2949 if (!pGroupDims)
2950 continue;
2952 pGroupDims->WriteToCache(rCache);
2958 bool ScDPCollection::SheetCaches::hasCache(const ScRange& rRange) const
2960 RangeIndexType::const_iterator it = std::find(maRanges.begin(), maRanges.end(), rRange);
2961 if (it == maRanges.end())
2962 return false;
2964 // Already cached.
2965 size_t nIndex = std::distance(maRanges.begin(), it);
2966 CachesType::const_iterator const itCache = m_Caches.find(nIndex);
2967 return itCache != m_Caches.end();
2970 const ScDPCache* ScDPCollection::SheetCaches::getCache(const ScRange& rRange, const ScDPDimensionSaveData* pDimData)
2972 RangeIndexType::iterator it = std::find(maRanges.begin(), maRanges.end(), rRange);
2973 if (it != maRanges.end())
2975 // Already cached.
2976 size_t nIndex = std::distance(maRanges.begin(), it);
2977 CachesType::iterator const itCache = m_Caches.find(nIndex);
2978 if (itCache == m_Caches.end())
2980 OSL_FAIL("Cache pool and index pool out-of-sync !!!");
2981 return nullptr;
2984 if (pDimData)
2986 (itCache->second)->ClearGroupFields();
2987 pDimData->WriteToCache(*itCache->second);
2990 return itCache->second.get();
2993 // Not cached. Create a new cache.
2994 ::std::unique_ptr<ScDPCache> pCache(new ScDPCache(mrDoc));
2995 pCache->InitFromDoc(mrDoc, rRange);
2996 if (pDimData)
2997 pDimData->WriteToCache(*pCache);
2999 // Get the smallest available range index.
3000 it = std::find_if(maRanges.begin(), maRanges.end(), FindInvalidRange());
3002 size_t nIndex = maRanges.size();
3003 if (it == maRanges.end())
3005 // All range indices are valid. Append a new index.
3006 maRanges.push_back(rRange);
3008 else
3010 // Slot with invalid range. Re-use this slot.
3011 *it = rRange;
3012 nIndex = std::distance(maRanges.begin(), it);
3015 const ScDPCache* p = pCache.get();
3016 m_Caches.insert(std::make_pair(nIndex, std::move(pCache)));
3017 return p;
3020 ScDPCache* ScDPCollection::SheetCaches::getExistingCache(const ScRange& rRange)
3022 RangeIndexType::iterator it = std::find(maRanges.begin(), maRanges.end(), rRange);
3023 if (it == maRanges.end())
3024 // Not cached.
3025 return nullptr;
3027 // Already cached.
3028 size_t nIndex = std::distance(maRanges.begin(), it);
3029 CachesType::iterator const itCache = m_Caches.find(nIndex);
3030 if (itCache == m_Caches.end())
3032 OSL_FAIL("Cache pool and index pool out-of-sync !!!");
3033 return nullptr;
3036 return itCache->second.get();
3039 const ScDPCache* ScDPCollection::SheetCaches::getExistingCache(const ScRange& rRange) const
3041 RangeIndexType::const_iterator it = std::find(maRanges.begin(), maRanges.end(), rRange);
3042 if (it == maRanges.end())
3043 // Not cached.
3044 return nullptr;
3046 // Already cached.
3047 size_t nIndex = std::distance(maRanges.begin(), it);
3048 CachesType::const_iterator const itCache = m_Caches.find(nIndex);
3049 if (itCache == m_Caches.end())
3051 OSL_FAIL("Cache pool and index pool out-of-sync !!!");
3052 return nullptr;
3055 return itCache->second.get();
3058 size_t ScDPCollection::SheetCaches::size() const
3060 return m_Caches.size();
3063 void ScDPCollection::SheetCaches::updateReference(
3064 UpdateRefMode eMode, const ScRange& r, SCCOL nDx, SCROW nDy, SCTAB nDz)
3066 if (maRanges.empty())
3067 // No caches.
3068 return;
3070 for (ScRange& rKeyRange : maRanges)
3072 SCCOL nCol1 = rKeyRange.aStart.Col();
3073 SCROW nRow1 = rKeyRange.aStart.Row();
3074 SCTAB nTab1 = rKeyRange.aStart.Tab();
3075 SCCOL nCol2 = rKeyRange.aEnd.Col();
3076 SCROW nRow2 = rKeyRange.aEnd.Row();
3077 SCTAB nTab2 = rKeyRange.aEnd.Tab();
3079 ScRefUpdateRes eRes = ScRefUpdate::Update(
3080 &mrDoc, eMode,
3081 r.aStart.Col(), r.aStart.Row(), r.aStart.Tab(),
3082 r.aEnd.Col(), r.aEnd.Row(), r.aEnd.Tab(), nDx, nDy, nDz,
3083 nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
3085 if (eRes != UR_NOTHING)
3087 // range updated.
3088 ScRange aNew(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
3089 rKeyRange = aNew;
3094 void ScDPCollection::SheetCaches::updateCache(const ScRange& rRange, o3tl::sorted_vector<ScDPObject*>& rRefs)
3096 RangeIndexType::iterator it = std::find(maRanges.begin(), maRanges.end(), rRange);
3097 if (it == maRanges.end())
3099 // Not cached. Nothing to do.
3100 rRefs.clear();
3101 return;
3104 size_t nIndex = std::distance(maRanges.begin(), it);
3105 CachesType::iterator const itCache = m_Caches.find(nIndex);
3106 if (itCache == m_Caches.end())
3108 OSL_FAIL("Cache pool and index pool out-of-sync !!!");
3109 rRefs.clear();
3110 return;
3113 ScDPCache& rCache = *itCache->second;
3115 // Update the cache with new cell values. This will clear all group dimension info.
3116 rCache.InitFromDoc(mrDoc, rRange);
3118 o3tl::sorted_vector<ScDPObject*> aRefs(rCache.GetAllReferences());
3119 rRefs.swap(aRefs);
3121 // Make sure to re-populate the group dimension info.
3122 setGroupItemsToCache(rCache, rRefs);
3125 bool ScDPCollection::SheetCaches::remove(const ScDPCache* p)
3127 CachesType::iterator it = std::find_if(m_Caches.begin(), m_Caches.end(),
3128 [&p](const CachesType::value_type& rEntry) { return rEntry.second.get() == p; });
3129 if (it != m_Caches.end())
3131 size_t idx = it->first;
3132 m_Caches.erase(it);
3133 maRanges[idx].SetInvalid();
3134 return true;
3136 return false;
3139 const std::vector<ScRange>& ScDPCollection::SheetCaches::getAllRanges() const
3141 return maRanges;
3144 ScDPCollection::NameCaches::NameCaches(ScDocument& rDoc) : mrDoc(rDoc) {}
3146 bool ScDPCollection::NameCaches::hasCache(const OUString& rName) const
3148 return m_Caches.count(rName) != 0;
3151 const ScDPCache* ScDPCollection::NameCaches::getCache(
3152 const OUString& rName, const ScRange& rRange, const ScDPDimensionSaveData* pDimData)
3154 CachesType::const_iterator const itr = m_Caches.find(rName);
3155 if (itr != m_Caches.end())
3156 // already cached.
3157 return itr->second.get();
3159 ::std::unique_ptr<ScDPCache> pCache(new ScDPCache(mrDoc));
3160 pCache->InitFromDoc(mrDoc, rRange);
3161 if (pDimData)
3162 pDimData->WriteToCache(*pCache);
3164 const ScDPCache *const p = pCache.get();
3165 m_Caches.insert(std::make_pair(rName, std::move(pCache)));
3166 return p;
3169 ScDPCache* ScDPCollection::NameCaches::getExistingCache(const OUString& rName)
3171 CachesType::iterator const itr = m_Caches.find(rName);
3172 return itr != m_Caches.end() ? itr->second.get() : nullptr;
3175 size_t ScDPCollection::NameCaches::size() const
3177 return m_Caches.size();
3180 void ScDPCollection::NameCaches::updateCache(
3181 const OUString& rName, const ScRange& rRange, o3tl::sorted_vector<ScDPObject*>& rRefs)
3183 CachesType::iterator const itr = m_Caches.find(rName);
3184 if (itr == m_Caches.end())
3186 rRefs.clear();
3187 return;
3190 ScDPCache& rCache = *itr->second;
3191 // Update the cache with new cell values. This will clear all group dimension info.
3192 rCache.InitFromDoc(mrDoc, rRange);
3194 o3tl::sorted_vector<ScDPObject*> aRefs(rCache.GetAllReferences());
3195 rRefs.swap(aRefs);
3197 // Make sure to re-populate the group dimension info.
3198 setGroupItemsToCache(rCache, rRefs);
3201 bool ScDPCollection::NameCaches::remove(const ScDPCache* p)
3203 CachesType::iterator it = std::find_if(m_Caches.begin(), m_Caches.end(),
3204 [&p](const CachesType::value_type& rEntry) { return rEntry.second.get() == p; });
3205 if (it != m_Caches.end())
3207 m_Caches.erase(it);
3208 return true;
3210 return false;
3213 ScDPCollection::DBType::DBType(sal_Int32 nSdbType, OUString aDBName, OUString aCommand) :
3214 mnSdbType(nSdbType), maDBName(std::move(aDBName)), maCommand(std::move(aCommand)) {}
3216 bool ScDPCollection::DBType::less::operator() (const DBType& left, const DBType& right) const
3218 return left < right;
3221 ScDPCollection::DBCaches::DBCaches(ScDocument& rDoc) : mrDoc(rDoc) {}
3223 bool ScDPCollection::DBCaches::hasCache(sal_Int32 nSdbType, const OUString& rDBName, const OUString& rCommand) const
3225 DBType aType(nSdbType, rDBName, rCommand);
3226 CachesType::const_iterator const itr = m_Caches.find(aType);
3227 return itr != m_Caches.end();
3230 const ScDPCache* ScDPCollection::DBCaches::getCache(
3231 sal_Int32 nSdbType, const OUString& rDBName, const OUString& rCommand,
3232 const ScDPDimensionSaveData* pDimData)
3234 DBType aType(nSdbType, rDBName, rCommand);
3235 CachesType::const_iterator const itr = m_Caches.find(aType);
3236 if (itr != m_Caches.end())
3237 // already cached.
3238 return itr->second.get();
3240 uno::Reference<sdbc::XRowSet> xRowSet = createRowSet(nSdbType, rDBName, rCommand);
3241 if (!xRowSet.is())
3242 return nullptr;
3244 ::std::unique_ptr<ScDPCache> pCache(new ScDPCache(mrDoc));
3245 SvNumberFormatter aFormat( comphelper::getProcessComponentContext(), ScGlobal::eLnge);
3246 DBConnector aDB(*pCache, xRowSet, aFormat.GetNullDate());
3247 if (!aDB.isValid())
3248 return nullptr;
3250 if (!pCache->InitFromDataBase(aDB))
3252 // initialization failed.
3253 comphelper::disposeComponent(xRowSet);
3254 return nullptr;
3257 if (pDimData)
3258 pDimData->WriteToCache(*pCache);
3260 ::comphelper::disposeComponent(xRowSet);
3261 const ScDPCache* p = pCache.get();
3262 m_Caches.insert(std::make_pair(aType, std::move(pCache)));
3263 return p;
3266 ScDPCache* ScDPCollection::DBCaches::getExistingCache(
3267 sal_Int32 nSdbType, const OUString& rDBName, const OUString& rCommand)
3269 DBType aType(nSdbType, rDBName, rCommand);
3270 CachesType::iterator const itr = m_Caches.find(aType);
3271 return itr != m_Caches.end() ? itr->second.get() : nullptr;
3274 uno::Reference<sdbc::XRowSet> ScDPCollection::DBCaches::createRowSet(
3275 sal_Int32 nSdbType, const OUString& rDBName, const OUString& rCommand)
3277 uno::Reference<sdbc::XRowSet> xRowSet;
3280 xRowSet.set(comphelper::getProcessServiceFactory()->createInstance(
3281 SC_SERVICE_ROWSET),
3282 UNO_QUERY);
3284 uno::Reference<beans::XPropertySet> xRowProp(xRowSet, UNO_QUERY);
3285 OSL_ENSURE( xRowProp.is(), "can't get RowSet" );
3286 if (!xRowProp.is())
3288 xRowSet.clear();
3289 return xRowSet;
3292 // set source parameters
3294 xRowProp->setPropertyValue( SC_DBPROP_DATASOURCENAME, Any(rDBName) );
3295 xRowProp->setPropertyValue( SC_DBPROP_COMMAND, Any(rCommand) );
3296 xRowProp->setPropertyValue( SC_DBPROP_COMMANDTYPE, Any(nSdbType) );
3298 uno::Reference<sdb::XCompletedExecution> xExecute( xRowSet, uno::UNO_QUERY );
3299 if ( xExecute.is() )
3301 uno::Reference<task::XInteractionHandler> xHandler(
3302 task::InteractionHandler::createWithParent(comphelper::getProcessComponentContext(), nullptr),
3303 uno::UNO_QUERY_THROW);
3304 xExecute->executeWithCompletion( xHandler );
3306 else
3307 xRowSet->execute();
3309 return xRowSet;
3311 catch ( const sdbc::SQLException& rError )
3313 //! store error message
3314 std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(),
3315 VclMessageType::Info, VclButtonsType::Ok,
3316 rError.Message));
3317 xInfoBox->run();
3319 catch ( uno::Exception& )
3321 TOOLS_WARN_EXCEPTION( "sc", "Unexpected exception in database");
3324 xRowSet.clear();
3325 return xRowSet;
3328 void ScDPCollection::DBCaches::updateCache(
3329 sal_Int32 nSdbType, const OUString& rDBName, const OUString& rCommand,
3330 o3tl::sorted_vector<ScDPObject*>& rRefs)
3332 DBType aType(nSdbType, rDBName, rCommand);
3333 CachesType::iterator const it = m_Caches.find(aType);
3334 if (it == m_Caches.end())
3336 // not cached.
3337 rRefs.clear();
3338 return;
3341 ScDPCache& rCache = *it->second;
3343 uno::Reference<sdbc::XRowSet> xRowSet = createRowSet(nSdbType, rDBName, rCommand);
3344 if (!xRowSet.is())
3346 rRefs.clear();
3347 return;
3350 SvNumberFormatter aFormat( comphelper::getProcessComponentContext(), ScGlobal::eLnge);
3351 DBConnector aDB(rCache, xRowSet, aFormat.GetNullDate());
3352 if (!aDB.isValid())
3353 return;
3355 if (!rCache.InitFromDataBase(aDB))
3357 // initialization failed.
3358 rRefs.clear();
3359 comphelper::disposeComponent(xRowSet);
3360 return;
3363 comphelper::disposeComponent(xRowSet);
3364 o3tl::sorted_vector<ScDPObject*> aRefs(rCache.GetAllReferences());
3365 aRefs.swap(rRefs);
3367 // Make sure to re-populate the group dimension info.
3368 setGroupItemsToCache(rCache, rRefs);
3371 bool ScDPCollection::DBCaches::remove(const ScDPCache* p)
3373 CachesType::iterator it = std::find_if(m_Caches.begin(), m_Caches.end(),
3374 [&p](const CachesType::value_type& rEntry) { return rEntry.second.get() == p; });
3375 if (it != m_Caches.end())
3377 m_Caches.erase(it);
3378 return true;
3380 return false;
3383 ScDPCollection::ScDPCollection(ScDocument& rDocument) :
3384 mrDoc(rDocument),
3385 maSheetCaches(rDocument),
3386 maNameCaches(rDocument),
3387 maDBCaches(rDocument)
3391 ScDPCollection::ScDPCollection(const ScDPCollection& r) :
3392 mrDoc(r.mrDoc),
3393 maSheetCaches(r.mrDoc),
3394 maNameCaches(r.mrDoc),
3395 maDBCaches(r.mrDoc)
3399 ScDPCollection::~ScDPCollection()
3401 maTables.clear();
3404 namespace {
3407 * Unary predicate to match DP objects by the table ID.
3409 class MatchByTable
3411 SCTAB mnTab;
3412 public:
3413 explicit MatchByTable(SCTAB nTab) : mnTab(nTab) {}
3415 bool operator() (const std::unique_ptr<ScDPObject>& rObj) const
3417 return rObj->GetOutRange().aStart.Tab() == mnTab;
3423 TranslateId ScDPCollection::ReloadCache(const ScDPObject* pDPObj, o3tl::sorted_vector<ScDPObject*>& rRefs)
3425 if (!pDPObj)
3426 return STR_ERR_DATAPILOTSOURCE;
3428 if (pDPObj->IsSheetData())
3430 // data source is internal sheet.
3431 const ScSheetSourceDesc* pDesc = pDPObj->GetSheetDesc();
3432 if (!pDesc)
3433 return STR_ERR_DATAPILOTSOURCE;
3435 TranslateId pErrId = pDesc->CheckSourceRange();
3436 if (pErrId)
3437 return pErrId;
3439 if (pDesc->HasRangeName())
3441 // cache by named range
3442 ScDPCollection::NameCaches& rCaches = GetNameCaches();
3443 if (rCaches.hasCache(pDesc->GetRangeName()))
3444 rCaches.updateCache(pDesc->GetRangeName(), pDesc->GetSourceRange(), rRefs);
3445 else
3447 // Not cached yet. Collect all tables that use this named
3448 // range as data source.
3449 GetAllTables(pDesc->GetRangeName(), rRefs);
3452 else
3454 // cache by cell range
3455 ScDPCollection::SheetCaches& rCaches = GetSheetCaches();
3456 if (rCaches.hasCache(pDesc->GetSourceRange()))
3457 rCaches.updateCache(pDesc->GetSourceRange(), rRefs);
3458 else
3460 // Not cached yet. Collect all tables that use this range as
3461 // data source.
3462 GetAllTables(pDesc->GetSourceRange(), rRefs);
3466 else if (pDPObj->IsImportData())
3468 // data source is external database.
3469 const ScImportSourceDesc* pDesc = pDPObj->GetImportSourceDesc();
3470 if (!pDesc)
3471 return STR_ERR_DATAPILOTSOURCE;
3473 ScDPCollection::DBCaches& rCaches = GetDBCaches();
3474 if (rCaches.hasCache(pDesc->GetCommandType(), pDesc->aDBName, pDesc->aObject))
3475 rCaches.updateCache(
3476 pDesc->GetCommandType(), pDesc->aDBName, pDesc->aObject, rRefs);
3477 else
3479 // Not cached yet. Collect all tables that use this range as
3480 // data source.
3481 GetAllTables(pDesc->GetCommandType(), pDesc->aDBName, pDesc->aObject, rRefs);
3484 return {};
3487 bool ScDPCollection::ReloadGroupsInCache(const ScDPObject* pDPObj, o3tl::sorted_vector<ScDPObject*>& rRefs)
3489 if (!pDPObj)
3490 return false;
3492 const ScDPSaveData* pSaveData = pDPObj->GetSaveData();
3493 if (!pSaveData)
3494 return false;
3496 // Note: Unlike reloading cache, when modifying the group dimensions the
3497 // cache may not have all its references when this method is called.
3498 // Therefore, we need to always call GetAllTables to get its correct
3499 // references even when the cache exists. This may become a non-issue
3500 // if/when we implement loading and saving of pivot caches.
3502 ScDPCache* pCache = nullptr;
3504 if (pDPObj->IsSheetData())
3506 // data source is internal sheet.
3507 const ScSheetSourceDesc* pDesc = pDPObj->GetSheetDesc();
3508 if (!pDesc)
3509 return false;
3511 if (pDesc->HasRangeName())
3513 // cache by named range
3514 ScDPCollection::NameCaches& rCaches = GetNameCaches();
3515 if (rCaches.hasCache(pDesc->GetRangeName()))
3516 pCache = rCaches.getExistingCache(pDesc->GetRangeName());
3517 else
3519 // Not cached yet. Cache the source dimensions. Groups will
3520 // be added below.
3521 pCache = const_cast<ScDPCache*>(
3522 rCaches.getCache(pDesc->GetRangeName(), pDesc->GetSourceRange(), nullptr));
3524 GetAllTables(pDesc->GetRangeName(), rRefs);
3526 else
3528 // cache by cell range
3529 ScDPCollection::SheetCaches& rCaches = GetSheetCaches();
3530 if (rCaches.hasCache(pDesc->GetSourceRange()))
3531 pCache = rCaches.getExistingCache(pDesc->GetSourceRange());
3532 else
3534 // Not cached yet. Cache the source dimensions. Groups will
3535 // be added below.
3536 pCache = const_cast<ScDPCache*>(
3537 rCaches.getCache(pDesc->GetSourceRange(), nullptr));
3539 GetAllTables(pDesc->GetSourceRange(), rRefs);
3542 else if (pDPObj->IsImportData())
3544 // data source is external database.
3545 const ScImportSourceDesc* pDesc = pDPObj->GetImportSourceDesc();
3546 if (!pDesc)
3547 return false;
3549 ScDPCollection::DBCaches& rCaches = GetDBCaches();
3550 if (rCaches.hasCache(pDesc->GetCommandType(), pDesc->aDBName, pDesc->aObject))
3551 pCache = rCaches.getExistingCache(
3552 pDesc->GetCommandType(), pDesc->aDBName, pDesc->aObject);
3553 else
3555 // Not cached yet. Cache the source dimensions. Groups will
3556 // be added below.
3557 pCache = const_cast<ScDPCache*>(
3558 rCaches.getCache(pDesc->GetCommandType(), pDesc->aDBName, pDesc->aObject, nullptr));
3560 GetAllTables(pDesc->GetCommandType(), pDesc->aDBName, pDesc->aObject, rRefs);
3563 if (!pCache)
3564 return false;
3566 // Clear the existing group/field data from the cache, and rebuild it from the
3567 // dimension data.
3568 pCache->ClearAllFields();
3569 const ScDPDimensionSaveData* pDimData = pSaveData->GetExistingDimensionData();
3570 if (pDimData)
3571 pDimData->WriteToCache(*pCache);
3572 return true;
3575 bool ScDPCollection::GetReferenceGroups(const ScDPObject& rDPObj, const ScDPDimensionSaveData** pGroups) const
3577 for (const std::unique_ptr<ScDPObject>& aTable : maTables)
3579 const ScDPObject& rRefObj = *aTable;
3581 if (&rRefObj == &rDPObj)
3582 continue;
3584 if (rDPObj.IsSheetData()){
3585 if(!rRefObj.IsSheetData())
3586 continue;
3588 const ScSheetSourceDesc* pDesc = rDPObj.GetSheetDesc();
3589 const ScSheetSourceDesc* pRefDesc = rRefObj.GetSheetDesc();
3590 if (pDesc == nullptr || pRefDesc == nullptr)
3591 continue;
3593 if (pDesc->HasRangeName())
3595 if (!pRefDesc->HasRangeName())
3596 continue;
3598 if (pDesc->GetRangeName() == pRefDesc->GetRangeName())
3600 *pGroups = rRefObj.GetSaveData()->GetExistingDimensionData();
3601 return true;
3604 else
3606 if (pRefDesc->HasRangeName())
3607 continue;
3609 if (pDesc->GetSourceRange() == pRefDesc->GetSourceRange())
3611 *pGroups = rRefObj.GetSaveData()->GetExistingDimensionData();
3612 return true;
3616 else if (rDPObj.IsImportData())
3618 if (!rRefObj.IsImportData ())
3619 continue;
3621 const ScImportSourceDesc* pDesc = rDPObj.GetImportSourceDesc();
3622 const ScImportSourceDesc* pRefDesc = rRefObj.GetImportSourceDesc();
3623 if (pDesc == nullptr || pRefDesc == nullptr)
3624 continue;
3626 if (pDesc->aDBName == pRefDesc->aDBName &&
3627 pDesc->aObject == pRefDesc->aObject &&
3628 pDesc->GetCommandType() == pRefDesc->GetCommandType())
3630 *pGroups = rRefObj.GetSaveData()->GetExistingDimensionData();
3631 return true;
3636 return false;
3640 void ScDPCollection::DeleteOnTab( SCTAB nTab )
3642 std::erase_if(maTables, MatchByTable(nTab));
3645 void ScDPCollection::UpdateReference( UpdateRefMode eUpdateRefMode,
3646 const ScRange& r, SCCOL nDx, SCROW nDy, SCTAB nDz )
3648 for (auto& rxTable : maTables)
3649 rxTable->UpdateReference(eUpdateRefMode, r, nDx, nDy, nDz);
3651 // Update the source ranges of the caches.
3652 maSheetCaches.updateReference(eUpdateRefMode, r, nDx, nDy, nDz);
3655 void ScDPCollection::CopyToTab( SCTAB nOld, SCTAB nNew )
3657 TablesType aAdded;
3658 for (const auto& rxTable : maTables)
3660 const ScDPObject& rObj = *rxTable;
3661 ScRange maOutputRange = rObj.GetOutRange();
3662 if (maOutputRange.aStart.Tab() != nOld)
3663 continue;
3665 ScAddress& start = maOutputRange.aStart;
3666 ScAddress& end = maOutputRange.aEnd;
3667 start.SetTab(nNew);
3668 end.SetTab(nNew);
3669 ScDPObject* pNew = new ScDPObject(rObj);
3670 pNew->SetOutRange(maOutputRange);
3671 mrDoc.ApplyFlagsTab(start.Col(), start.Row(), end.Col(), end.Row(), start.Tab(), ScMF::DpTable);
3672 aAdded.push_back(std::unique_ptr<ScDPObject>(pNew));
3675 std::move(aAdded.begin(), aAdded.end(), std::back_inserter(maTables));
3678 bool ScDPCollection::RefsEqual( const ScDPCollection& r ) const
3680 return std::equal(maTables.begin(), maTables.end(), r.maTables.begin(), r.maTables.end(),
3681 [](const TablesType::value_type& a, const TablesType::value_type& b) { return a->RefsEqual(*b); });
3684 void ScDPCollection::WriteRefsTo( ScDPCollection& r ) const
3686 if ( maTables.size() == r.maTables.size() )
3688 //TODO: assert equal names?
3689 TablesType::iterator itr2 = r.maTables.begin();
3690 for (const auto& rxTable : maTables)
3692 rxTable->WriteRefsTo(**itr2);
3693 ++itr2;
3696 else
3698 // #i8180# If data pilot tables were deleted with their sheet,
3699 // this collection contains extra entries that must be restored.
3700 // Matching objects are found by their names.
3701 size_t nSrcSize = maTables.size();
3702 size_t nDestSize = r.maTables.size();
3703 OSL_ENSURE( nSrcSize >= nDestSize, "WriteRefsTo: missing entries in document" );
3704 for (size_t nSrcPos = 0; nSrcPos < nSrcSize; ++nSrcPos)
3706 const ScDPObject& rSrcObj = *maTables[nSrcPos];
3707 const OUString& aName = rSrcObj.GetName();
3708 bool bFound = false;
3709 for (size_t nDestPos = 0; nDestPos < nDestSize && !bFound; ++nDestPos)
3711 ScDPObject& rDestObj = *r.maTables[nDestPos];
3712 if (rDestObj.GetName() == aName)
3714 rSrcObj.WriteRefsTo(rDestObj); // found object, copy refs
3715 bFound = true;
3719 if (!bFound)
3721 // none found, re-insert deleted object (see ScUndoDataPilot::Undo)
3722 r.InsertNewTable(std::make_unique<ScDPObject>(rSrcObj));
3725 OSL_ENSURE( maTables.size() == r.maTables.size(), "WriteRefsTo: couldn't restore all entries" );
3729 size_t ScDPCollection::GetCount() const
3731 return maTables.size();
3734 ScDPObject& ScDPCollection::operator [](size_t nIndex)
3736 return *maTables[nIndex];
3739 const ScDPObject& ScDPCollection::operator [](size_t nIndex) const
3741 return *maTables[nIndex];
3744 ScDPObject* ScDPCollection::GetByName(std::u16string_view rName) const
3746 for (std::unique_ptr<ScDPObject> const & pObject : maTables)
3748 if (pObject->GetName() == rName)
3749 return pObject.get();
3752 return nullptr;
3755 OUString ScDPCollection::CreateNewName() const
3757 size_t n = maTables.size();
3758 for (size_t nAdd = 0; nAdd <= n; ++nAdd) // nCount+1 tries
3760 OUString aNewName = "DataPilot" + OUString::number(1 + nAdd);
3761 if (std::none_of(maTables.begin(), maTables.end(),
3762 [&aNewName](const TablesType::value_type& rxObj) { return rxObj->GetName() == aNewName; }))
3763 return aNewName; // found unused Name
3765 return OUString(); // should not happen
3768 void ScDPCollection::FreeTable(const ScDPObject* pDPObject)
3770 const ScRange& rOutRange = pDPObject->GetOutRange();
3771 const ScAddress& s = rOutRange.aStart;
3772 const ScAddress& e = rOutRange.aEnd;
3773 mrDoc.RemoveFlagsTab(s.Col(), s.Row(), e.Col(), e.Row(), s.Tab(), ScMF::DpTable);
3775 auto funcRemoveCondition = [pDPObject] (std::unique_ptr<ScDPObject> const & pCurrent)
3777 return pCurrent.get() == pDPObject;
3780 std::erase_if(maTables, funcRemoveCondition);
3783 ScDPObject* ScDPCollection::InsertNewTable(std::unique_ptr<ScDPObject> pDPObj)
3785 const ScRange& rOutRange = pDPObj->GetOutRange();
3786 const ScAddress& s = rOutRange.aStart;
3787 const ScAddress& e = rOutRange.aEnd;
3788 mrDoc.ApplyFlagsTab(s.Col(), s.Row(), e.Col(), e.Row(), s.Tab(), ScMF::DpTable);
3790 maTables.push_back(std::move(pDPObj));
3791 return maTables.back().get();
3794 bool ScDPCollection::HasTable(const ScDPObject* pDPObj) const
3796 for (const std::unique_ptr<ScDPObject>& aTable : maTables)
3798 if (aTable.get() == pDPObj)
3800 return true;
3803 return false;
3806 ScDPCollection::SheetCaches& ScDPCollection::GetSheetCaches()
3808 return maSheetCaches;
3811 const ScDPCollection::SheetCaches& ScDPCollection::GetSheetCaches() const
3813 return maSheetCaches;
3816 ScDPCollection::NameCaches& ScDPCollection::GetNameCaches()
3818 return maNameCaches;
3821 const ScDPCollection::NameCaches& ScDPCollection::GetNameCaches() const
3823 return maNameCaches;
3826 ScDPCollection::DBCaches& ScDPCollection::GetDBCaches()
3828 return maDBCaches;
3831 const ScDPCollection::DBCaches& ScDPCollection::GetDBCaches() const
3833 return maDBCaches;
3836 ScRangeList ScDPCollection::GetAllTableRanges( SCTAB nTab ) const
3838 return std::for_each(maTables.begin(), maTables.end(), AccumulateOutputRanges(nTab)).getRanges();
3841 bool ScDPCollection::IntersectsTableByColumns( SCCOL nCol1, SCCOL nCol2, SCROW nRow, SCTAB nTab ) const
3843 return std::any_of(maTables.begin(), maTables.end(), FindIntersectingTableByColumns(nCol1, nCol2, nRow, nTab));
3846 bool ScDPCollection::IntersectsTableByRows( SCCOL nCol, SCROW nRow1, SCROW nRow2, SCTAB nTab ) const
3848 return std::any_of(maTables.begin(), maTables.end(), FindIntersectingTableByRows(nCol, nRow1, nRow2, nTab));
3851 bool ScDPCollection::HasTable( const ScRange& rRange ) const
3853 return std::any_of(maTables.begin(), maTables.end(), FindIntersectingTable(rRange));
3856 #if DEBUG_PIVOT_TABLE
3858 namespace {
3860 struct DumpTable
3862 void operator() (const std::unique_ptr<ScDPObject>& rObj) const
3864 cout << "-- '" << rObj->GetName() << "'" << endl;
3865 ScDPSaveData* pSaveData = rObj->GetSaveData();
3866 if (!pSaveData)
3867 return;
3869 pSaveData->Dump();
3871 cout << endl; // blank line
3877 void ScDPCollection::DumpTables() const
3879 std::for_each(maTables.begin(), maTables.end(), DumpTable());
3882 #endif
3884 void ScDPCollection::RemoveCache(const ScDPCache* pCache)
3886 if (maSheetCaches.remove(pCache))
3887 // sheet cache removed.
3888 return;
3890 if (maNameCaches.remove(pCache))
3891 // named range cache removed.
3892 return;
3894 if (maDBCaches.remove(pCache))
3895 // database cache removed.
3896 return;
3899 void ScDPCollection::GetAllTables(const ScRange& rSrcRange, o3tl::sorted_vector<ScDPObject*>& rRefs) const
3901 o3tl::sorted_vector<ScDPObject*> aRefs;
3902 for (const auto& rxTable : maTables)
3904 const ScDPObject& rObj = *rxTable;
3905 if (!rObj.IsSheetData())
3906 // Source is not a sheet range.
3907 continue;
3909 const ScSheetSourceDesc* pDesc = rObj.GetSheetDesc();
3910 if (!pDesc)
3911 continue;
3913 if (pDesc->HasRangeName())
3914 // This table has a range name as its source.
3915 continue;
3917 if (pDesc->GetSourceRange() != rSrcRange)
3918 // Different source range.
3919 continue;
3921 aRefs.insert(const_cast<ScDPObject*>(&rObj));
3924 rRefs.swap(aRefs);
3927 void ScDPCollection::GetAllTables(std::u16string_view rSrcName, o3tl::sorted_vector<ScDPObject*>& rRefs) const
3929 o3tl::sorted_vector<ScDPObject*> aRefs;
3930 for (const auto& rxTable : maTables)
3932 const ScDPObject& rObj = *rxTable;
3933 if (!rObj.IsSheetData())
3934 // Source is not a sheet range.
3935 continue;
3937 const ScSheetSourceDesc* pDesc = rObj.GetSheetDesc();
3938 if (!pDesc)
3939 continue;
3941 if (!pDesc->HasRangeName())
3942 // This table probably has a sheet range as its source.
3943 continue;
3945 if (pDesc->GetRangeName() != rSrcName)
3946 // Different source name.
3947 continue;
3949 aRefs.insert(const_cast<ScDPObject*>(&rObj));
3952 rRefs.swap(aRefs);
3955 void ScDPCollection::GetAllTables(
3956 sal_Int32 nSdbType, std::u16string_view rDBName, std::u16string_view rCommand,
3957 o3tl::sorted_vector<ScDPObject*>& rRefs) const
3959 o3tl::sorted_vector<ScDPObject*> aRefs;
3960 for (const auto& rxTable : maTables)
3962 const ScDPObject& rObj = *rxTable;
3963 if (!rObj.IsImportData())
3964 // Source data is not a database.
3965 continue;
3967 const ScImportSourceDesc* pDesc = rObj.GetImportSourceDesc();
3968 if (!pDesc)
3969 continue;
3971 if (pDesc->aDBName != rDBName || pDesc->aObject != rCommand || pDesc->GetCommandType() != nSdbType)
3972 // Different database source.
3973 continue;
3975 aRefs.insert(const_cast<ScDPObject*>(&rObj));
3978 rRefs.swap(aRefs);
3981 bool operator<(const ScDPCollection::DBType& left, const ScDPCollection::DBType& right)
3983 if (left.mnSdbType != right.mnSdbType)
3984 return left.mnSdbType < right.mnSdbType;
3986 if (left.maDBName != right.maDBName)
3987 return left.maDBName < right.maDBName;
3989 return left.maCommand < right.maCommand;
3992 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */