Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/sc/source/core/data/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 130 kB image not shown  

Quelle  dpobject.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */


#include <docsh.hxx>
#include <dpcache.hxx>
#include <dpobject.hxx>
#include <dptabsrc.hxx>
#include <dpsave.hxx>
#include <dpdimsave.hxx>
#include <dpoutput.hxx>
#include <dpshttab.hxx>
#include <dpsdbtab.hxx>
#include <dpgroup.hxx>
#include <document.hxx>
#include <pivot.hxx>
#include <dapiuno.hxx>
#include <miscuno.hxx>
#include <refupdat.hxx>
#include <attrib.hxx>
#include <scitems.hxx>
#include <unonames.hxx>
#include <dpglobal.hxx>
#include <globstr.hrc>
#include <queryentry.hxx>
#include <dputil.hxx>

#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/sdb/XCompletedExecution.hpp>
#include <com/sun/star/sdbc/DataType.hpp>
#include <com/sun/star/sdbc/SQLException.hpp>
#include <com/sun/star/sdbc/XResultSetMetaData.hpp>
#include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp>
#include <com/sun/star/sdbc/XRow.hpp>
#include <com/sun/star/sdbc/XRowSet.hpp>
#include <com/sun/star/sheet/GeneralFunction2.hpp>
#include <com/sun/star/sheet/DataPilotFieldFilter.hpp>
#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
#include <com/sun/star/sheet/DataPilotFieldReferenceType.hpp>
#include <com/sun/star/sheet/DataPilotTableHeaderData.hpp>
#include <com/sun/star/sheet/DataPilotTablePositionData.hpp>
#include <com/sun/star/sheet/DataPilotTablePositionType.hpp>
#include <com/sun/star/sheet/DimensionFlags.hpp>
#include <com/sun/star/task/InteractionHandler.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/lang/XSingleServiceFactory.hpp>
#include <com/sun/star/lang/XSingleComponentFactory.hpp>
#include <com/sun/star/lang/XInitialization.hpp>
#include <com/sun/star/container/XContentEnumerationAccess.hpp>
#include <com/sun/star/sheet/XDrillDownDataSupplier.hpp>

#include <unotools/charclass.hxx>
#include <comphelper/processfactory.hxx>
#include <comphelper/string.hxx>
#include <comphelper/types.hxx>
#include <o3tl/safeint.hxx>
#include <sal/macros.h>
#include <svl/numformat.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <svl/zforlist.hxx>
#include <utility>
#include <vcl/svapp.hxx>
#include <vcl/weld.hxx>

#include <vector>
#include <memory>
#include <algorithm>

using namespace com::sun::star;
using ::std::vector;
using ::std::shared_ptr;
using ::com::sun::star::uno::Sequence;
using ::com::sun::star::uno::Reference;
using ::com::sun::star::uno::UNO_QUERY;
using ::com::sun::star::uno::Any;
using ::com::sun::star::uno::Exception;
using ::com::sun::star::lang::XComponent;
using ::com::sun::star::sheet::DataPilotTableHeaderData;
using ::com::sun::star::sheet::DataPilotTablePositionData;
using ::com::sun::star::sheet::XDimensionsSupplier;
using ::com::sun::star::beans::XPropertySet;

constexpr OUStringLiteral SC_SERVICE_ROWSET = u"com.sun.star.sdb.RowSet";

constexpr OUStringLiteral SC_DBPROP_DATASOURCENAME = u"DataSourceName";
constexpr OUStringLiteral SC_DBPROP_COMMAND = u"Command";
constexpr OUStringLiteral SC_DBPROP_COMMANDTYPE = u"CommandType";

constexpr OUString SCDPSOURCE_SERVICE = u"com.sun.star.sheet.DataPilotSource"_ustr;

namespace {

/**
 * Database connection implementation for UNO database API.  Note that in
 * the UNO database API, column index is 1-based, whereas the interface
 * requires that column index be 0-based.
 */

class DBConnector : public ScDPCache::DBConnector
{
    ScDPCache& mrCache;

    uno::Reference<sdbc::XRowSet> mxRowSet;
    uno::Reference<sdbc::XRow> mxRow;
    uno::Reference<sdbc::XResultSetMetaData> mxMetaData;
    Date maNullDate;

public:
    DBConnector(ScDPCache& rCache, uno::Reference<sdbc::XRowSet> xRowSet, const Date&&nbsp;rNullDate);

    bool isValid() const;

    virtual void getValue(tools::Long nCol, ScDPItemData &rData, SvNumFormatType& rNumType) const override;
    virtual OUString getColumnLabel(tools::Long nCol) const override;
    virtual tools::Long getColumnCount() const override;
    virtual bool first() override;
    virtual bool next() override;
    virtual void finish() override;
};

DBConnector::DBConnector(ScDPCache& rCache, uno::Reference<sdbc::XRowSet> xRowSetconst Date& rNullDate) :
    mrCache(rCache), mxRowSet(std::move(xRowSet)), maNullDate(rNullDate)
{
    Reference<sdbc::XResultSetMetaDataSupplier> xMetaSupp(mxRowSet, UNO_QUERY);
    if (xMetaSupp.is())
        mxMetaData = xMetaSupp->getMetaData();

    mxRow.set(mxRowSet, UNO_QUERY);
}

bool DBConnector::isValid() const
{
    return mxRowSet.is() && mxRow.is() && mxMetaData.is();
}

bool DBConnector::first()
{
    return mxRowSet->first();
}

bool DBConnector::next()
{
    return mxRowSet->next();
}

void DBConnector::finish()
{
    mxRowSet->beforeFirst();
}

tools::Long DBConnector::getColumnCount() const
{
    return mxMetaData->getColumnCount();
}

OUString DBConnector::getColumnLabel(tools::Long nCol) const
{
    return mxMetaData->getColumnLabel(nCol+1);
}

void DBConnector::getValue(tools::Long nCol, ScDPItemData &rData, SvNumFormatType& rNumType) const
{
    rNumType = SvNumFormatType::NUMBER;
    sal_Int32 nType = mxMetaData->getColumnType(nCol+1);

    try
    {
        double fValue = 0.0;
        switch (nType)
        {
            case sdbc::DataType::BIT:
            case sdbc::DataType::BOOLEAN:
            {
                rNumType = SvNumFormatType::LOGICAL;
                fValue  = mxRow->getBoolean(nCol+1) ? 1 : 0;
                rData.SetValue(fValue);
                break;
            }
            case sdbc::DataType::TINYINT:
            case sdbc::DataType::SMALLINT:
            case sdbc::DataType::INTEGER:
            case sdbc::DataType::BIGINT:
            case sdbc::DataType::FLOAT:
            case sdbc::DataType::REAL:
            case sdbc::DataType::DOUBLE:
            case sdbc::DataType::NUMERIC:
            case sdbc::DataType::DECIMAL:
            {
                //TODO: do the conversion here?
                fValue = mxRow->getDouble(nCol+1);
                rData.SetValue(fValue);
                break;
            }
            case sdbc::DataType::DATE:
            {
                rNumType = SvNumFormatType::DATE;

                util::Date aDate = mxRow->getDate(nCol+1);
                fValue = Date(aDate.Day, aDate.Month, aDate.Year) - maNullDate;
                rData.SetValue(fValue);
                break;
            }
            case sdbc::DataType::TIME:
            {
                rNumType = SvNumFormatType::TIME;

                util::Time aTime = mxRow->getTime(nCol+1);
                fValue = aTime.Hours       / static_cast<double>(::tools::Time::hourPerDay)   +
                         aTime.Minutes     / static_cast<double>(::tools::Time::minutePerDay) +
                         aTime.Seconds     / static_cast<double>(::tools::Time::secondPerDay) +
                         aTime.NanoSeconds / static_cast<double>(::tools::Time::nanoSecPerDay);
                rData.SetValue(fValue);
                break;
            }
            case sdbc::DataType::TIMESTAMP:
            {
                rNumType = SvNumFormatType::DATETIME;

                util::DateTime aStamp = mxRow->getTimestamp(nCol+1);
                fValue = ( Date( aStamp.Day, aStamp.Month, aStamp.Year ) - maNullDate ) +
                         aStamp.Hours       / static_cast<double>(::tools::Time::hourPerDay)   +
                         aStamp.Minutes     / static_cast<double>(::tools::Time::minutePerDay) +
                         aStamp.Seconds     / static_cast<double>(::tools::Time::secondPerDay) +
                         aStamp.NanoSeconds / static_cast<double>(::tools::Time::nanoSecPerDay);
                rData.SetValue(fValue);
                break;
            }
            case sdbc::DataType::CHAR:
            case sdbc::DataType::VARCHAR:
            case sdbc::DataType::LONGVARCHAR:
            case sdbc::DataType::SQLNULL:
            case sdbc::DataType::BINARY:
            case sdbc::DataType::VARBINARY:
            case sdbc::DataType::LONGVARBINARY:
            default:
                // nCol is 0-based, and the left-most column always has nCol == 0.
                rData.SetStringInterned(
                    mrCache.InternString(nCol, mxRow->getString(nCol+1)));
        }
    }
    catch (uno::Exception&)
    {
        rData.SetEmpty();
    }
}

}

static sheet::DataPilotFieldOrientation lcl_GetDataGetOrientation( const uno::Reference<sheet::XDimensionsSupplier>& xSource )
{
    sheet::DataPilotFieldOrientation nRet = sheet::DataPilotFieldOrientation_HIDDEN;
    if ( xSource.is() )
    {
        uno::Reference<container::XNameAccess> xDimNameAccess = xSource->getDimensions();
        const uno::Sequence<OUString> aDimNames = xDimNameAccess->getElementNames();
        for (const OUString& rDimName : aDimNames)
        {
            uno::Reference<beans::XPropertySet> xDimProp(xDimNameAccess->getByName(rDimName),
                                                         uno::UNO_QUERY);
            if ( xDimProp.is() )
            {
                const bool bFound = ScUnoHelpFunctions::GetBoolProperty( xDimProp,
                    SC_UNO_DP_ISDATALAYOUT );
                //TODO: error checking -- is "IsDataLayoutDimension" property required??
                if (bFound)
                {
                    nRet = ScUnoHelpFunctions::GetEnumProperty(
                            xDimProp, SC_UNO_DP_ORIENTATION,
                            sheet::DataPilotFieldOrientation_HIDDEN );
                    break;
                }
            }
        }
    }
    return nRet;
}

ScDPServiceDesc::ScDPServiceDesc(
    OUString aServ, OUString aSrc, OUString aNam,
    OUString aUser, OUString aPass ) :
    aServiceName(std::move( aServ )),
    aParSource(std::move( aSrc )),
    aParName(std::move( aNam )),
    aParUser(std::move( aUser )),
    aParPass(std::move( aPass )) {}

bool ScDPServiceDesc::operator== ( const ScDPServiceDesc& rOther ) const
{
    return aServiceName == rOther.aServiceName &&
        aParSource == rOther.aParSource &&
        aParName == rOther.aParName &&
        aParUser == rOther.aParUser &&
        aParPass == rOther.aParPass;
}

ScDPObject::ScDPObject(ScDocument* pDocument)
    : mpDocument(pDocument)
    , mnHeaderRows(0)
    , mbHeaderLayout(false)
    , mbAllowMove(false)
    , mbSettingsChanged(false)
    , mbEnableGetPivotData(true)
    , mbHideHeader(false)
{
}

ScDPObject::ScDPObject(const ScDPObject& rOther)
    : mpDocument(rOther.mpDocument)
    , maTableName(rOther.maTableName)
    , maTableTag(rOther.maTableTag)
    , maOutputRange(rOther.maOutputRange)
    , maInteropGrabBag(rOther.maInteropGrabBag)
    , mnHeaderRows(rOther.mnHeaderRows)
    , mbHeaderLayout(rOther.mbHeaderLayout)
    , mbAllowMove(false)
    , mbSettingsChanged(false)
    , mbEnableGetPivotData(rOther.mbEnableGetPivotData)
    , mbHideHeader(rOther.mbHideHeader)
{
    if (rOther.mpSaveData)
        mpSaveData.reset(new ScDPSaveData(*rOther.mpSaveData));
    if (rOther.mpSheetDescription)
        mpSheetDescription.reset(new ScSheetSourceDesc(*rOther.mpSheetDescription));
    if (rOther.mpImportDescription)
        mpImportDescription.reset(new ScImportSourceDesc(*rOther.mpImportDescription));
    if (rOther.mpServiceDescription)
        mpServiceDescription.reset(new ScDPServiceDesc(*rOther.mpServiceDescription));
    // mxSource (and mpOutput) is not copied
}

ScDPObject::~ScDPObject()
{
    Clear();
}

ScDPObject& ScDPObject::operator= (const ScDPObject& rOther)
{
    if (this != &rOther)
    {
        Clear();

        mpDocument = rOther.mpDocument;
        maTableName = rOther.maTableName;
        maTableTag = rOther.maTableTag;
        maOutputRange = rOther.maOutputRange;
        maInteropGrabBag = rOther.maInteropGrabBag;
        mnHeaderRows = rOther.mnHeaderRows;
        mbHeaderLayout =rOther.mbHeaderLayout;
        mbAllowMove = false;
        mbSettingsChanged = false;
        mbEnableGetPivotData = rOther.mbEnableGetPivotData;
        mbHideHeader = rOther.mbHideHeader;

        if (rOther.mpSaveData)
            mpSaveData.reset(new ScDPSaveData(*rOther.mpSaveData));
        if (rOther.mpSheetDescription)
            mpSheetDescription.reset(new ScSheetSourceDesc(*rOther.mpSheetDescription));
        if (rOther.mpImportDescription)
            mpImportDescription.reset(new ScImportSourceDesc(*rOther.mpImportDescription));
        if (rOther.mpServiceDescription)
            mpServiceDescription.reset(new ScDPServiceDesc(*rOther.mpServiceDescription));
    }
    return *this;
}

void ScDPObject::EnableGetPivotData(bool b)
{
    mbEnableGetPivotData = b;
}

void ScDPObject::SetAllowMove(bool bSet)
{
    mbAllowMove = bSet;
}

void ScDPObject::SetSaveData(const ScDPSaveData& rData)
{
    if (mpSaveData.get() != &rData)      // API implementation modifies the original SaveData object
    {
        mpSaveData.reset(new ScDPSaveData(rData));
    }

    InvalidateData();       // re-init source from SaveData
}

void ScDPObject::SetHeaderLayout (bool bUseGrid)
{
    mbHeaderLayout = bUseGrid;
}

void ScDPObject::SetHideHeader(bool bHideHeader) { mbHideHeader = bHideHeader; }

void ScDPObject::SetOutRange(const ScRange& rRange)
{
    maOutputRange = rRange;

    if (mpOutput)
        mpOutput->SetPosition( rRange.aStart );
}

const ScRange& ScDPObject::GetOutRange() const
{
    return maOutputRange;
}

void ScDPObject::SetSheetDesc(const ScSheetSourceDesc& rDesc)
{
    if (mpSheetDescription && rDesc == *mpSheetDescription)
        return// nothing to do

    mpImportDescription.reset();
    mpServiceDescription.reset();

    mpSheetDescription.reset( new ScSheetSourceDesc(rDesc) );

    //  make valid QueryParam

    const ScRange& rSrcRange = mpSheetDescription->GetSourceRange();
    ScQueryParam aParam = mpSheetDescription->GetQueryParam();
    aParam.nCol1 = rSrcRange.aStart.Col();
    aParam.nRow1 = rSrcRange.aStart.Row();
    aParam.nCol2 = rSrcRange.aEnd.Col();
    aParam.nRow2 = rSrcRange.aEnd.Row();
    aParam.bHasHeader = true;
    mpSheetDescription->SetQueryParam(aParam);

    ClearTableData(); // new source must be created
}

void ScDPObject::SetImportDesc(const ScImportSourceDesc& rDesc)
{
    if (mpImportDescription && rDesc == *mpImportDescription)
        return// nothing to do

    mpSheetDescription.reset();
    mpServiceDescription.reset();

    mpImportDescription.reset(new ScImportSourceDesc(rDesc));

    ClearTableData(); // new source must be created
}

void ScDPObject::SetServiceData(const ScDPServiceDesc& rDesc)
{
    if (mpServiceDescription && rDesc == *mpServiceDescription)
        return// nothing to do

    mpSheetDescription.reset();
    mpImportDescription.reset();

    mpServiceDescription.reset(new ScDPServiceDesc(rDesc));

    ClearTableData();      // new source must be created
}

void ScDPObject::WriteSourceDataTo( ScDPObject& rDest ) const
{
    if (mpSheetDescription)
        rDest.SetSheetDesc(*mpSheetDescription);
    else if (mpImportDescription)
        rDest.SetImportDesc(*mpImportDescription);
    else if (mpServiceDescription)
        rDest.SetServiceData(*mpServiceDescription);

    //  name/tag are not source data, but needed along with source data

    rDest.maTableName = maTableName;
    rDest.maTableTag  = maTableTag;
}

void ScDPObject::WriteTempDataTo( ScDPObject& rDest ) const
{
    rDest.mnHeaderRows = mnHeaderRows;
}

bool ScDPObject::IsSheetData() const
{
    return mpSheetDescription != nullptr;
}

void ScDPObject::SetName(const OUString& rNew)
{
    maTableName = rNew;
}

void ScDPObject::SetTag(const OUString& rNew)
{
    maTableTag = rNew;
}

bool ScDPObject::IsDataDescriptionCell(const ScAddress& rPos)
{
    if (!mpSaveData)
        return false;

    tools::Long nDataDimCount = mpSaveData->GetDataDimensionCount();
    if (nDataDimCount != 1)
        // There has to be exactly one data dimension for the description to
        // appear at top-left corner.
        return false;

    CreateOutput();
    ScRange aTabRange = mpOutput->GetOutputRange(sheet::DataPilotOutputRangeType::TABLE);
    return (rPos == aTabRange.aStart);
}

uno::Reference<sheet::XDimensionsSupplier> const & ScDPObject::GetSource()
{
    CreateObjects();
    return mxSource;
}

void ScDPObject::CreateOutput()
{
    CreateObjects();
    if (mpOutput)
        return;

    bool bFilterButton = IsSheetData() && mpSaveData && mpSaveData->GetFilterButton();
    bool bExpandCollapse = mpSaveData ? mpSaveData->GetExpandCollapse() : false;

    mpOutput.reset(new ScDPOutput(mpDocument, mxSource, maOutputRange.aStart, bFilterButton, bExpandCollapse, *this, mbHideHeader));
    mpOutput->SetHeaderLayout(mbHeaderLayout);
    if (mpSaveData->hasFormats())
        mpOutput->setFormats(mpSaveData->getFormats());

    sal_Int32 nOldRows = mnHeaderRows;
    mnHeaderRows = mpOutput->GetHeaderRows();

    if (!(mbAllowMove && mnHeaderRows != nOldRows))
        return;

    sal_Int32 nDiff = nOldRows - mnHeaderRows;
    if ( nOldRows == 0 )
        --nDiff;
    if (mnHeaderRows == 0)
        ++nDiff;

    sal_Int32 nNewRow = maOutputRange.aStart.Row() + nDiff;
    if ( nNewRow < 0 )
        nNewRow = 0;

    ScAddress aStart(maOutputRange.aStart);
    aStart.SetRow(nNewRow);
    mpOutput->SetPosition( aStart );

    //TODO: modify maOutputRange?

    mbAllowMove = false// use only once
}

namespace {

class DisableGetPivotData
{
    ScDPObject& mrDPObj;
    bool mbOldState;
public:
    DisableGetPivotData(ScDPObject& rObj, bool bOld) : mrDPObj(rObj), mbOldState(bOld)
    {
        mrDPObj.EnableGetPivotData(false);
    }

    ~DisableGetPivotData()
    {
        mrDPObj.EnableGetPivotData(mbOldState);
    }
};

class FindIntersectingTable
{
    ScRange maRange;
public:
    explicit FindIntersectingTable(const ScRange& rRange) : maRange(rRange) {}

    bool operator() (const std::unique_ptr<ScDPObject>& rObj) const
    {
        return maRange.Intersects(rObj->GetOutRange());
    }
};

class FindIntersectingTableByColumns
{
    SCCOL mnCol1;
    SCCOL mnCol2;
    SCROW mnRow;
    SCTAB mnTab;
public:
    FindIntersectingTableByColumns(SCCOL nCol1, SCCOL nCol2, SCROW nRow, SCTAB nTab) :
        mnCol1(nCol1), mnCol2(nCol2), mnRow(nRow), mnTab(nTab) {}

    bool operator() (const std::unique_ptr<ScDPObject>& rObj) const
    {
        const ScRange& rRange = rObj->GetOutRange();
        if (mnTab != rRange.aStart.Tab())
            // Not on this sheet.
            return false;

        if (rRange.aEnd.Row() < mnRow)
            // This table is above the row.  It's safe.
            return false;

        if (mnCol1 <= rRange.aStart.Col() && rRange.aEnd.Col() <= mnCol2)
            // This table is fully enclosed in this column range.
            return false;

        if (rRange.aEnd.Col() < mnCol1 || mnCol2 < rRange.aStart.Col())
            // This table is entirely outside this column range.
            return false;

        // This table must be intersected by this column range.
        return true;
    }
};

class FindIntersectingTableByRows
{
    SCCOL mnCol;
    SCROW mnRow1;
    SCROW mnRow2;
    SCTAB mnTab;
public:
    FindIntersectingTableByRows(SCCOL nCol, SCROW nRow1, SCROW nRow2, SCTAB nTab) :
        mnCol(nCol), mnRow1(nRow1), mnRow2(nRow2), mnTab(nTab) {}

    bool operator() (const std::unique_ptr<ScDPObject>& rObj) const
    {
        const ScRange& rRange = rObj->GetOutRange();
        if (mnTab != rRange.aStart.Tab())
            // Not on this sheet.
            return false;

        if (rRange.aEnd.Col() < mnCol)
            // This table is to the left of the column.  It's safe.
            return false;

        if (mnRow1 <= rRange.aStart.Row() && rRange.aEnd.Row() <= mnRow2)
            // This table is fully enclosed in this row range.
            return false;

        if (rRange.aEnd.Row() < mnRow1 || mnRow2 < rRange.aStart.Row())
            // This table is entirely outside this row range.
            return false;

        // This table must be intersected by this row range.
        return true;
    }
};

class AccumulateOutputRanges
{
    ScRangeList maRanges;
    SCTAB mnTab;
public:
    explicit AccumulateOutputRanges(SCTAB nTab) : mnTab(nTab) {}
    AccumulateOutputRanges(const AccumulateOutputRanges& r) : maRanges(r.maRanges), mnTab(r.mnTab) {}

    void operator() (const std::unique_ptr<ScDPObject>& rObj)
    {
        const ScRange& rRange = rObj->GetOutRange();
        if (mnTab != rRange.aStart.Tab())
            // Not on this sheet.
            return;

        maRanges.Join(rRange);
    }

    const ScRangeList& getRanges() const { return maRanges; }
};

}

ScDPTableData* ScDPObject::GetTableData()
{
    if (!mpTableData)
    {
        shared_ptr<ScDPTableData> pData;
        const ScDPDimensionSaveData* pDimData = mpSaveData ? mpSaveData->GetExistingDimensionData() : nullptr;

        if (mpImportDescription)
        {
            // database data
            const ScDPCache* pCache = mpImportDescription->CreateCache(pDimData);
            if (pCache)
            {
                pCache->AddReference(this);
                pData = std::make_shared<ScDatabaseDPData>(mpDocument, *pCache);
            }
        }
        else
        {
            // cell data
            if (!mpSheetDescription)
            {
                OSL_FAIL("no source descriptor");
                mpSheetDescription.reset(new ScSheetSourceDesc(mpDocument)); // dummy defaults
            }

            {
                // Temporarily disable GETPIVOTDATA to avoid having
                // GETPIVOTDATA called onto itself from within the source
                // range.
                DisableGetPivotData aSwitch(*this, mbEnableGetPivotData);
                const ScDPCache* pCache = mpSheetDescription->CreateCache(pDimData);
                if (pCache)
                {
                    pCache->AddReference(this);
                    pData = std::make_shared<ScSheetDPData>(mpDocument, *mpSheetDescription, *pCache);
                }
            }
        }

        // grouping (for cell or database data)
        if (pData && pDimData)
        {
            auto pGroupData = std::make_shared<ScDPGroupTableData>(pData, mpDocument);
            pDimData->WriteToData(*pGroupData);
            pData = pGroupData;
        }

        mpTableData = std::move(pData);             // after SetCacheId
    }

    return mpTableData.get();
}

void ScDPObject::CreateObjects()
{
    if (!mxSource.is())
    {
        mpOutput.reset(); // not valid when mxSource is changed

        if (mpServiceDescription)
        {
            mxSource = CreateSource(*mpServiceDescription);
        }

        if (!mxSource.is())    // database or sheet data, or error in CreateSource
        {
            OSL_ENSURE(!mpServiceDescription, "DPSource could not be created");
            ScDPTableData* pData = GetTableData();
            if (pData)
            {
                if (mpSaveData)
                    // Make sure to transfer these flags to the table data
                    // since they may have changed.
                    pData->SetEmptyFlags(mpSaveData->GetIgnoreEmptyRows(), mpSaveData->GetRepeatIfEmpty());

                pData->ReloadCacheTable();
                mxSource = new ScDPSource( pData );
            }
        }

        if (mpSaveData)
            mpSaveData->WriteToSource(mxSource);
    }
    else if (mbSettingsChanged)
    {
        mpOutput.reset(); // not valid when mxSource is changed

        uno::Reference<util::XRefreshable> xRef(mxSource, uno::UNO_QUERY);
        if (xRef.is())
        {
            try
            {
                xRef->refresh();
            }
            catch(uno::Exception&)
            {
                TOOLS_WARN_EXCEPTION( "sc""exception in refresh");
            }
        }

        if (mpSaveData)
            mpSaveData->WriteToSource(mxSource);
    }
    mbSettingsChanged = false;
}

void ScDPObject::InvalidateData()
{
    mbSettingsChanged = true;
}

void ScDPObject::Clear()
{
    mpOutput.reset();
    mpSaveData.reset();
    mpSheetDescription.reset();
    mpImportDescription.reset();
    mpServiceDescription.reset();
    ClearTableData();
    maInteropGrabBag.clear();
}

void ScDPObject::ClearTableData()
{
    ClearSource();

    if (mpTableData)
        mpTableData->GetCacheTable().getCache().RemoveReference(this);
    mpTableData.reset();
}

void ScDPObject::ReloadGroupTableData()
{
    ClearSource();

    if (!mpTableData)
        // Table data not built yet.  No need to reload the group data.
        return;

    if (!mpSaveData)
        // How could it not have the save data... but whatever.
        return;

    const ScDPDimensionSaveData* pDimData = mpSaveData->GetExistingDimensionData();
    if (!pDimData || !pDimData->HasGroupDimensions())
    {
        // No group dimensions exist.  Check if it currently has group
        // dimensions, and if so, remove all of them.
        ScDPGroupTableData* pData = dynamic_cast<ScDPGroupTableData*>(mpTableData.get());
        if (pData)
        {
            // Replace the existing group table data with the source data.
            mpTableData = pData->GetSourceTableData();
        }
        return;
    }

    ScDPGroupTableData* pData = dynamic_cast<ScDPGroupTableData*>(mpTableData.get());
    if (pData)
    {
        // This is already a group table data. Salvage the source data and
        // re-create a new group data.
        const shared_ptr<ScDPTableData>& pSource = pData->GetSourceTableData();
        auto pGroupData = std::make_shared<ScDPGroupTableData>(pSource, mpDocument);
        pDimData->WriteToData(*pGroupData);
        mpTableData = pGroupData;
    }
    else
    {
        // This is a source data.  Create a group data based on it.
        auto pGroupData = std::make_shared<ScDPGroupTableData>(mpTableData, mpDocument);
        pDimData->WriteToData(*pGroupData);
        mpTableData = pGroupData;
    }

    mbSettingsChanged = true;
}

void ScDPObject::ClearSource()
{
    uno::Reference<XComponent> xObjectComp(mxSource, UNO_QUERY);
    if (xObjectComp.is())
    {
        try
        {
            xObjectComp->dispose();
        }
        catchconst Exception& )
        {
            DBG_UNHANDLED_EXCEPTION("sc.core");
        }
    }
    mxSource = nullptr;
}

ScRange ScDPObject::GetNewOutputRange( bool& rOverflow )
{
    CreateOutput(); // create mxSource and mpOutput if not already done

    rOverflow = mpOutput->HasError();        // range overflow or exception from source
    if ( rOverflow )
        return ScRange(maOutputRange.aStart);
    else
    {
        //  don't store the result in maOutputRange, because nothing has been output yet
        return mpOutput->GetOutputRange();
    }
}

void ScDPObject::Output( const ScAddress& rPos )
{
    //  clear old output area
    mpDocument->DeleteAreaTab(maOutputRange.aStart.Col(), maOutputRange.aStart.Row(),
                         maOutputRange.aEnd.Col(), maOutputRange.aEnd.Row(),
                         maOutputRange.aStart.Tab(), InsertDeleteFlags::ALL );
    mpDocument->RemoveFlagsTab( maOutputRange.aStart.Col(), maOutputRange.aStart.Row(),
                          maOutputRange.aEnd.Col(), maOutputRange.aEnd.Row(),
                          maOutputRange.aStart.Tab(), ScMF::Auto );

    CreateOutput(); // create mxSource and mpOutput if not already done

    mpOutput->SetPosition( rPos );

    mpOutput->Output();

    // maOutputRange is always the range that was last output to the document
    maOutputRange = mpOutput->GetOutputRange();
    const ScAddress& s = maOutputRange.aStart;
    const ScAddress& e = maOutputRange.aEnd;
    mpDocument->ApplyFlagsTab(s.Col(), s.Row(), e.Col(), e.Row(), s.Tab(), ScMF::DpTable);
}

ScRange ScDPObject::GetOutputRangeByType( sal_Int32 nType )
{
    CreateOutput();

    if (mpOutput->HasError())
        return ScRange(maOutputRange.aStart);

    return mpOutput->GetOutputRange(nType);
}

ScRange ScDPObject::GetOutputRangeByType( sal_Int32 nType ) const
{
    if (!mpOutput || mpOutput->HasError())
        return ScRange(ScAddress::INITIALIZE_INVALID);

    return mpOutput->GetOutputRange(nType);
}

static bool lcl_HasButton( const ScDocument* pDoc, SCCOL nCol, SCROW nRow, SCTAB nTab )
{
    return pDoc->GetAttr( nCol, nRow, nTab, ATTR_MERGE_FLAG )->HasPivotButton();
}

void ScDPObject::RefreshAfterLoad()
{
    // apply drop-down attribute, initialize mnHeaderRows, without accessing the source
    // (button attribute must be present)

    // simple test: block of button cells at the top, followed by an empty cell

    SCCOL nFirstCol = maOutputRange.aStart.Col();
    SCROW nFirstRow = maOutputRange.aStart.Row();
    SCTAB nTab = maOutputRange.aStart.Tab();

    SCROW nInitial = 0;
    SCROW nOutRows = maOutputRange.aEnd.Row() + 1 - maOutputRange.aStart.Row();
    while (nInitial + 1 < nOutRows && lcl_HasButton(mpDocument, nFirstCol, nFirstRow + nInitial, nTab))
        ++nInitial;

    if ( nInitial + 1 < nOutRows &&
        mpDocument->IsBlockEmpty( nFirstCol, nFirstRow + nInitial, nFirstCol, nFirstRow + nInitial, nTab ) &&
        maOutputRange.aEnd.Col() > nFirstCol )
    {
        mnHeaderRows = nInitial;
    }
    else
        mnHeaderRows = 0;        // nothing found, no drop-down lists
}

void ScDPObject::BuildAllDimensionMembers()
{
    if (!mpSaveData)
        return;

    // #i111857# don't always create empty mpTableData for external service.
    if (mpServiceDescription)
        return;

    ScDPTableData* pTableData = GetTableData();
    if(pTableData)
        mpSaveData->BuildAllDimensionMembers(pTableData);
}

bool ScDPObject::SyncAllDimensionMembers()
{
    if (!mpSaveData)
        return false;

    // #i111857# don't always create empty mpTableData for external service.
    // Ideally, mxSource should be used instead of mpTableData.
    if (mpServiceDescription)
        return false;

    ScDPTableData* pData = GetTableData();
    if (!pData)
        // No table data exists.  This can happen when refreshing from an
        // external source which doesn't exist.
        return false;

    // Refresh the cache wrapper since the cache may have changed.
    pData->SetEmptyFlags(mpSaveData->GetIgnoreEmptyRows(), mpSaveData->GetRepeatIfEmpty());
    pData->ReloadCacheTable();
    mpSaveData->SyncAllDimensionMembers(pData);
    return true;
}

bool ScDPObject::GetMemberNames( sal_Int32 nDim, Sequence<OUString>& rNames )
{
    vector<ScDPLabelData::Member> aMembers;
    if (!GetMembers(nDim, GetUsedHierarchy(nDim), aMembers))
        return false;

    size_t n = aMembers.size();
    rNames.realloc(n);
    auto pNames = rNames.getArray();
    for (size_t i = 0; i < n; ++i)
        pNames[i] = aMembers[i].maName;

    return true;
}

bool ScDPObject::GetMembers( sal_Int32 nDim, sal_Int32 nHier, vector<ScDPLabelData::Member>& rMembers )
{
    Reference< sheet::XMembersAccess > xMembersNA;
    if (!GetMembersNA( nDim, nHier, xMembersNA ))
        return false;

    Reference<container::XIndexAccess> xMembersIA( new ScNameToIndexAccess(xMembersNA) );
    sal_Int32 nCount = xMembersIA->getCount();
    vector<ScDPLabelData::Member> aMembers;
    aMembers.reserve(nCount);

    for (sal_Int32 i = 0; i < nCount; ++i)
    {
        Reference<container::XNamed> xMember;
        try
        {
            xMember = Reference<container::XNamed>(xMembersIA->getByIndex(i), UNO_QUERY);
        }
        catch (const container::NoSuchElementException&)
        {
            TOOLS_WARN_EXCEPTION("sc""ScNameToIndexAccess getByIndex failed");
        }

        ScDPLabelData::Member aMem;

        if (xMember.is())
            aMem.maName = xMember->getName();

        Reference<beans::XPropertySet> xMemProp(xMember, UNO_QUERY);
        if (xMemProp.is())
        {
            aMem.mbVisible     = ScUnoHelpFunctions::GetBoolProperty(xMemProp, SC_UNO_DP_ISVISIBLE);
            aMem.mbShowDetails = ScUnoHelpFunctions::GetBoolProperty(xMemProp, SC_UNO_DP_SHOWDETAILS);

            aMem.maLayoutName = ScUnoHelpFunctions::GetStringProperty(
                xMemProp, SC_UNO_DP_LAYOUTNAME, OUString());
        }

        aMembers.push_back(aMem);
    }
    rMembers.swap(aMembers);
    return true;
}

void ScDPObject::UpdateReference( UpdateRefMode eUpdateRefMode,
                                     const ScRange& rRange, SCCOL nDx, SCROW nDy, SCTAB nDz )
{
    // Output area

    SCCOL nCol1 = maOutputRange.aStart.Col();
    SCROW nRow1 = maOutputRange.aStart.Row();
    SCTAB nTab1 = maOutputRange.aStart.Tab();
    SCCOL nCol2 = maOutputRange.aEnd.Col();
    SCROW nRow2 = maOutputRange.aEnd.Row();
    SCTAB nTab2 = maOutputRange.aEnd.Tab();

    ScRefUpdateRes eRes =
        ScRefUpdate::Update(*mpDocument, eUpdateRefMode,
            rRange.aStart.Col(), rRange.aStart.Row(), rRange.aStart.Tab(),
            rRange.aEnd.Col(), rRange.aEnd.Row(), rRange.aEnd.Tab(), nDx, nDy, nDz,
            nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
    if ( eRes != UR_NOTHING )
        SetOutRange( ScRange( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ) );

    // sheet source data

    if (!mpSheetDescription)
        return;

    const OUString& rRangeName = mpSheetDescription->GetRangeName();
    if (!rRangeName.isEmpty())
        // Source range is a named range.  No need to update.
        return;

    const ScRange& rSrcRange = mpSheetDescription->GetSourceRange();
    nCol1 = rSrcRange.aStart.Col();
    nRow1 = rSrcRange.aStart.Row();
    nTab1 = rSrcRange.aStart.Tab();
    nCol2 = rSrcRange.aEnd.Col();
    nRow2 = rSrcRange.aEnd.Row();
    nTab2 = rSrcRange.aEnd.Tab();

    eRes = ScRefUpdate::Update(*mpDocument, eUpdateRefMode,
            rRange.aStart.Col(), rRange.aStart.Row(), rRange.aStart.Tab(),
            rRange.aEnd.Col(), rRange.aEnd.Row(), rRange.aEnd.Tab(), nDx, nDy, nDz,
            nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
    if ( eRes == UR_NOTHING )
        return;

    SCCOL nDiffX = nCol1 - mpSheetDescription->GetSourceRange().aStart.Col();
    SCROW nDiffY = nRow1 - mpSheetDescription->GetSourceRange().aStart.Row();

    ScQueryParam aParam = mpSheetDescription->GetQueryParam();
    aParam.nCol1 = sal::static_int_cast<SCCOL>( aParam.nCol1 + nDiffX );
    aParam.nCol2 = sal::static_int_cast<SCCOL>( aParam.nCol2 + nDiffX );
    aParam.nRow1 += nDiffY; //TODO: used?
    aParam.nRow2 += nDiffY; //TODO: used?
    SCSIZE nEC = aParam.GetEntryCount();
    for (SCSIZE i=0; i<nEC; i++)
        if (aParam.GetEntry(i).bDoQuery)
            aParam.GetEntry(i).nField += nDiffX;

    mpSheetDescription->SetQueryParam(aParam);
    mpSheetDescription->SetSourceRange(ScRange(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2));
}

bool ScDPObject::RefsEqual( const ScDPObject& rOther) const
{
    if (maOutputRange != rOther.maOutputRange)
        return false;

    if (mpSheetDescription && rOther.mpSheetDescription)
    {
        if (mpSheetDescription->GetSourceRange() != rOther.mpSheetDescription->GetSourceRange())
            return false;
    }
    else if (mpSheetDescription || rOther.mpSheetDescription)
    {
        OSL_FAIL("RefsEqual: SheetDesc set at only one object");
        return false;
    }

    return true;
}

void ScDPObject::WriteRefsTo(ScDPObject& rObject) const
{
    rObject.SetOutRange(maOutputRange);
    if (mpSheetDescription)
        rObject.SetSheetDesc(*mpSheetDescription);
}

void ScDPObject::GetPositionData(const ScAddress& rPos, DataPilotTablePositionData& rPosData)
{
    CreateOutput();
    mpOutput->GetPositionData(rPos, rPosData);
}

bool ScDPObject::GetDataFieldPositionData(
    const ScAddress& rPos, Sequence<sheet::DataPilotFieldFilter>& rFilters)
{
    CreateOutput();

    vector<sheet::DataPilotFieldFilter> aFilters;
    if (!mpOutput->GetDataResultPositionData(aFilters, rPos))
        return false;

    sal_Int32 n = static_cast<sal_Int32>(aFilters.size());
    rFilters.realloc(n);
    auto pFilters = rFilters.getArray();
    for (sal_Int32 i = 0; i < n; ++i)
        pFilters[i] = aFilters[i];

    return true;
}

void ScDPObject::GetDrillDownData(const ScAddress& rPos, Sequence< Sequence<Any> >& ;rTableData)
{
    CreateOutput();

    uno::Reference<sheet::XDrillDownDataSupplier> xDrillDownData(mxSource, UNO_QUERY);
    if (!xDrillDownData.is())
        return;

    Sequence<sheet::DataPilotFieldFilter> filters;
    if (!GetDataFieldPositionData(rPos, filters))
        return;

    rTableData = xDrillDownData->getDrillDownData(filters);
}

bool ScDPObject::IsDimNameInUse(std::u16string_view rName) const
{
    if (!mxSource.is())
        return false;

    Reference<container::XNameAccess> xDims = mxSource->getDimensions();
    const Sequence<OUString> aDimNames = xDims->getElementNames();
    for (const OUString& rDimName : aDimNames)
    {
        if (rDimName.equalsIgnoreAsciiCase(rName))
            return true;

        Reference<beans::XPropertySet> xPropSet(xDims->getByName(rDimName), UNO_QUERY);
        if (!xPropSet.is())
            continue;

        OUString aLayoutName = ScUnoHelpFunctions::GetStringProperty(
            xPropSet, SC_UNO_DP_LAYOUTNAME, OUString());
        if (aLayoutName.equalsIgnoreAsciiCase(rName))
            return true;
    }
    return false;
}

OUString ScDPObject::GetDimName( tools::Long nDim, bool& rIsDataLayout, sal_Int32* pFlags )
{
    rIsDataLayout = false;
    OUString aRet;

    if (mxSource.is())
    {
        uno::Reference<container::XNameAccess> xDimsName = mxSource->getDimensions();
        uno::Reference<container::XIndexAccess> xDims = new ScNameToIndexAccess( xDimsName );
        tools::Long nDimCount = xDims->getCount();
        if ( nDim < nDimCount )
        {
            uno::Reference<uno::XInterface> xIntDim(xDims->getByIndex(nDim), uno::UNO_QUERY);
            uno::Reference<container::XNamed> xDimName( xIntDim, uno::UNO_QUERY );
            uno::Reference<beans::XPropertySet> xDimProp( xIntDim, uno::UNO_QUERY );
            if ( xDimName.is() && xDimProp.is() )
            {
                bool bData = ScUnoHelpFunctions::GetBoolProperty( xDimProp,
                                SC_UNO_DP_ISDATALAYOUT );
                //TODO: error checking -- is "IsDataLayoutDimension" property required??

                OUString aName;
                try
                {
                    aName = xDimName->getName();
                }
                catch(uno::Exception&)
                {
                }
                if ( bData )
                    rIsDataLayout = true;
                else
                    aRet = aName;

                if (pFlags)
                    *pFlags = ScUnoHelpFunctions::GetLongProperty( xDimProp,
                                SC_UNO_DP_FLAGS );
            }
        }
    }
    else if (ScDPTableData* pData = GetTableData())
    {
        aRet = pData->getDimensionName(nDim);
        rIsDataLayout = pData->getIsDataLayoutDimension(nDim);
    }

    return aRet;
}

bool ScDPObject::IsDuplicated( tools::Long nDim )
{
    bool bDuplicated = false;
    if (mxSource.is())
    {
        uno::Reference<container::XNameAccess> xDimsName = mxSource->getDimensions();
        uno::Reference<container::XIndexAccess> xDims = new ScNameToIndexAccess( xDimsName );
        tools::Long nDimCount = xDims->getCount();
        if ( nDim < nDimCount )
        {
            uno::Reference<beans::XPropertySet> xDimProp(xDims->getByIndex(nDim), uno::UNO_QUERY);
            if ( xDimProp.is() )
            {
                try
                {
                    uno::Any aOrigAny = xDimProp->getPropertyValue( SC_UNO_DP_ORIGINAL );
                    uno::Reference<uno::XInterface> xIntOrig;
                    if ( (aOrigAny >>= xIntOrig) && xIntOrig.is() )
                        bDuplicated = true;
                }
                catch(uno::Exception&)
                {
                }
            }
        }
    }
    return bDuplicated;
}

tools::Long ScDPObject::GetDimCount()
{
    tools::Long nRet = 0;
    if (mxSource.is())
    {
        try
        {
            uno::Reference<container::XNameAccess> xDimsName = mxSource->getDimensions();
            if ( xDimsName.is() )
                nRet = xDimsName->getElementNames().getLength();
        }
        catch(uno::Exception&)
        {
        }
    }
    return nRet;
}

void ScDPObject::GetHeaderPositionData(const ScAddress& rPos, DataPilotTableHeaderData& rData)
{
    CreateOutput(); // create mxSource and mpOutput if not already done

    // Reset member values to invalid state.
    rData.Dimension = rData.Hierarchy = rData.Level = -1;
    rData.Flags = 0;

    DataPilotTablePositionData aPosData;
    mpOutput->GetPositionData(rPos, aPosData);
    const sal_Int32 nPosType = aPosData.PositionType;
    if (nPosType == css::sheet::DataPilotTablePositionType::COLUMN_HEADER || nPosType == css::sheet::DataPilotTablePositionType::ROW_HEADER)
        aPosData.PositionData >>= rData;
}

namespace {

class FindByName
{
    OUString maName; // must be all uppercase.
public:
    explicit FindByName(OUString aName) : maName(std::move(aName)) {}
    bool operator() (const ScDPSaveDimension* pDim) const
    {
        // Layout name takes precedence.
        const std::optional<OUString> & pLayoutName = pDim->GetLayoutName();
        if (pLayoutName && ScGlobal::getCharClass().uppercase(*pLayoutName) == maName)
            return true;

        ScGeneralFunction eGenFunc = pDim->GetFunction();
        ScSubTotalFunc eFunc = ScDPUtil::toSubTotalFunc(eGenFunc);
        OUString aSrcName = ScDPUtil::getSourceDimensionName(pDim->GetName());
        OUString aFuncName = ScDPUtil::getDisplayedMeasureName(aSrcName, eFunc);
        if (maName == ScGlobal::getCharClass().uppercase(aFuncName))
            return true;

        return maName == ScGlobal::getCharClass().uppercase(aSrcName);
    }
};

class LessByDimOrder
{
    const ScDPSaveData::DimOrderType& mrDimOrder;

public:
    explicit LessByDimOrder(const ScDPSaveData::DimOrderType& rDimOrder) : mrDimOrder(rDimOrder) {}

    bool operator() (const sheet::DataPilotFieldFilter& r1, const sheet::DataPilotFieldFilter& r2) const
    {
        size_t nRank1 = mrDimOrder.size();
        size_t nRank2 = mrDimOrder.size();
        ScDPSaveData::DimOrderType::const_iterator it1 = mrDimOrder.find(
            ScGlobal::getCharClass().uppercase(r1.FieldName));
        if (it1 != mrDimOrder.end())
            nRank1 = it1->second;

        ScDPSaveData::DimOrderType::const_iterator it2 = mrDimOrder.find(
            ScGlobal::getCharClass().uppercase(r2.FieldName));
        if (it2 != mrDimOrder.end())
            nRank2 = it2->second;

        return nRank1 < nRank2;
    }
};

}

double ScDPObject::GetPivotData(const OUString& rDataFieldName, std::vector<sheet::DataPilotFieldFilter>& rFilters)
{
    if (!mbEnableGetPivotData)
        return std::numeric_limits<double>::quiet_NaN();

    CreateObjects();

    std::vector<const ScDPSaveDimension*> aDataDims;
    mpSaveData->GetAllDimensionsByOrientation(sheet::DataPilotFieldOrientation_DATA, aDataDims);
    if (aDataDims.empty())
        return std::numeric_limits<double>::quiet_NaN();

    std::vector<const ScDPSaveDimension*>::iterator it = std::find_if(
        aDataDims.begin(), aDataDims.end(),
        FindByName(ScGlobal::getCharClass().uppercase(rDataFieldName)));

    if (it == aDataDims.end())
        return std::numeric_limits<double>::quiet_NaN();

    size_t nDataIndex = std::distance(aDataDims.begin(), it);

    uno::Reference<sheet::XDataPilotResults> xDPResults(mxSource, uno::UNO_QUERY);
    if (!xDPResults.is())
        return std::numeric_limits<double>::quiet_NaN();

    // Dimensions must be sorted in order of appearance, and row dimensions
    // must come before column dimensions.
    std::sort(rFilters.begin(), rFilters.end(), LessByDimOrder(mpSaveData->GetDimensionSortOrder()));

    size_t n = rFilters.size();
    uno::Sequence<sheet::DataPilotFieldFilter> aFilters(n);
    auto aFiltersRange = asNonConstRange(aFilters);
    for (size_t i = 0; i < n; ++i)
        aFiltersRange[i] = rFilters[i];

    uno::Sequence<double> aRes = xDPResults->getFilteredResults(aFilters);
    if (nDataIndex >= o3tl::make_unsigned(aRes.getLength()))
        return std::numeric_limits<double>::quiet_NaN();

    return aRes[nDataIndex];
}

bool ScDPObject::IsFilterButton( const ScAddress& rPos )
{
    CreateOutput(); // create mxSource and mpOutput if not already done

    return mpOutput->IsFilterButton( rPos );
}

tools::Long ScDPObject::GetHeaderDim( const ScAddress& rPos, sheet::DataPilotFieldOrientation& rOrient )
{
    CreateOutput(); // create mxSource and mpOutput if not already done

    return mpOutput->GetHeaderDim( rPos, rOrient );
}

bool ScDPObject::GetHeaderDrag( const ScAddress& rPos, bool bMouseLeft, bool bMouseTop, tools::Long nDragDim,
                                tools::Rectangle& rPosRect, sheet::DataPilotFieldOrientation& rOrient, tools::Long& rDimPos )
{
    CreateOutput();// create mxSource and mpOutput if not already done

    return mpOutput->GetHeaderDrag( rPos, bMouseLeft, bMouseTop, nDragDim, rPosRect, rOrient, rDimPos );
}

void ScDPObject::GetMemberResultNames(ScDPUniqueStringSet& rNames, tools::Long nDimension)
{
    CreateOutput();// create mxSource and mpOutput if not already done

    mpOutput->GetMemberResultNames(rNames, nDimension);    // used only with table data -> level not needed
}

OUString ScDPObject::GetFormattedString(const ScDPTableData* pTableData, tools::Long nDimension, const double fValue)
{
    ScDPItemData aItemData;
    aItemData.SetValue(fValue);
    return pTableData->GetFormattedString(nDimension, aItemData, false);
}

OUString ScDPObject::GetFormattedString(std::u16string_view rDimName, const double fValue)
{
    ScDPTableData* pTableData = GetTableData();
    if(!pTableData)
        return OUString();

    tools::Long nDim;
    for (nDim = 0; nDim < pTableData->GetColumnCount(); ++nDim)
    {
        if(rDimName == pTableData->getDimensionName(nDim))
            break;
    }

    return GetFormattedString(pTableData, nDim, fValue);
}


namespace {

bool dequote( std::u16string_view rSource, sal_Int32 nStartPos, sal_Int32& rEndPos, OUString& rResult )
{
    // nStartPos has to point to opening quote

    const sal_Unicode cQuote = '\'';

    if (rSource[nStartPos] == cQuote)
    {
        OUStringBuffer aBuffer;
        sal_Int32 nPos = nStartPos + 1;
        const sal_Int32 nLen = rSource.size();

        while ( nPos < nLen )
        {
            const sal_Unicode cNext = rSource[nPos];
            if ( cNext == cQuote )
            {
                if (nPos+1 < nLen && rSource[nPos+1] == cQuote)
                {
                    // double quote is used for an embedded quote
                    aBuffer.append( cNext );    // append one quote
                    ++nPos;                     // skip the next one
                }
                else
                {
                    // end of quoted string
                    rResult = aBuffer.makeStringAndClear();
                    rEndPos = nPos + 1;         // behind closing quote
                    return true;
                }
            }
            else
                aBuffer.append( cNext );

            ++nPos;
        }
        // no closing quote before the end of the string -> error (bRet still false)
    }

    return false;
}

struct ScGetPivotDataFunctionEntry
{
    const char*       pName;
    sal_Int16         eFunc;
};

bool parseFunction( std::u16string_view rList, sal_Int32 nStartPos, sal_Int32& rEndPos, sal_Int16& rFunc )
{
    static const ScGetPivotDataFunctionEntry aFunctions[] =
    {
        // our names
        { "Sum",        sheet::GeneralFunction2::SUM       },
        { "Count",      sheet::GeneralFunction2::COUNT     },
        { "Average",    sheet::GeneralFunction2::AVERAGE   },
        { "Max",        sheet::GeneralFunction2::MAX       },
        { "Min",        sheet::GeneralFunction2::MIN       },
        { "Product",    sheet::GeneralFunction2::PRODUCT   },
        { "CountNums",  sheet::GeneralFunction2::COUNTNUMS },
        { "StDev",      sheet::GeneralFunction2::STDEV     },
        { "StDevp",     sheet::GeneralFunction2::STDEVP    },
        { "Var",        sheet::GeneralFunction2::VAR       },
        { "VarP",       sheet::GeneralFunction2::VARP      },
        // compatibility names
        { "Count Nums", sheet::GeneralFunction2::COUNTNUMS },
        { "StdDev",     sheet::GeneralFunction2::STDEV     },
        { "StdDevp",    sheet::GeneralFunction2::STDEVP    }
    };

    const sal_Int32 nListLen = rList.size();
    while (nStartPos < nListLen && rList[nStartPos] == ' ')
        ++nStartPos;

    bool bParsed = false;
    bool bFound = false;
    OUString aFuncStr;
    sal_Int32 nFuncEnd = 0;
    if (nStartPos < nListLen && rList[nStartPos] == '\'')
        bParsed = dequote( rList, nStartPos, nFuncEnd, aFuncStr );
    else
    {
        nFuncEnd = rList.find(']', nStartPos);
        if (nFuncEnd >= 0)
        {
            aFuncStr = rList.substr(nStartPos, nFuncEnd - nStartPos);
            bParsed = true;
        }
    }

    if ( bParsed )
    {
        aFuncStr = comphelper::string::strip(aFuncStr, ' ');

        const sal_Int32 nFuncCount = SAL_N_ELEMENTS(aFunctions);
        for ( sal_Int32 nFunc=0; nFunc<nFuncCount && !bFound; nFunc++ )
        {
            if (aFuncStr.equalsIgnoreAsciiCaseAscii(aFunctions[nFunc].pName))
            {
                rFunc = aFunctions[nFunc].eFunc;
                bFound = true;

                while (nFuncEnd < nListLen && rList[nFuncEnd] == ' ')
                    ++nFuncEnd;
                rEndPos = nFuncEnd;
            }
        }
    }

    return bFound;
}

bool extractAtStart( std::u16string_view rList, sal_Int32& rMatched, bool bAllowBracket, sal_Int16* pFunc,
        OUString& rDequoted )
{
    size_t nMatchList = 0;
    sal_Unicode cFirst = rList[0];
    bool bParsed = false;
    if ( cFirst == '\'' || cFirst == '[' )
    {
        // quoted string or string in brackets must match completely

        OUString aDequoted;
        sal_Int32 nQuoteEnd = 0;

        if ( cFirst == '\'' )
            bParsed = dequote( rList, 0, nQuoteEnd, aDequoted );
        else if ( cFirst == '[' )
        {
            // skip spaces after the opening bracket

            sal_Int32 nStartPos = 1;
            const sal_Int32 nListLen = rList.size();
            while (nStartPos < nListLen && rList[nStartPos] == ' ')
                ++nStartPos;

            if (nStartPos < nListLen && rList[nStartPos] == '\'') // quoted within the brackets?
            {
                if ( dequote( rList, nStartPos, nQuoteEnd, aDequoted ) )
                {
                    // after the quoted string, there must be the closing bracket, optionally preceded by spaces,
                    // and/or a function name
                    while (nQuoteEnd < nListLen && rList[nQuoteEnd] == ' ')
                        ++nQuoteEnd;

                    // semicolon separates function name
                    if (nQuoteEnd < nListLen && rList[nQuoteEnd] == ';' && pFunc)
                    {
                        sal_Int32 nFuncEnd = 0;
                        if ( parseFunction( rList, nQuoteEnd + 1, nFuncEnd, *pFunc ) )
                            nQuoteEnd = nFuncEnd;
                    }
                    if (nQuoteEnd < nListLen && rList[nQuoteEnd] == ']')
                    {
                        ++nQuoteEnd;        // include the closing bracket for the matched length
                        bParsed = true;
                    }
                }
            }
            else
            {
                // implicit quoting to the closing bracket

                sal_Int32 nClosePos = rList.find(']', nStartPos);
                if (nClosePos >= 0)
                {
                    sal_Int32 nNameEnd = nClosePos;
                    sal_Int32 nSemiPos = rList.find(';', nStartPos);
                    if (nSemiPos >= 0 && nSemiPos < nClosePos && pFunc)
                    {
                        sal_Int32 nFuncEnd = 0;
                        if (parseFunction(rList, nSemiPos+1, nFuncEnd, *pFunc))
                            nNameEnd = nSemiPos;
                    }

                    aDequoted = rList.substr(nStartPos, nNameEnd - nStartPos);
                    // spaces before the closing bracket or semicolon
                    aDequoted = comphelper::string::stripEnd(aDequoted, ' ');
                    nQuoteEnd = nClosePos + 1;
                    bParsed = true;
                }
            }
        }

        if ( bParsed )
        {
            nMatchList = nQuoteEnd;             // match count in the list string, including quotes
            rDequoted = aDequoted;
        }
    }

    if (bParsed)
    {
        // look for following space or end of string

        bool bValid = false;
        if ( nMatchList >= rList.size() )
            bValid = true;
        else
        {
            sal_Unicode cNext = rList[nMatchList];
            if ( cNext == ' ' || ( bAllowBracket && cNext == '[' ) )
                bValid = true;
        }

        if ( bValid )
        {
            rMatched = nMatchList;
            return true;
        }
    }

    return false;
}

bool isAtStart(
    const OUString& rList, const OUString& rSearch, sal_Int32& rMatched,
    bool bAllowBracket, sal_Int16* pFunc )
{
    sal_Int32 nMatchList = 0;
    sal_Int32 nMatchSearch = 0;
    sal_Unicode cFirst = rList[0];
    if ( cFirst == '\'' || cFirst == '[' )
    {
        OUString aDequoted;
        bool bParsed = extractAtStart( rList, rMatched, bAllowBracket, pFunc, aDequoted);
        if ( bParsed && ScGlobal::GetTransliteration().isEqual( aDequoted, rSearch ) )
        {
            nMatchList = rMatched;             // match count in the list string, including quotes
            nMatchSearch = rSearch.getLength();
        }
    }
    else
    {
        // otherwise look for search string at the start of rList
        ScGlobal::GetTransliteration().equals(
            rList, 0, rList.getLength(), nMatchList, rSearch, 0, rSearch.getLength(), nMatchSearch);
    }

    if (nMatchSearch == rSearch.getLength())
    {
        // search string is at start of rList - look for following space or end of string

        bool bValid = false;
        if ( sal::static_int_cast<sal_Int32>(nMatchList) >= rList.getLength() )
            bValid = true;
        else
        {
            sal_Unicode cNext = rList[nMatchList];
            if ( cNext == ' ' || ( bAllowBracket && cNext == '[' ) )
                bValid = true;
        }

        if ( bValid )
        {
            rMatched = nMatchList;
            return true;
        }
    }

    return false;
}

// anonymous namespace

bool ScDPObject::ParseFilters(
    OUString& rDataFieldName,
    std::vector<sheet::DataPilotFieldFilter>& rFilters,
    std::vector<sal_Int16>& rFilterFuncs, std::u16string_view rFilterList )
{
    // parse the string rFilterList into parameters for GetPivotData

    CreateObjects(); // create mxSource if not already done

    std::vector<OUString> aDataNames;     // data fields (source name)
    std::vector<OUString> aGivenNames;    // data fields (compound name)
    std::vector<OUString> aFieldNames;    // column/row/data fields
    std::vector< uno::Sequence<OUString> > aFieldValueNames;
    std::vector< uno::Sequence<OUString> > aFieldValues;

    // get all the field and item names

    uno::Reference<container::XNameAccess> xDimsName = mxSource->getDimensions();
    uno::Reference<container::XIndexAccess> xIntDims = new ScNameToIndexAccess( xDimsName );
    sal_Int32 nDimCount = xIntDims->getCount();
    for ( sal_Int32 nDim = 0; nDim<nDimCount; nDim++ )
    {
        uno::Reference<uno::XInterface> xIntDim(xIntDims->getByIndex(nDim), uno::UNO_QUERY);
        uno::Reference<container::XNamed> xDim( xIntDim, uno::UNO_QUERY );
        uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
        uno::Reference<sheet::XHierarchiesSupplier> xDimSupp( xDim, uno::UNO_QUERY );
        bool bDataLayout = ScUnoHelpFunctions::GetBoolProperty( xDimProp,
                            SC_UNO_DP_ISDATALAYOUT );
        sheet::DataPilotFieldOrientation nOrient = ScUnoHelpFunctions::GetEnumProperty(
                            xDimProp, SC_UNO_DP_ORIENTATION,
                            sheet::DataPilotFieldOrientation_HIDDEN );
        if ( !bDataLayout )
        {
            if ( nOrient == sheet::DataPilotFieldOrientation_DATA )
            {
                OUString aSourceName;
                OUString aGivenName;
                ScDPOutput::GetDataDimensionNames( aSourceName, aGivenName, xIntDim );
                aDataNames.push_back( aSourceName );
                aGivenNames.push_back( aGivenName );
            }
            else if ( nOrient != sheet::DataPilotFieldOrientation_HIDDEN )
            {
                // get level names, as in ScDPOutput

                uno::Reference<container::XIndexAccess> xHiers = new ScNameToIndexAccess( xDimSupp->getHierarchies() );
                sal_Int32 nHierarchy = ScUnoHelpFunctions::GetLongProperty( xDimProp,
                                                    SC_UNO_DP_USEDHIERARCHY );
                if ( nHierarchy >= xHiers->getCount() )
                    nHierarchy = 0;

                uno::Reference<sheet::XLevelsSupplier> xHierSupp(xHiers->getByIndex(nHierarchy),
                                                                 uno::UNO_QUERY);
                if ( xHierSupp.is() )
                {
                    uno::Reference<container::XIndexAccess> xLevels = new ScNameToIndexAccess( xHierSupp->getLevels() );
                    sal_Int32 nLevCount = xLevels->getCount();
                    for (sal_Int32 nLev=0; nLev<nLevCount; nLev++)
                    {
                        uno::Reference<uno::XInterface> xLevel(xLevels->getByIndex(nLev),
                                                               uno::UNO_QUERY);
                        uno::Reference<container::XNamed> xLevNam( xLevel, uno::UNO_QUERY );
                        uno::Reference<sheet::XMembersSupplier> xLevSupp( xLevel, uno::UNO_QUERY );
                        if ( xLevNam.is() && xLevSupp.is() )
                        {
                            uno::Reference<sheet::XMembersAccess> xMembers = xLevSupp->getMembers();

                            OUString aFieldName( xLevNam->getName() );
                            // getElementNames() and getLocaleIndependentElementNames()
                            // must be consecutive calls to obtain strings in matching order.
                            uno::Sequence<OUString> aMemberValueNames( xMembers->getElementNames() );
                            uno::Sequence<OUString> aMemberValues( xMembers->getLocaleIndependentElementNames() );

                            aFieldNames.push_back( aFieldName );
                            aFieldValueNames.push_back( aMemberValueNames );
                            aFieldValues.push_back( aMemberValues );
                        }
                    }
                }
            }
        }
    }

    // compare and build filters

    SCSIZE nDataFields = aDataNames.size();
    SCSIZE nFieldCount = aFieldNames.size();
    OSL_ENSURE( aGivenNames.size() == nDataFields && aFieldValueNames.size() == nFieldCount &&
            aFieldValues.size() == nFieldCount, "wrong count" );

    bool bError = false;
    bool bHasData = false;
    OUString aRemaining(comphelper::string::strip(rFilterList, ' '));
    while (!aRemaining.isEmpty() && !bError)
    {
        bool bUsed = false;

        // look for data field name

        for ( SCSIZE nDataPos=0; nDataPos<nDataFields && !bUsed; nDataPos++ )
        {
            OUString aFound;
            sal_Int32 nMatched = 0;
            if (isAtStart(aRemaining, aDataNames[nDataPos], nMatched, false, nullptr))
                aFound = aDataNames[nDataPos];
            else if (isAtStart(aRemaining, aGivenNames[nDataPos], nMatched, false, nullptr))
                aFound = aGivenNames[nDataPos];

            if (!aFound.isEmpty())
            {
                rDataFieldName = aFound;
                aRemaining = aRemaining.copy(nMatched);
                bHasData = true;
                bUsed = true;
            }
        }

        // look for field name

        OUString aSpecField;
        bool bHasFieldName = false;
        if ( !bUsed )
        {
            sal_Int32 nMatched = 0;
            for ( SCSIZE nField=0; nField<nFieldCount && !bHasFieldName; nField++ )
            {
                if (isAtStart(aRemaining, aFieldNames[nField], nMatched, true, nullptr))
                {
                    aSpecField = aFieldNames[nField];
                    aRemaining = aRemaining.copy(nMatched);
                    aRemaining = comphelper::string::stripStart(aRemaining, ' ');

                    // field name has to be followed by item name in brackets
                    if (aRemaining.startsWith("["))
                    {
                        bHasFieldName = true;
                        // bUsed remains false - still need the item
                    }
                    else
                    {
                        bUsed = true;
                        bError = true;
                    }
                }
            }
        }

        // look for field item

        if ( !bUsed )
        {
            bool bItemFound = false;
            sal_Int32 nMatched = 0;
            OUString aFoundName;
            OUString aFoundValueName;
            OUString aFoundValue;
            sal_Int16 eFunc = sheet::GeneralFunction2::NONE;
            sal_Int16 eFoundFunc = sheet::GeneralFunction2::NONE;

            OUString aQueryValueName;
            const bool bHasQuery = extractAtStart( aRemaining, nMatched, false, &eFunc, aQueryValueName);

            OUString aQueryValue = aQueryValueName;
            if (mpTableData)
            {
                ScInterpreterContext& rContext = mpTableData->GetCacheTable().getCache().GetInterpreterContext();
                // Parse possible number from aQueryValueName and format
                // locale independent as aQueryValue.
                sal_uInt32 nNumFormat = 0;
                double fValue;
                if (rContext.NFIsNumberFormat(aQueryValueName, nNumFormat, fValue))
                    aQueryValue = ScDPCache::GetLocaleIndependentFormattedString(fValue, rContext, nNumFormat);
            }

            for ( SCSIZE nField=0; nField<nFieldCount; nField++ )
            {
                // If a field name is given, look in that field only, otherwise in all fields.
                // aSpecField is initialized from aFieldNames array, so exact comparison can be used.
                if ( !bHasFieldName || aFieldNames[nField] == aSpecField )
                {
                    const uno::Sequence<OUString>& rItemNames = aFieldValueNames[nField];
                    const uno::Sequence<OUString>& rItemValues = aFieldValues[nField];
                    sal_Int32 nItemCount = rItemNames.getLength();
                    assert(nItemCount == rItemValues.getLength());
                    const OUString* pItemNamesArr = rItemNames.getConstArray();
                    const OUString* pItemValuesArr = rItemValues.getConstArray();
                    for ( sal_Int32 nItem=0; nItem<nItemCount; nItem++ )
                    {
                        bool bThisItemFound;
                        if (bHasQuery)
                        {
                            // First check given value name against both.
                            bThisItemFound = ScGlobal::GetTransliteration().isEqual(
                                    aQueryValueName, pItemNamesArr[nItem]);
                            if (!bThisItemFound && pItemValuesArr[nItem] != pItemNamesArr[nItem])
                                bThisItemFound = ScGlobal::GetTransliteration().isEqual(
                                        aQueryValueName, pItemValuesArr[nItem]);
                            if (!bThisItemFound && aQueryValueName != aQueryValue)
                            {
                                // Second check locale independent value
                                // against both.
                                /* TODO: or check only value string against
                                 * value string, not against the value name? */

--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=94 H=95 G=94

¤ Dauer der Verarbeitung: 0.21 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.