/* -*- 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 <datauno.hxx>
 
#include <svl/hint.hxx>
#include <svl/numformat.hxx>
#include <svl/sharedstringpool.hxx>
#include <utility>
#include <vcl/svapp.hxx>
#include <unotools/charclass.hxx>
#include <osl/diagnose.h>
 
#include <com/sun/star/awt/XBitmap.hpp>
#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
#include <com/sun/star/util/SortField.hpp>
#include <com/sun/star/table/TableSortField.hpp>
#include <com/sun/star/beans/PropertyAttribute.hpp>
#include <com/sun/star/table/TableOrientation.hpp>
#include <com/sun/star/table/CellRangeAddress.hpp>
#include <com/sun/star/sheet/DataImportMode.hpp>
#include <com/sun/star/sheet/FilterFieldType.hpp>
#include <com/sun/star/sheet/FilterOperator2.hpp>
#include <com/sun/star/sheet/TableFilterField2.hpp>
 
#include <dapiuno.hxx>
#include <cellsuno.hxx>
#include <miscuno.hxx>
#include <targuno.hxx>
#include <rangeutl.hxx>
#include <dbdata.hxx>
#include <docsh.hxx>
#include <dbdocfun.hxx>
#include <unonames.hxx>
#include <globalnames.hxx>
#include <convuno.hxx>
#include <hints.hxx>
#include <attrib.hxx>
#include <dpshttab.hxx>
#include <queryentry.hxx>
#include <dputil.hxx>
#include <sortparam.hxx>
#include <dpobject.hxx>
#include <filterentries.hxx>
 
#include <comphelper/extract.hxx>
#include <cppuhelper/supportsservice.hxx>
#include <svx/dataaccessdescriptor.hxx>
 
#include <memory>
 
using namespace com::sun::star;
using namespace css::sheet;
 
//  everything without Which-ID, map only for PropertySetInfo
 
static std::span<const SfxItemPropertyMapEntry> lcl_GetSubTotalPropertyMap()
{
    // some old property names are for 5.2 compatibility
 
    static const SfxItemPropertyMapEntry aSubTotalPropertyMap_Impl[] =
    {
        { SC_UNONAME_BINDFMT,  0,  cppu::UnoType<bool>::get(),       0, 0},
        { SC_UNONAME_CASE,     0,  cppu::UnoType<bool>::get(),       0, 0},
        { SC_UNONAME_ENABSORT, 0,  cppu::UnoType<bool>::get(),       0, 0},
        { SC_UNONAME_ENUSLIST, 0,  cppu::UnoType<bool>::get(),       0, 0},
        { SC_UNONAME_FORMATS,  0,  cppu::UnoType<bool>::get(),       0, 0},
        { SC_UNONAME_INSBRK,   0,  cppu::UnoType<bool>::get(),       0, 0},
        { SC_UNONAME_ISCASE,   0,  cppu::UnoType<bool>::get(),       0, 0},
        { SC_UNONAME_MAXFLD,   0,  cppu::UnoType<sal_Int32>::get(), beans::PropertyAttribute::READONLY, 0},
        { SC_UNONAME_SORTASC,  0,  cppu::UnoType<bool>::get(),       0, 0},
        { SC_UNONAME_ULIST,    0,  cppu::UnoType<bool>::get(),       0, 0},
        { SC_UNONAME_UINDEX,   0,  cppu::UnoType<sal_Int32>::get(), 0, 0},
        { SC_UNONAME_USINDEX,  0,  cppu::UnoType<sal_Int32>::get(), 0, 0},
    };
    return aSubTotalPropertyMap_Impl;
}
 
static std::span<const SfxItemPropertyMapEntry> lcl_GetFilterPropertyMap()
{
    static const SfxItemPropertyMapEntry aFilterPropertyMap_Impl[] =
    {
        { SC_UNONAME_CONTHDR,  0,  cppu::UnoType<bool>::get(),                      0, 0},
        { SC_UNONAME_COPYOUT,  0,  cppu::UnoType<bool>::get(),                      0, 0},
        { SC_UNONAME_ISCASE,   0,  cppu::UnoType<bool>::get(),                      0, 0},
        { SC_UNONAME_MAXFLD,   0,  cppu::UnoType<sal_Int32>::get(),                beans::PropertyAttribute::READONLY, 0},
        { SC_UNONAME_ORIENT,   0,  cppu::UnoType<table::TableOrientation>::get(),  0, 0},
        { SC_UNONAME_OUTPOS,   0,  cppu::UnoType<table::CellAddress>::get(),       0, 0},
        { SC_UNONAME_SAVEOUT,  0,  cppu::UnoType<bool>::get(),                      0, 0},
        { SC_UNONAME_SKIPDUP,  0,  cppu::UnoType<bool>::get(),                      0, 0},
        { SC_UNONAME_USEREGEX, 0,  cppu::UnoType<bool>::get(),                      0, 0},
    };
    return aFilterPropertyMap_Impl;
}
 
static std::span<const SfxItemPropertyMapEntry> lcl_GetDBRangePropertyMap()
{
    static const SfxItemPropertyMapEntry aDBRangePropertyMap_Impl[] =
    {
        { SC_UNONAME_AUTOFLT,  0,  cppu::UnoType<bool>::get(),                      0, 0},
        { SC_UNONAME_FLTCRT,   0,  cppu::UnoType<table::CellRangeAddress>::get(),  0, 0},
        { SC_UNONAME_FROMSELECT,0, cppu::UnoType<bool>::get(),                      0, 0},
        { SC_UNONAME_ISUSER,   0,  cppu::UnoType<bool>::get(),           beans::PropertyAttribute::READONLY, 0 },
        { SC_UNONAME_KEEPFORM, 0,  cppu::UnoType<bool>::get(),                      0, 0},
        { SC_UNO_LINKDISPBIT,  0,  cppu::UnoType<awt::XBitmap>::get(), beans::PropertyAttribute::READONLY, 0 },
        { SC_UNO_LINKDISPNAME, 0,  cppu::UnoType<OUString>::get(), beans::PropertyAttribute::READONLY, 0 },
        { SC_UNONAME_MOVCELLS, 0,  cppu::UnoType<bool>::get(),                      0, 0},
        { SC_UNONAME_REFPERIOD, 0, cppu::UnoType<sal_Int32>::get(),                0, 0},
        { SC_UNONAME_STRIPDAT, 0,  cppu::UnoType<bool>::get(),                      0, 0},
        { SC_UNONAME_TOKENINDEX,0, cppu::UnoType<sal_Int32>::get(),     beans::PropertyAttribute::READONLY, 0 },
        { SC_UNONAME_USEFLTCRT,0,  cppu::UnoType<bool>::get(),                      0, 0},
        { SC_UNONAME_TOTALSROW,0,  cppu::UnoType<bool>::get(),                      0, 0},
        { SC_UNONAME_CONTHDR  ,0,  cppu::UnoType<bool>::get(),                      0, 0},
    };
    return aDBRangePropertyMap_Impl;
}
 
SC_SIMPLE_SERVICE_INFO( ScConsolidationDescriptor, u"ScConsolidationDescriptor"_ustr, u"com.sun.star.sheet.ConsolidationDescriptor"_ustr )
SC_SIMPLE_SERVICE_INFO( ScDatabaseRangesObj, u"ScDatabaseRangesObj"_ustr, u"com.sun.star.sheet.DatabaseRanges"_ustr )
SC_SIMPLE_SERVICE_INFO( ScFilterDescriptorBase, u"ScFilterDescriptorBase"_ustr, u"com.sun.star.sheet.SheetFilterDescriptor"_ustr )
SC_SIMPLE_SERVICE_INFO( ScSubTotalDescriptorBase, u"ScSubTotalDescriptorBase"_ustr, u"com.sun.star.sheet.SubTotalDescriptor"_ustr )
SC_SIMPLE_SERVICE_INFO( ScSubTotalFieldObj, u"ScSubTotalFieldObj"_ustr, u"com.sun.star.sheet.SubTotalField"_ustr )
 
sheet::GeneralFunction  ScDataUnoConversion::SubTotalToGeneral( ScSubTotalFunc eSubTotal )
{
    sheet::GeneralFunction eGeneral;
    switch (eSubTotal)
    {
        case SUBTOTAL_FUNC_NONE: eGeneral = sheet::GeneralFunction_NONE;      break;
        case SUBTOTAL_FUNC_AVE:  eGeneral = sheet::GeneralFunction_AVERAGE;   break;
        case SUBTOTAL_FUNC_CNT:  eGeneral = sheet::GeneralFunction_COUNTNUMS; break;
        case SUBTOTAL_FUNC_CNT2: eGeneral = sheet::GeneralFunction_COUNT;     break;
        case SUBTOTAL_FUNC_MAX:  eGeneral = sheet::GeneralFunction_MAX;       break;
        case SUBTOTAL_FUNC_MIN:  eGeneral = sheet::GeneralFunction_MIN;       break;
        case SUBTOTAL_FUNC_PROD: eGeneral = sheet::GeneralFunction_PRODUCT;   break;
        case SUBTOTAL_FUNC_STD:  eGeneral = sheet::GeneralFunction_STDEV;     break;
        case SUBTOTAL_FUNC_STDP: eGeneral = sheet::GeneralFunction_STDEVP;    break;
        case SUBTOTAL_FUNC_SUM:  eGeneral = sheet::GeneralFunction_SUM;       break;
        case SUBTOTAL_FUNC_VAR:  eGeneral = sheet::GeneralFunction_VAR;       break;
        case SUBTOTAL_FUNC_VARP: eGeneral = sheet::GeneralFunction_VARP;      break;
        default:
            OSL_FAIL("SubTotalToGeneral: wrong enum");
            eGeneral = sheet::GeneralFunction_NONE;
            break;
    }
    return eGeneral;
}
 
void ScImportDescriptor::FillProperties( uno::Sequence<beans::PropertyValue>& rSeq, const ScImportParam& rParam )
{
    OSL_ENSURE( rSeq.getLength() == GetPropertyCount(), "wrong Count" );
 
    beans::PropertyValue* pArray = rSeq.getArray();
 
    sheet::DataImportMode eMode = sheet::DataImportMode_NONE;
    if ( rParam.bImport )
    {
        if ( rParam.bSql )
            eMode = sheet::DataImportMode_SQL;
        else if ( rParam.nType == ScDbQuery )
            eMode = sheet::DataImportMode_QUERY;
        else
            eMode = sheet::DataImportMode_TABLE;        // type always ScDbQuery or ScDbTable
    }
 
    svx::ODataAccessDescriptor aDescriptor;
    aDescriptor.setDataSource(rParam.aDBName);
    if (aDescriptor.has( svx::DataAccessDescriptorProperty::DataSource ))
    {
        pArray[0].Name = SC_UNONAME_DBNAME;
        pArray[0].Value <<= rParam.aDBName;
    }
    else if (aDescriptor.has( svx::DataAccessDescriptorProperty::ConnectionResource ))
    {
        pArray[0].Name = SC_UNONAME_CONRES;
        pArray[0].Value <<= rParam.aDBName;
    }
 
    pArray[1].Name = SC_UNONAME_SRCTYPE;
    pArray[1].Value <<= eMode;
 
    pArray[2].Name = SC_UNONAME_SRCOBJ;
    pArray[2].Value <<= rParam.aStatement;
 
    pArray[3].Name = SC_UNONAME_ISNATIVE;
    pArray[3].Value <<= rParam.bNative;
}
 
void ScImportDescriptor::FillImportParam( ScImportParam& rParam, const uno::Sequence<beans::PropertyValue>& rSeq )
{
    OUString aStrVal;
    for (const beans::PropertyValue& rProp : rSeq)
    {
        OUString aPropName(rProp.Name);
 
        if (aPropName == SC_UNONAME_ISNATIVE)
            rParam.bNative = ScUnoHelpFunctions::GetBoolFromAny( rProp.Value );
        else if (aPropName == SC_UNONAME_DBNAME)
        {
            if ( rProp.Value >>= aStrVal )
                rParam.aDBName = aStrVal;
        }
        else if (aPropName == SC_UNONAME_CONRES)
        {
            if ( rProp.Value >>= aStrVal )
                rParam.aDBName = aStrVal;
        }
        else if (aPropName == SC_UNONAME_SRCOBJ)
        {
            if ( rProp.Value >>= aStrVal )
                rParam.aStatement = aStrVal;
        }
        else if (aPropName == SC_UNONAME_SRCTYPE)
        {
            //! test for correct enum type?
            sheet::DataImportMode eMode = static_cast<sheet::DataImportMode>(ScUnoHelpFunctions::GetEnumFromAny( rProp.Value ));
            switch (eMode)
            {
                case sheet::DataImportMode_NONE:
                    rParam.bImport = false;
                    break;
                case sheet::DataImportMode_SQL:
                    rParam.bImport = true;
                    rParam.bSql    = true;
                    break;
                case sheet::DataImportMode_TABLE:
                    rParam.bImport = true;
                    rParam.bSql    = false;
                    rParam.nType   = ScDbTable;
                    break;
                case sheet::DataImportMode_QUERY:
                    rParam.bImport = true;
                    rParam.bSql    = false;
                    rParam.nType   = ScDbQuery;
                    break;
                default:
                    OSL_FAIL("wrong mode");
                    rParam.bImport = false;
            }
        }
    }
}
 
void ScSortDescriptor::FillProperties( uno::Sequence<beans::PropertyValue>& rSeq, const ScSortParam& rParam )
{
    OSL_ENSURE( rSeq.getLength() == GetPropertyCount(), "wrong count" );
 
    beans::PropertyValue* pArray = rSeq.getArray();
 
    //  gather Uno values together
 
    table::CellAddress aOutPos;
    aOutPos.Sheet  = rParam.nDestTab;
    aOutPos.Column = rParam.nDestCol;
    aOutPos.Row    = rParam.nDestRow;
 
    sal_uInt16 nSortCount = 0;
    while ( nSortCount < rParam.GetSortKeyCount() && rParam.maKeyState[nSortCount].bDoSort )
        ++nSortCount;
 
    uno::Sequence<table::TableSortField> aFields(nSortCount);
    if (nSortCount)
    {
        table::TableSortField* pFieldArray = aFields.getArray();
        for (sal_uInt16 i=0; i<nSortCount; i++)
        {
            pFieldArray[i].Field         = rParam.maKeyState[i].nField;
            pFieldArray[i].IsAscending   = rParam.maKeyState[i].bAscending;
            pFieldArray[i].FieldType     = table::TableSortFieldType_AUTOMATIC;     // always automatic
            pFieldArray[i].IsCaseSensitive = rParam.bCaseSens;
            pFieldArray[i].CollatorLocale = rParam.aCollatorLocale;
            pFieldArray[i].CollatorAlgorithm = rParam.aCollatorAlgorithm;
        }
    }
 
    //  fill the sequence
 
    pArray[0].Name = SC_UNONAME_ISSORTCOLUMNS;
    pArray[0].Value <<= !rParam.bByRow;
 
    pArray[1].Name = SC_UNONAME_CONTHDR;
    pArray[1].Value <<= rParam.bHasHeader;
 
    pArray[2].Name = SC_UNONAME_MAXFLD;
    pArray[2].Value <<= static_cast<sal_Int32>( rParam.GetSortKeyCount() );
 
    pArray[3].Name = SC_UNONAME_SORTFLD;
    pArray[3].Value <<= aFields;
 
    pArray[4].Name = SC_UNONAME_BINDFMT;
    pArray[4].Value <<= rParam.aDataAreaExtras.mbCellFormats;
 
    pArray[5].Name = SC_UNONAME_COPYOUT;
    pArray[5].Value <<= !rParam.bInplace;
 
    pArray[6].Name = SC_UNONAME_OUTPOS;
    pArray[6].Value <<= aOutPos;
 
    pArray[7].Name = SC_UNONAME_ISULIST;
    pArray[7].Value <<= rParam.bUserDef;
 
    pArray[8].Name = SC_UNONAME_UINDEX;
    pArray[8].Value <<= static_cast<sal_Int32>( rParam.nUserIndex );
}
 
void ScSortDescriptor::FillSortParam( ScSortParam& rParam, const uno::Sequence<beans::PropertyValue>& rSeq )
{
    sal_Int32 nSortSize = static_cast<sal_Int32>(rParam.GetSortKeyCount());
 
    for (const beans::PropertyValue& rProp : rSeq)
    {
        OUString aPropName(rProp.Name);
 
        if (aPropName == SC_UNONAME_ORIENT)
        {
            //! test for correct enum type?
            table::TableOrientation eOrient = static_cast<table::TableOrientation>(ScUnoHelpFunctions::GetEnumFromAny( rProp.Value ));
            rParam.bByRow = ( eOrient != table::TableOrientation_COLUMNS );
        }
        else if (aPropName == SC_UNONAME_ISSORTCOLUMNS)
        {
            rParam.bByRow = !::cppu::any2bool(rProp.Value);
        }
        else if (aPropName == SC_UNONAME_CONTHDR)
            rParam.bHasHeader = ScUnoHelpFunctions::GetBoolFromAny( rProp.Value );
        else if (aPropName == SC_UNONAME_MAXFLD)
        {
            sal_Int32 nVal;
            if ( (rProp.Value >>= nVal) && nVal > nSortSize )
            {
                //! specify exceptions
                //! throw lang::IllegalArgumentException();
            }
        }
        else if (aPropName == SC_UNONAME_SORTFLD)
        {
            uno::Sequence<util::SortField> aSeq;
            uno::Sequence<table::TableSortField> aNewSeq;
            if ( rProp.Value >>= aSeq )
            {
                sal_Int32 nCount = aSeq.getLength();
                sal_Int32 i;
                if ( nCount > static_cast<sal_Int32>( rParam.GetSortKeyCount() ) )
                {
                    // tdf#105301 - increase the size of the sorting keys
                    nSortSize = nCount;
                    rParam.maKeyState.resize(nCount);
                }
                const util::SortField* pFieldArray = aSeq.getConstArray();
                for (i=0; i<nCount; i++)
                {
                    rParam.maKeyState[i].nField     = static_cast<SCCOLROW>( pFieldArray[i].Field );
                    rParam.maKeyState[i].bAscending = pFieldArray[i].SortAscending;
 
                    // FieldType is ignored
                    rParam.maKeyState[i].bDoSort = true;
                }
                for (i=nCount; i<nSortSize; i++)
                    rParam.maKeyState[i].bDoSort = false;
            }
            else if ( rProp.Value >>= aNewSeq )
            {
                sal_Int32 nCount = aNewSeq.getLength();
                sal_Int32 i;
                if ( nCount > nSortSize )
                {
                    nCount = nSortSize;
                    rParam.maKeyState.resize(nCount);
                }
                const table::TableSortField* pFieldArray = aNewSeq.getConstArray();
                for (i=0; i<nCount; i++)
                {
                    rParam.maKeyState[i].nField     = static_cast<SCCOLROW>( pFieldArray[i].Field );
                    rParam.maKeyState[i].bAscending = pFieldArray[i].IsAscending;
 
                    // only one is possible, sometime we should make it possible to have different for every entry
                    rParam.bCaseSens = pFieldArray[i].IsCaseSensitive;
                    rParam.aCollatorLocale = pFieldArray[i].CollatorLocale;
                    rParam.aCollatorAlgorithm = pFieldArray[i].CollatorAlgorithm;
 
                    // FieldType is ignored
                    rParam.maKeyState[i].bDoSort = true;
                }
                for (i=nCount; i<nSortSize; i++)
                    rParam.maKeyState[i].bDoSort = false;
            }
        }
        else if (aPropName == SC_UNONAME_ISCASE)
        {
            rParam.bCaseSens = ScUnoHelpFunctions::GetBoolFromAny( rProp.Value );
        }
        else if (aPropName == SC_UNONAME_BINDFMT)
            rParam.aDataAreaExtras.mbCellFormats = ScUnoHelpFunctions::GetBoolFromAny( rProp.Value );
        else if (aPropName == SC_UNONAME_COPYOUT)
            rParam.bInplace = !ScUnoHelpFunctions::GetBoolFromAny( rProp.Value );
        else if (aPropName == SC_UNONAME_OUTPOS)
        {
            table::CellAddress aAddress;
            if ( rProp.Value >>= aAddress )
            {
                rParam.nDestTab = aAddress.Sheet;
                rParam.nDestCol = static_cast<SCCOL>(aAddress.Column);
                rParam.nDestRow = static_cast<SCROW>(aAddress.Row);
            }
        }
        else if (aPropName == SC_UNONAME_ISULIST)
            rParam.bUserDef = ScUnoHelpFunctions::GetBoolFromAny( rProp.Value );
        else if (aPropName == SC_UNONAME_UINDEX)
        {
            sal_Int32 nVal = 0;
            if ( rProp.Value >>= nVal )
                rParam.nUserIndex = static_cast<sal_uInt16>(nVal);
        }
        else if (aPropName == SC_UNONAME_COLLLOC)
        {
            rProp.Value >>= rParam.aCollatorLocale;
        }
        else if (aPropName == SC_UNONAME_COLLALG)
        {
            OUString sStr;
            if ( rProp.Value >>= sStr )
                rParam.aCollatorAlgorithm = sStr;
        }
    }
}
 
ScSubTotalFieldObj::ScSubTotalFieldObj( ScSubTotalDescriptorBase* pDesc, sal_uInt16 nP ) :
    xParent( pDesc ),
    nPos( nP )
{
    OSL_ENSURE(pDesc, "ScSubTotalFieldObj: Parent is 0");
}
 
ScSubTotalFieldObj::~ScSubTotalFieldObj()
{
}
 
// XSubTotalField
 
sal_Int32 SAL_CALL ScSubTotalFieldObj::getGroupColumn()
{
    SolarMutexGuard aGuard;
    ScSubTotalParam aParam;
    xParent->GetData(aParam);
 
    return aParam.nField[nPos];
}
 
void SAL_CALL ScSubTotalFieldObj::setGroupColumn( sal_Int32 nGroupColumn )
{
    SolarMutexGuard aGuard;
    ScSubTotalParam aParam;
    xParent->GetData(aParam);
 
    aParam.nField[nPos] = static_cast<SCCOL>(nGroupColumn);
 
    xParent->PutData(aParam);
}
 
uno::Sequence<sheet::SubTotalColumn> SAL_CALL ScSubTotalFieldObj::getSubTotalColumns()
{
    SolarMutexGuard aGuard;
    ScSubTotalParam aParam;
    xParent->GetData(aParam);
 
    SCCOL nCount = aParam.nSubTotals[nPos];
    uno::Sequence<sheet::SubTotalColumn> aSeq(nCount);
    sheet::SubTotalColumn* pAry = aSeq.getArray();
    for (SCCOL i=0; i<nCount; i++)
    {
        pAry[i].Column = aParam.pSubTotals[nPos][i];
        pAry[i].Function = ScDataUnoConversion::SubTotalToGeneral(
                                        aParam.pFunctions[nPos][i] );
    }
    return aSeq;
}
 
void SAL_CALL ScSubTotalFieldObj::setSubTotalColumns(
                            const uno::Sequence<sheet::SubTotalColumn>& aSubTotalColumns )
{
    SolarMutexGuard aGuard;
    ScSubTotalParam aParam;
    xParent->GetData(aParam);
 
    sal_uInt32 nColCount = aSubTotalColumns.getLength();
    if ( nColCount <= sal::static_int_cast<sal_uInt32>(SCCOL_MAX) )
    {
        SCCOL nCount = static_cast<SCCOL>(nColCount);
        aParam.nSubTotals[nPos] = nCount;
        if (nCount != 0)
        {
            aParam.pSubTotals[nPos].reset(new SCCOL[nCount]);
            aParam.pFunctions[nPos].reset(new ScSubTotalFunc[nCount]);
 
            const sheet::SubTotalColumn* pAry = aSubTotalColumns.getConstArray();
            for (SCCOL i=0; i<nCount; i++)
            {
                aParam.pSubTotals[nPos][i] = static_cast<SCCOL>(pAry[i].Column);
                aParam.pFunctions[nPos][i] = ScDPUtil::toSubTotalFunc(static_cast<ScGeneralFunction>(pAry[i].Function));
            }
        }
        else
        {
            aParam.pSubTotals[nPos].reset();
            aParam.pFunctions[nPos].reset();
        }
    }
    //! otherwise exception or so? (too many columns)
 
    xParent->PutData(aParam);
}
 
ScSubTotalDescriptorBase::ScSubTotalDescriptorBase() :
    aPropSet( lcl_GetSubTotalPropertyMap() )
{
}
 
ScSubTotalDescriptorBase::~ScSubTotalDescriptorBase()
{
}
 
// XSubTotalDescriptor
 
rtl::Reference<ScSubTotalFieldObj> ScSubTotalDescriptorBase::GetObjectByIndex_Impl(sal_uInt16 nIndex)
{
    if ( nIndex < getCount() )
        return new ScSubTotalFieldObj( this, nIndex );
    return nullptr;
}
 
void SAL_CALL ScSubTotalDescriptorBase::clear()
{
    SolarMutexGuard aGuard;
    ScSubTotalParam aParam;
    GetData(aParam);
 
    for (bool & rn : aParam.bGroupActive)
        rn = false;
 
    //! notify the field objects???
 
    PutData(aParam);
}
 
void SAL_CALL ScSubTotalDescriptorBase::addNew(
                        const uno::Sequence<sheet::SubTotalColumn>& aSubTotalColumns,
                        sal_Int32 nGroupColumn )
{
    SolarMutexGuard aGuard;
    ScSubTotalParam aParam;
    GetData(aParam);
 
    sal_uInt16 nPos = 0;
    while ( nPos < MAXSUBTOTAL && aParam.bGroupActive[nPos] )
        ++nPos;
 
    sal_uInt32 nColCount = aSubTotalColumns.getLength();
 
    if ( nPos >= MAXSUBTOTAL || nColCount > sal::static_int_cast<sal_uInt32>(SCCOL_MAX) )
        // too many fields / columns
        throw uno::RuntimeException();      // no other exceptions specified
 
    aParam.bGroupActive[nPos] = true;
    aParam.nField[nPos] = static_cast<SCCOL>(nGroupColumn);
 
    aParam.pSubTotals[nPos].reset();
    aParam.pFunctions[nPos].reset();
 
    SCCOL nCount = static_cast<SCCOL>(nColCount);
    aParam.nSubTotals[nPos] = nCount;
    if (nCount != 0)
    {
        aParam.pSubTotals[nPos].reset(new SCCOL[nCount]);
        aParam.pFunctions[nPos].reset(new ScSubTotalFunc[nCount]);
 
        const sheet::SubTotalColumn* pAry = aSubTotalColumns.getConstArray();
        for (SCCOL i=0; i<nCount; i++)
        {
            aParam.pSubTotals[nPos][i] = static_cast<SCCOL>(pAry[i].Column);
            aParam.pFunctions[nPos][i] = ScDPUtil::toSubTotalFunc(static_cast<ScGeneralFunction>(pAry[i].Function));
        }
    }
    else
    {
        aParam.pSubTotals[nPos].reset();
        aParam.pFunctions[nPos].reset();
    }
 
    PutData(aParam);
}
 
//  flags/settings as properties
 
// XEnumerationAccess
 
uno::Reference<container::XEnumeration> SAL_CALL ScSubTotalDescriptorBase::createEnumeration()
{
    SolarMutexGuard aGuard;
    return new ScIndexEnumeration(this, u"com.sun.star.sheet.SubTotalFieldsEnumeration"_ustr);
}
 
// XIndexAccess
 
sal_Int32 SAL_CALL ScSubTotalDescriptorBase::getCount()
{
    SolarMutexGuard aGuard;
    ScSubTotalParam aParam;
    GetData(aParam);
 
    sal_uInt16 nCount = 0;
    while ( nCount < MAXSUBTOTAL && aParam.bGroupActive[nCount] )
        ++nCount;
    return nCount;
}
 
uno::Any SAL_CALL ScSubTotalDescriptorBase::getByIndex( sal_Int32 nIndex )
{
    SolarMutexGuard aGuard;
    uno::Reference<sheet::XSubTotalField> xField(GetObjectByIndex_Impl(static_cast<sal_uInt16>(nIndex)));
    if (!xField.is())
        throw lang::IndexOutOfBoundsException();
 
    return uno::Any(xField);
}
 
uno::Type SAL_CALL ScSubTotalDescriptorBase::getElementType()
{
    return cppu::UnoType<sheet::XSubTotalField>::get();
}
 
sal_Bool SAL_CALL ScSubTotalDescriptorBase::hasElements()
{
    SolarMutexGuard aGuard;
    return ( getCount() != 0 );
}
 
// XPropertySet
 
uno::Reference<beans::XPropertySetInfo> SAL_CALL ScSubTotalDescriptorBase::getPropertySetInfo()
{
    SolarMutexGuard aGuard;
    static uno::Reference<beans::XPropertySetInfo> aRef(
        new SfxItemPropertySetInfo( aPropSet.getPropertyMap() ));
    return aRef;
}
 
void SAL_CALL ScSubTotalDescriptorBase::setPropertyValue(
                        const OUString& aPropertyName, const uno::Any& aValue )
{
    SolarMutexGuard aGuard;
    ScSubTotalParam aParam;
    GetData(aParam);
 
    // some old property names are for 5.2 compatibility
 
    if (aPropertyName == SC_UNONAME_CASE || aPropertyName == SC_UNONAME_ISCASE )
        aParam.bCaseSens = ScUnoHelpFunctions::GetBoolFromAny( aValue );
    else if (aPropertyName == SC_UNONAME_FORMATS || aPropertyName == SC_UNONAME_BINDFMT )
        aParam.bIncludePattern = ScUnoHelpFunctions::GetBoolFromAny( aValue );
    else if (aPropertyName == SC_UNONAME_ENABSORT )
        aParam.bDoSort = ScUnoHelpFunctions::GetBoolFromAny( aValue );
    else if (aPropertyName == SC_UNONAME_SORTASC )
        aParam.bAscending = ScUnoHelpFunctions::GetBoolFromAny( aValue );
    else if (aPropertyName == SC_UNONAME_INSBRK )
        aParam.bPagebreak = ScUnoHelpFunctions::GetBoolFromAny( aValue );
    else if (aPropertyName == SC_UNONAME_ULIST || aPropertyName == SC_UNONAME_ENUSLIST )
        aParam.bUserDef = ScUnoHelpFunctions::GetBoolFromAny( aValue );
    else if (aPropertyName == SC_UNONAME_UINDEX || aPropertyName == SC_UNONAME_USINDEX )
    {
        sal_Int32 nVal = 0;
        if ( aValue >>= nVal )
            aParam.nUserIndex = static_cast<sal_uInt16>(nVal);
    }
    else if (aPropertyName == SC_UNONAME_MAXFLD )
    {
        sal_Int32 nVal = 0;
        if ( (aValue >>= nVal) && nVal > sal::static_int_cast<sal_Int32>(MAXSUBTOTAL) )
        {
            throw lang::IllegalArgumentException();
        }
    }
 
    PutData(aParam);
}
 
uno::Any SAL_CALL ScSubTotalDescriptorBase::getPropertyValue( const OUString& aPropertyName )
{
    SolarMutexGuard aGuard;
    ScSubTotalParam aParam;
    GetData(aParam);
 
    uno::Any aRet;
 
    // some old property names are for 5.2 compatibility
 
    if (aPropertyName == SC_UNONAME_CASE || aPropertyName == SC_UNONAME_ISCASE )
        aRet <<= aParam.bCaseSens;
    else if (aPropertyName == SC_UNONAME_FORMATS || aPropertyName == SC_UNONAME_BINDFMT )
        aRet <<= aParam.bIncludePattern;
    else if (aPropertyName == SC_UNONAME_ENABSORT )
        aRet <<= aParam.bDoSort;
    else if (aPropertyName == SC_UNONAME_SORTASC )
        aRet <<= aParam.bAscending;
    else if (aPropertyName == SC_UNONAME_INSBRK )
        aRet <<= aParam.bPagebreak;
    else if (aPropertyName == SC_UNONAME_ULIST || aPropertyName == SC_UNONAME_ENUSLIST )
        aRet <<= aParam.bUserDef;
    else if (aPropertyName == SC_UNONAME_UINDEX || aPropertyName == SC_UNONAME_USINDEX )
        aRet <<= static_cast<sal_Int32>(aParam.nUserIndex);
    else if (aPropertyName == SC_UNONAME_MAXFLD )
        aRet <<= sal_Int32(MAXSUBTOTAL);
 
    return aRet;
}
 
SC_IMPL_DUMMY_PROPERTY_LISTENER( ScSubTotalDescriptorBase )
 
ScSubTotalDescriptor::ScSubTotalDescriptor()
{
}
 
ScSubTotalDescriptor::~ScSubTotalDescriptor()
{
}
 
void ScSubTotalDescriptor::GetData( ScSubTotalParam& rParam ) const
{
    rParam = aStoredParam;          // query for interface
}
 
void ScSubTotalDescriptor::PutData( const ScSubTotalParam& rParam )
{
    aStoredParam = rParam;          // set by the interface
}
 
void ScSubTotalDescriptor::SetParam( const ScSubTotalParam& rNew )
{
    aStoredParam = rNew;            // set from outside
}
 
ScRangeSubTotalDescriptor::ScRangeSubTotalDescriptor(ScDatabaseRangeObj* pPar) :
    mxParent(pPar)
{
}
 
ScRangeSubTotalDescriptor::~ScRangeSubTotalDescriptor()
{
}
 
void ScRangeSubTotalDescriptor::GetData( ScSubTotalParam& rParam ) const
{
    if (mxParent.is())
        mxParent->GetSubTotalParam( rParam );
}
 
void ScRangeSubTotalDescriptor::PutData( const ScSubTotalParam& rParam )
{
    if (mxParent.is())
        mxParent->SetSubTotalParam( rParam );
}
 
ScConsolidationDescriptor::ScConsolidationDescriptor()
{
}
 
ScConsolidationDescriptor::~ScConsolidationDescriptor()
{
}
 
void ScConsolidationDescriptor::SetParam( const ScConsolidateParam& rNew )
{
    aParam = rNew;
}
 
// XConsolidationDescriptor
 
sheet::GeneralFunction SAL_CALL ScConsolidationDescriptor::getFunction()
{
    SolarMutexGuard aGuard;
    return ScDataUnoConversion::SubTotalToGeneral(aParam.eFunction);
}
 
void SAL_CALL ScConsolidationDescriptor::setFunction( sheet::GeneralFunction nFunction )
{
    SolarMutexGuard aGuard;
    aParam.eFunction = ScDPUtil::toSubTotalFunc(static_cast<ScGeneralFunction>(nFunction));
}
 
uno::Sequence<table::CellRangeAddress> SAL_CALL ScConsolidationDescriptor::getSources()
{
    SolarMutexGuard aGuard;
    sal_uInt16 nCount = aParam.nDataAreaCount;
    if (!aParam.pDataAreas)
        nCount = 0;
    table::CellRangeAddress aRange;
    uno::Sequence<table::CellRangeAddress> aSeq(nCount);
    table::CellRangeAddress* pAry = aSeq.getArray();
    for (sal_uInt16 i=0; i<nCount; i++)
    {
        ScArea const & rArea = aParam.pDataAreas[i];
        aRange.Sheet        = rArea.nTab;
        aRange.StartColumn  = rArea.nColStart;
        aRange.StartRow     = rArea.nRowStart;
        aRange.EndColumn    = rArea.nColEnd;
        aRange.EndRow       = rArea.nRowEnd;
        pAry[i] = aRange;
    }
    return aSeq;
}
 
void SAL_CALL ScConsolidationDescriptor::setSources(
                    const uno::Sequence<table::CellRangeAddress>& aSources )
{
    SolarMutexGuard aGuard;
    sal_uInt16 nCount = static_cast<sal_uInt16>(aSources.getLength());
    if (nCount)
    {
        const table::CellRangeAddress* pAry = aSources.getConstArray();
        std::unique_ptr<ScArea[]> pNew(new ScArea[nCount]);
        sal_uInt16 i;
        for (i=0; i<nCount; i++)
            pNew[i] = ScArea( pAry[i].Sheet,
                    static_cast<SCCOL>(pAry[i].StartColumn), pAry[i].StartRow,
                    static_cast<SCCOL>(pAry[i].EndColumn),   pAry[i].EndRow );
 
        aParam.SetAreas( std::move(pNew), nCount );    // copy everything
    }
    else
        aParam.ClearDataAreas();
}
 
table::CellAddress SAL_CALL ScConsolidationDescriptor::getStartOutputPosition()
{
    SolarMutexGuard aGuard;
    table::CellAddress aPos;
    aPos.Column = aParam.nCol;
    aPos.Row    = aParam.nRow;
    aPos.Sheet  = aParam.nTab;
    return aPos;
}
 
void SAL_CALL ScConsolidationDescriptor::setStartOutputPosition(
                                const table::CellAddress& aStartOutputPosition )
{
    SolarMutexGuard aGuard;
    aParam.nCol = static_cast<SCCOL>(aStartOutputPosition.Column);
    aParam.nRow = static_cast<SCROW>(aStartOutputPosition.Row);
    aParam.nTab = aStartOutputPosition.Sheet;
}
 
sal_Bool SAL_CALL ScConsolidationDescriptor::getUseColumnHeaders()
{
    SolarMutexGuard aGuard;
    return aParam.bByCol;
}
 
void SAL_CALL ScConsolidationDescriptor::setUseColumnHeaders( sal_Bool bUseColumnHeaders )
{
    SolarMutexGuard aGuard;
    aParam.bByCol = bUseColumnHeaders;
}
 
sal_Bool SAL_CALL ScConsolidationDescriptor::getUseRowHeaders()
{
    SolarMutexGuard aGuard;
    return aParam.bByRow;
}
 
void SAL_CALL ScConsolidationDescriptor::setUseRowHeaders( sal_Bool bUseRowHeaders )
{
    SolarMutexGuard aGuard;
    aParam.bByRow = bUseRowHeaders;
}
 
sal_Bool SAL_CALL ScConsolidationDescriptor::getInsertLinks()
{
    SolarMutexGuard aGuard;
    return aParam.bReferenceData;
}
 
void SAL_CALL ScConsolidationDescriptor::setInsertLinks( sal_Bool bInsertLinks )
{
    SolarMutexGuard aGuard;
    aParam.bReferenceData = bInsertLinks;
}
 
ScFilterDescriptorBase::ScFilterDescriptorBase(ScDocShell* pDocShell) :
    aPropSet( lcl_GetFilterPropertyMap() ),
    pDocSh(pDocShell)
{
    if (pDocSh)
        pDocSh->GetDocument().AddUnoObject(*this);
}
 
ScFilterDescriptorBase::~ScFilterDescriptorBase()
{
    SolarMutexGuard g;
 
    if (pDocSh)
        pDocSh->GetDocument().RemoveUnoObject(*this);
}
 
void ScFilterDescriptorBase::Notify( SfxBroadcaster&, const SfxHint& rHint )
{
    if ( rHint.GetId() == SfxHintId::Dying )
    {
        pDocSh = nullptr;          // invalid
    }
}
 
// XSheetFilterDescriptor and XSheetFilterDescriptor2
 
uno::Sequence<sheet::TableFilterField> SAL_CALL ScFilterDescriptorBase::getFilterFields()
{
    SolarMutexGuard aGuard;
    ScQueryParam aParam;
    GetData(aParam);
 
    SCSIZE nEntries = aParam.GetEntryCount();   // allocated entries in Param
    SCSIZE nCount = 0;                          // active
    while ( nCount < nEntries &&
            aParam.GetEntry(nCount).bDoQuery )
        ++nCount;
 
    sheet::TableFilterField aField;
    uno::Sequence<sheet::TableFilterField> aSeq(static_cast<sal_Int32>(nCount));
    sheet::TableFilterField* pAry = aSeq.getArray();
    for (SCSIZE i=0; i<nCount; i++)
    {
        const ScQueryEntry& rEntry = aParam.GetEntry(i);
        if (rEntry.GetQueryItems().empty())
            continue;
 
        const ScQueryEntry::Item& rItem = rEntry.GetQueryItems().front();
 
        aField.Connection    = (rEntry.eConnect == SC_AND) ? sheet::FilterConnection_AND :
                                                             sheet::FilterConnection_OR;
        aField.Field         = rEntry.nField;
        aField.IsNumeric     = rItem.meType != ScQueryEntry::ByString;
        aField.StringValue   = rItem.maString.getString();
        aField.NumericValue  = rItem.mfVal;
 
        switch (rEntry.eOp)             // ScQueryOp
        {
            case SC_EQUAL:
                {
                    aField.Operator = sheet::FilterOperator_EQUAL;
                    if (rEntry.IsQueryByEmpty())
                    {
                        aField.Operator = sheet::FilterOperator_EMPTY;
                        aField.NumericValue = 0;
                    }
                    else if (rEntry.IsQueryByNonEmpty())
                    {
                        aField.Operator = sheet::FilterOperator_NOT_EMPTY;
                        aField.NumericValue = 0;
                    }
                }
                break;
            case SC_LESS:           aField.Operator = sheet::FilterOperator_LESS;             break;
            case SC_GREATER:        aField.Operator = sheet::FilterOperator_GREATER;          break;
            case SC_LESS_EQUAL:     aField.Operator = sheet::FilterOperator_LESS_EQUAL;   break;
            case SC_GREATER_EQUAL:  aField.Operator = sheet::FilterOperator_GREATER_EQUAL;  break;
            case SC_NOT_EQUAL:      aField.Operator = sheet::FilterOperator_NOT_EQUAL;    break;
            case SC_TOPVAL:         aField.Operator = sheet::FilterOperator_TOP_VALUES;   break;
            case SC_BOTVAL:         aField.Operator = sheet::FilterOperator_BOTTOM_VALUES;  break;
            case SC_TOPPERC:        aField.Operator = sheet::FilterOperator_TOP_PERCENT;      break;
            case SC_BOTPERC:        aField.Operator = sheet::FilterOperator_BOTTOM_PERCENT; break;
            default:
                OSL_FAIL("wrong filter enum");
                aField.Operator = sheet::FilterOperator_EMPTY;
        }
        pAry[i] = aField;
    }
    return aSeq;
}
 
namespace {
 
template<typename T>
void convertQueryEntryToUno(const ScQueryEntry& rEntry, T& rField)
{
    rField.Connection = (rEntry.eConnect == SC_AND) ? sheet::FilterConnection_AND : sheet::FilterConnection_OR;
    rField.Field = rEntry.nField;
 
    switch (rEntry.eOp)             // ScQueryOp
    {
    case SC_EQUAL:                  rField.Operator = sheet::FilterOperator2::EQUAL;                break;
    case SC_LESS:                   rField.Operator = sheet::FilterOperator2::LESS;                 break;
    case SC_GREATER:                rField.Operator = sheet::FilterOperator2::GREATER;              break;
    case SC_LESS_EQUAL:             rField.Operator = sheet::FilterOperator2::LESS_EQUAL;           break;
    case SC_GREATER_EQUAL:          rField.Operator = sheet::FilterOperator2::GREATER_EQUAL;        break;
    case SC_NOT_EQUAL:              rField.Operator = sheet::FilterOperator2::NOT_EQUAL;            break;
    case SC_TOPVAL:                 rField.Operator = sheet::FilterOperator2::TOP_VALUES;           break;
    case SC_BOTVAL:                 rField.Operator = sheet::FilterOperator2::BOTTOM_VALUES;        break;
    case SC_TOPPERC:                rField.Operator = sheet::FilterOperator2::TOP_PERCENT;          break;
    case SC_BOTPERC:                rField.Operator = sheet::FilterOperator2::BOTTOM_PERCENT;       break;
    case SC_CONTAINS:               rField.Operator = sheet::FilterOperator2::CONTAINS;             break;
    case SC_DOES_NOT_CONTAIN:       rField.Operator = sheet::FilterOperator2::DOES_NOT_CONTAIN;     break;
    case SC_BEGINS_WITH:            rField.Operator = sheet::FilterOperator2::BEGINS_WITH;          break;
    case SC_DOES_NOT_BEGIN_WITH:    rField.Operator = sheet::FilterOperator2::DOES_NOT_BEGIN_WITH;  break;
    case SC_ENDS_WITH:              rField.Operator = sheet::FilterOperator2::ENDS_WITH;            break;
    case SC_DOES_NOT_END_WITH:      rField.Operator = sheet::FilterOperator2::DOES_NOT_END_WITH;    break;
    default:
        OSL_FAIL("Unknown filter operator value.");
        rField.Operator = sheet::FilterOperator2::EMPTY;
    }
}
 
template<typename T>
void convertUnoToQueryEntry(const T& rField, ScQueryEntry& rEntry)
{
    rEntry.bDoQuery = true;
    rEntry.eConnect = (rField.Connection == sheet::FilterConnection_AND) ? SC_AND : SC_OR;
    rEntry.nField   = rField.Field;
 
    switch (rField.Operator)           // FilterOperator
    {
    case sheet::FilterOperator2::EQUAL:                 rEntry.eOp = SC_EQUAL;              break;
    case sheet::FilterOperator2::LESS:                  rEntry.eOp = SC_LESS;               break;
    case sheet::FilterOperator2::GREATER:               rEntry.eOp = SC_GREATER;            break;
    case sheet::FilterOperator2::LESS_EQUAL:            rEntry.eOp = SC_LESS_EQUAL;         break;
    case sheet::FilterOperator2::GREATER_EQUAL:         rEntry.eOp = SC_GREATER_EQUAL;      break;
    case sheet::FilterOperator2::NOT_EQUAL:             rEntry.eOp = SC_NOT_EQUAL;          break;
    case sheet::FilterOperator2::TOP_VALUES:            rEntry.eOp = SC_TOPVAL;             break;
    case sheet::FilterOperator2::BOTTOM_VALUES:         rEntry.eOp = SC_BOTVAL;             break;
    case sheet::FilterOperator2::TOP_PERCENT:           rEntry.eOp = SC_TOPPERC;            break;
    case sheet::FilterOperator2::BOTTOM_PERCENT:        rEntry.eOp = SC_BOTPERC;            break;
    case sheet::FilterOperator2::CONTAINS:              rEntry.eOp = SC_CONTAINS;           break;
    case sheet::FilterOperator2::DOES_NOT_CONTAIN:      rEntry.eOp = SC_DOES_NOT_CONTAIN;   break;
    case sheet::FilterOperator2::BEGINS_WITH:           rEntry.eOp = SC_BEGINS_WITH;        break;
    case sheet::FilterOperator2::DOES_NOT_BEGIN_WITH:   rEntry.eOp = SC_DOES_NOT_BEGIN_WITH;break;
    case sheet::FilterOperator2::ENDS_WITH:             rEntry.eOp = SC_ENDS_WITH;          break;
    case sheet::FilterOperator2::DOES_NOT_END_WITH:     rEntry.eOp = SC_DOES_NOT_END_WITH;  break;
    case sheet::FilterOperator2::EMPTY:
        rEntry.SetQueryByEmpty();
        break;
    case sheet::FilterOperator2::NOT_EMPTY:
        rEntry.SetQueryByNonEmpty();
        break;
    default:
        OSL_FAIL("Unknown filter operator type.");
        rEntry.eOp = SC_EQUAL;
    }
}
 
void fillQueryParam(
    ScQueryParam& rParam, ScDocument* pDoc,
    const uno::Sequence<sheet::TableFilterField2>& aFilterFields)
{
    size_t nCount = static_cast<size_t>(aFilterFields.getLength());
    rParam.Resize(nCount);
 
    const sheet::TableFilterField2* pAry = aFilterFields.getConstArray();
    svl::SharedStringPool& rPool = pDoc->GetSharedStringPool();
    for (size_t i = 0; i < nCount; ++i)
    {
        ScQueryEntry& rEntry = rParam.GetEntry(i);
        convertUnoToQueryEntry(pAry[i], rEntry);
 
        if (pAry[i].Operator != sheet::FilterOperator2::EMPTY && pAry[i].Operator != sheet::FilterOperator2::NOT_EMPTY)
        {
            ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
            rItems.resize(1);
            ScQueryEntry::Item& rItem = rItems.front();
            rItem.meType    = pAry[i].IsNumeric ? ScQueryEntry::ByValue : ScQueryEntry::ByString;
            rItem.mfVal     = pAry[i].NumericValue;
            rItem.maString = rPool.intern(pAry[i].StringValue);
 
            if (rItem.meType == ScQueryEntry::ByValue)
            {
                OUString aStr = pDoc->GetFormatTable()->GetInputLineString(rItem.mfVal, 0);
                rItem.maString = rPool.intern(aStr);
            }
        }
    }
 
    size_t nParamCount = rParam.GetEntryCount();    // if below eight Param isn't resized
    for (size_t i = nCount; i < nParamCount; ++i)
        rParam.GetEntry(i).bDoQuery = false;        // reset surplus fields
}
 
void fillQueryParam(
    ScQueryParam& rParam, ScDocument* pDoc,
    const uno::Sequence<sheet::TableFilterField3>& aFilterFields)
{
    size_t nCount = static_cast<size_t>(aFilterFields.getLength());
    rParam.Resize(nCount);
 
    svl::SharedStringPool& rPool = pDoc->GetSharedStringPool();
    const sheet::TableFilterField3* pAry = aFilterFields.getConstArray();
    for (size_t i = 0; i < nCount; ++i)
    {
        ScQueryEntry& rEntry = rParam.GetEntry(i);
        convertUnoToQueryEntry(pAry[i], rEntry);
 
        if (pAry[i].Operator != sheet::FilterOperator2::EMPTY && pAry[i].Operator != sheet::FilterOperator2::NOT_EMPTY)
        {
            ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
            rItems.clear();
            const uno::Sequence<sheet::FilterFieldValue>& rVals = pAry[i].Values;
            for (const auto& rVal : rVals)
            {
                ScQueryEntry::Item aItem;
                switch (rVal.FilterType)
                {
                    case FilterFieldType::NUMERIC:
                        aItem.meType = ScQueryEntry::ByValue;
                        break;
                    case FilterFieldType::STRING:
                        aItem.meType = ScQueryEntry::ByString;
                        break;
                    case FilterFieldType::DATE:
                        aItem.meType = ScQueryEntry::ByDate;
                        break;
                    case FilterFieldType::TEXT_COLOR:
                        aItem.meType = ScQueryEntry::ByTextColor;
                        break;
                    case FilterFieldType::BACKGROUND_COLOR:
                        aItem.meType = ScQueryEntry::ByBackgroundColor;
                        break;
                }
                aItem.mfVal    = rVal.NumericValue;
                aItem.maString = rPool.intern(rVal.StringValue);
 
                if (aItem.meType == ScQueryEntry::ByValue)
                {
                    OUString aStr = pDoc->GetFormatTable()->GetInputLineString(aItem.mfVal, 0);
                    aItem.maString = rPool.intern(aStr);
                }
                else if (aItem.meType == ScQueryEntry::ByTextColor
                         || aItem.meType == ScQueryEntry::ByBackgroundColor)
                {
                    aItem.maColor = Color(ColorTransparency, rVal.ColorValue);
                }
 
                // filter all dates starting with the given date filter YYYY or YYYY-MM and filter all datetimes
                // starting with the given datetime filter YYYY-MM-DD, YYYY-MM-DD HH, or YYYY-MM-DD HH:MM
                if( aItem.meType == ScQueryEntry::ByDate && aItem.maString.getLength() < 19 )
                {
                    ScFilterEntries aFilterEntries;
                    pDoc->GetFilterEntries(rEntry.nField, rParam.nRow1, rParam.nTab, aFilterEntries);
                    for( const auto& rFilter : aFilterEntries )
                    {
                        if( rFilter.GetString().startsWith(rVal.StringValue) )
                        {
                            aItem.maString = rPool.intern(rFilter.GetString());
                            rItems.push_back(aItem);
                        }
                    }
                }
                else
                {
                    rItems.push_back(aItem);
                }
            }
        }
    }
 
    size_t nParamCount = rParam.GetEntryCount();    // if below eight Param isn't resized
    for (size_t i = nCount; i < nParamCount; ++i)
        rParam.GetEntry(i).bDoQuery = false;        // reset surplus fields
}
 
}
 
uno::Sequence<sheet::TableFilterField2> SAL_CALL ScFilterDescriptorBase::getFilterFields2()
{
    SolarMutexGuard aGuard;
    ScQueryParam aParam;
    GetData(aParam);
 
    SCSIZE nEntries = aParam.GetEntryCount();   // allocated entries in Param
    SCSIZE nCount = 0;                          // active
    while ( nCount < nEntries &&
        aParam.GetEntry(nCount).bDoQuery )
        ++nCount;
 
    sheet::TableFilterField2 aField;
    uno::Sequence<sheet::TableFilterField2> aSeq(static_cast<sal_Int32>(nCount));
    sheet::TableFilterField2* pAry = aSeq.getArray();
    for (SCSIZE i=0; i<nCount; i++)
    {
        const ScQueryEntry& rEntry = aParam.GetEntry(i);
        convertQueryEntryToUno(rEntry, aField);
 
        bool bByEmpty = false;
        if (aField.Operator == sheet::FilterOperator2::EQUAL)
        {
            if (rEntry.IsQueryByEmpty())
            {
                aField.Operator = sheet::FilterOperator2::EMPTY;
                aField.NumericValue = 0;
                bByEmpty = true;
            }
            else if (rEntry.IsQueryByNonEmpty())
            {
                aField.Operator = sheet::FilterOperator2::NOT_EMPTY;
                aField.NumericValue = 0;
                bByEmpty = true;
            }
        }
 
        if (!bByEmpty && !rEntry.GetQueryItems().empty())
        {
            const ScQueryEntry::Item& rItem = rEntry.GetQueryItems().front();
            aField.IsNumeric     = rItem.meType != ScQueryEntry::ByString;
            aField.StringValue   = rItem.maString.getString();
            aField.NumericValue  = rItem.mfVal;
        }
 
        pAry[i] = aField;
    }
    return aSeq;
}
 
uno::Sequence<sheet::TableFilterField3> SAL_CALL ScFilterDescriptorBase::getFilterFields3()
{
    SolarMutexGuard aGuard;
    ScQueryParam aParam;
    GetData(aParam);
 
    SCSIZE nEntries = aParam.GetEntryCount();   // allocated entries in Param
    SCSIZE nCount = 0;                          // active
    while ( nCount < nEntries &&
        aParam.GetEntry(nCount).bDoQuery )
        ++nCount;
 
    sheet::TableFilterField3 aField;
    uno::Sequence<sheet::TableFilterField3> aSeq(static_cast<sal_Int32>(nCount));
    sheet::TableFilterField3* pAry = aSeq.getArray();
    for (SCSIZE i = 0; i < nCount; ++i)
    {
        const ScQueryEntry& rEntry = aParam.GetEntry(i);
        convertQueryEntryToUno(rEntry, aField);
 
        bool bByEmpty = false;
        if (aField.Operator == sheet::FilterOperator2::EQUAL)
        {
            if (rEntry.IsQueryByEmpty())
            {
                aField.Operator = sheet::FilterOperator2::EMPTY;
                aField.Values.realloc(1);
                aField.Values.getArray()[0].NumericValue = 0;
                bByEmpty = true;
            }
            else if (rEntry.IsQueryByNonEmpty())
            {
                aField.Operator = sheet::FilterOperator2::NOT_EMPTY;
                aField.Values.realloc(1);
                aField.Values.getArray()[0].NumericValue = 0;
                bByEmpty = true;
            }
        }
 
        if (!bByEmpty)
        {
            const ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
            size_t nItemCount = rItems.size();
            aField.Values.realloc(nItemCount);
            auto pValues = aField.Values.getArray();
            size_t j = 0;
            for (const auto& rItem : rItems)
            {
                pValues[j].IsNumeric = rItem.meType != ScQueryEntry::ByString;
                pValues[j].StringValue = rItem.maString.getString();
                pValues[j].NumericValue = rItem.mfVal;
                ++j;
            }
        }
 
        pAry[i] = aField;
    }
    return aSeq;
}
 
void SAL_CALL ScFilterDescriptorBase::setFilterFields(
                const uno::Sequence<sheet::TableFilterField>& aFilterFields )
{
    SolarMutexGuard aGuard;
    ScQueryParam aParam;
    GetData(aParam);
 
    SCSIZE nCount = static_cast<SCSIZE>(aFilterFields.getLength());
    aParam.Resize( nCount );
 
    ScDocument& rDoc = pDocSh->GetDocument();
    svl::SharedStringPool& rPool = rDoc.GetSharedStringPool();
    const sheet::TableFilterField* pAry = aFilterFields.getConstArray();
    SCSIZE i;
    for (i=0; i<nCount; i++)
    {
        ScQueryEntry& rEntry = aParam.GetEntry(i);
        ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
        rItems.resize(1);
        ScQueryEntry::Item& rItem = rItems.front();
        rEntry.bDoQuery = true;
        rEntry.eConnect = (pAry[i].Connection == sheet::FilterConnection_AND) ? SC_AND : SC_OR;
        rEntry.nField   = pAry[i].Field;
        rItem.meType    = pAry[i].IsNumeric ? ScQueryEntry::ByValue : ScQueryEntry::ByString;
        rItem.mfVal     = pAry[i].NumericValue;
        rItem.maString = rPool.intern(pAry[i].StringValue);
 
        if (rItem.meType != ScQueryEntry::ByString)
        {
            OUString aStr = rDoc.GetFormatTable()->GetInputLineString(rItem.mfVal, 0);
            rItem.maString = rPool.intern(aStr);
        }
 
        switch (pAry[i].Operator)           // FilterOperator
        {
            case sheet::FilterOperator_EQUAL:           rEntry.eOp = SC_EQUAL;       break;
            case sheet::FilterOperator_LESS:            rEntry.eOp = SC_LESS;            break;
            case sheet::FilterOperator_GREATER:         rEntry.eOp = SC_GREATER;         break;
            case sheet::FilterOperator_LESS_EQUAL:      rEntry.eOp = SC_LESS_EQUAL;  break;
            case sheet::FilterOperator_GREATER_EQUAL:   rEntry.eOp = SC_GREATER_EQUAL; break;
            case sheet::FilterOperator_NOT_EQUAL:       rEntry.eOp = SC_NOT_EQUAL;   break;
            case sheet::FilterOperator_TOP_VALUES:      rEntry.eOp = SC_TOPVAL;      break;
            case sheet::FilterOperator_BOTTOM_VALUES:   rEntry.eOp = SC_BOTVAL;      break;
            case sheet::FilterOperator_TOP_PERCENT:     rEntry.eOp = SC_TOPPERC;         break;
            case sheet::FilterOperator_BOTTOM_PERCENT:  rEntry.eOp = SC_BOTPERC;         break;
            case sheet::FilterOperator_EMPTY:
                rEntry.SetQueryByEmpty();
                break;
            case sheet::FilterOperator_NOT_EMPTY:
                rEntry.SetQueryByNonEmpty();
                break;
            default:
                OSL_FAIL("Wrong query enum");
                rEntry.eOp = SC_EQUAL;
        }
    }
 
    SCSIZE nParamCount = aParam.GetEntryCount();    // if below eight Param isn't resized
    for (i=nCount; i<nParamCount; i++)
        aParam.GetEntry(i).bDoQuery = false;        // reset surplus fields
 
    PutData(aParam);
}
 
void SAL_CALL ScFilterDescriptorBase::setFilterFields2(
    const uno::Sequence<sheet::TableFilterField2>& aFilterFields )
{
    SolarMutexGuard aGuard;
    ScQueryParam aParam;
    GetData(aParam);
    fillQueryParam(aParam, &pDocSh->GetDocument(), aFilterFields);
    PutData(aParam);
}
 
void SAL_CALL ScFilterDescriptorBase::setFilterFields3(
    const uno::Sequence<sheet::TableFilterField3>& aFilterFields )
{
    SolarMutexGuard aGuard;
    ScQueryParam aParam;
    GetData(aParam);
    fillQueryParam(aParam, &pDocSh->GetDocument(), aFilterFields);
    PutData(aParam);
}
 
// Rest are Properties
 
// XPropertySet
 
uno::Reference<beans::XPropertySetInfo> SAL_CALL ScFilterDescriptorBase::getPropertySetInfo()
{
    SolarMutexGuard aGuard;
    static uno::Reference<beans::XPropertySetInfo> aRef(
        new SfxItemPropertySetInfo( aPropSet.getPropertyMap() ));
    return aRef;
}
 
void SAL_CALL ScFilterDescriptorBase::setPropertyValue(
                        const OUString& aPropertyName, const uno::Any& aValue )
{
    SolarMutexGuard aGuard;
    ScQueryParam aParam;
    GetData(aParam);
 
    if (aPropertyName == SC_UNONAME_CONTHDR)
        aParam.bHasHeader = ScUnoHelpFunctions::GetBoolFromAny( aValue );
    else if (aPropertyName == SC_UNONAME_COPYOUT)
        aParam.bInplace = !(ScUnoHelpFunctions::GetBoolFromAny( aValue ));
    else if (aPropertyName == SC_UNONAME_ISCASE)
        aParam.bCaseSens = ScUnoHelpFunctions::GetBoolFromAny( aValue );
    else if (aPropertyName == SC_UNONAME_MAXFLD)
    {
        // silently ignored
    }
    else if (aPropertyName == SC_UNONAME_ORIENT)
    {
        //! test for correct enum type?
        table::TableOrientation eOrient = static_cast<table::TableOrientation>(ScUnoHelpFunctions::GetEnumFromAny( aValue ));
        aParam.bByRow = ( eOrient != table::TableOrientation_COLUMNS );
    }
    else if (aPropertyName == SC_UNONAME_OUTPOS)
    {
        table::CellAddress aAddress;
        if ( aValue >>= aAddress )
        {
            aParam.nDestTab = aAddress.Sheet;
            aParam.nDestCol = static_cast<SCCOL>(aAddress.Column);
            aParam.nDestRow = static_cast<SCROW>(aAddress.Row);
        }
    }
    else if (aPropertyName == SC_UNONAME_SAVEOUT)
        aParam.bDestPers = ScUnoHelpFunctions::GetBoolFromAny( aValue );
    else if (aPropertyName == SC_UNONAME_SKIPDUP)
        aParam.bDuplicate = !(ScUnoHelpFunctions::GetBoolFromAny( aValue ));
    else if (aPropertyName == SC_UNONAME_USEREGEX)
        aParam.eSearchType = ScUnoHelpFunctions::GetBoolFromAny( aValue ) ? utl::SearchParam::SearchType::Regexp :
            utl::SearchParam::SearchType::Normal;
 
    PutData(aParam);
}
 
uno::Any SAL_CALL ScFilterDescriptorBase::getPropertyValue( const OUString& aPropertyName )
{
    SolarMutexGuard aGuard;
    ScQueryParam aParam;
    GetData(aParam);
 
    uno::Any aRet;
 
    if (aPropertyName == SC_UNONAME_CONTHDR )
        aRet <<= aParam.bHasHeader;
    else if (aPropertyName == SC_UNONAME_COPYOUT )
        aRet <<= !(aParam.bInplace);
    else if (aPropertyName == SC_UNONAME_ISCASE )
        aRet <<= aParam.bCaseSens;
    else if (aPropertyName == SC_UNONAME_MAXFLD )
        aRet <<= static_cast<sal_Int32>(aParam.GetEntryCount());
    else if (aPropertyName == SC_UNONAME_ORIENT )
    {
        table::TableOrientation eOrient = aParam.bByRow ? table::TableOrientation_ROWS :
                                                          table::TableOrientation_COLUMNS;
        aRet <<= eOrient;
    }
    else if (aPropertyName == SC_UNONAME_OUTPOS )
    {
        table::CellAddress aOutPos;
        aOutPos.Sheet  = aParam.nDestTab;
        aOutPos.Column = aParam.nDestCol;
        aOutPos.Row    = aParam.nDestRow;
        aRet <<= aOutPos;
    }
    else if (aPropertyName == SC_UNONAME_SAVEOUT )
        aRet <<= aParam.bDestPers;
    else if (aPropertyName == SC_UNONAME_SKIPDUP )
        aRet <<= !(aParam.bDuplicate);
    else if (aPropertyName == SC_UNONAME_USEREGEX )
        aRet <<= (aParam.eSearchType == utl::SearchParam::SearchType::Regexp);
 
    return aRet;
}
 
SC_IMPL_DUMMY_PROPERTY_LISTENER( ScFilterDescriptorBase )
 
ScFilterDescriptor::ScFilterDescriptor(ScDocShell* pDocShell)
    :
    ScFilterDescriptorBase(pDocShell)
{
}
 
ScFilterDescriptor::~ScFilterDescriptor()
{
}
 
void ScFilterDescriptor::GetData( ScQueryParam& rParam ) const
{
    rParam = aStoredParam;          // query for interface
}
 
void ScFilterDescriptor::PutData( const ScQueryParam& rParam )
{
    aStoredParam = rParam;          // set by the interface
}
 
void ScFilterDescriptor::SetParam( const ScQueryParam& rNew )
{
    aStoredParam = rNew;            // set from outside
}
 
ScRangeFilterDescriptor::ScRangeFilterDescriptor(ScDocShell* pDocShell, ScDatabaseRangeObj* pPar) :
    ScFilterDescriptorBase(pDocShell),
    mxParent(pPar)
{
}
 
ScRangeFilterDescriptor::~ScRangeFilterDescriptor()
{
}
 
void ScRangeFilterDescriptor::GetData( ScQueryParam& rParam ) const
{
    if (mxParent.is())
        mxParent->GetQueryParam( rParam );
}
 
void ScRangeFilterDescriptor::PutData( const ScQueryParam& rParam )
{
    if (mxParent.is())
        mxParent->SetQueryParam( rParam );
}
 
ScDataPilotFilterDescriptor::ScDataPilotFilterDescriptor(ScDocShell* pDocShell, ScDataPilotDescriptorBase* pPar) :
    ScFilterDescriptorBase(pDocShell),
    mxParent(pPar)
{
}
 
ScDataPilotFilterDescriptor::~ScDataPilotFilterDescriptor()
{
}
 
void ScDataPilotFilterDescriptor::GetData( ScQueryParam& rParam ) const
{
    if (mxParent.is())
    {
        ScDPObject* pDPObj = mxParent->GetDPObject();
        if (pDPObj && pDPObj->IsSheetData())
            rParam = pDPObj->GetSheetDesc()->GetQueryParam();
    }
}
 
void ScDataPilotFilterDescriptor::PutData( const ScQueryParam& rParam )
{
    if (!mxParent.is())
        return;
 
    ScDPObject* pDPObj = mxParent->GetDPObject();
    if (pDPObj)
    {
        ScSheetSourceDesc aSheetDesc(&mxParent->GetDocShell()->GetDocument());
        if (pDPObj->IsSheetData())
            aSheetDesc = *pDPObj->GetSheetDesc();
        aSheetDesc.SetQueryParam(rParam);
        pDPObj->SetSheetDesc(aSheetDesc);
        mxParent->SetDPObject(pDPObj);
    }
}
 
ScDatabaseRangeObj::ScDatabaseRangeObj(ScDocShell* pDocSh, OUString aNm) :
    pDocShell( pDocSh ),
    aName(std::move( aNm )),
    aPropSet( lcl_GetDBRangePropertyMap() ),
    bIsUnnamed(false),
    aTab( 0 )
{
    pDocShell->GetDocument().AddUnoObject(*this);
}
 
ScDatabaseRangeObj::ScDatabaseRangeObj(ScDocShell* pDocSh, const SCTAB nTab) :
    pDocShell( pDocSh ),
    aName(STR_DB_LOCAL_NONAME),
    aPropSet( lcl_GetDBRangePropertyMap() ),
    bIsUnnamed(true),
    aTab( nTab )
{
    pDocShell->GetDocument().AddUnoObject(*this);
}
 
ScDatabaseRangeObj::~ScDatabaseRangeObj()
{
    SolarMutexGuard g;
 
    if (pDocShell)
        pDocShell->GetDocument().RemoveUnoObject(*this);
}
 
void ScDatabaseRangeObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
{
 
    if ( rHint.GetId() == SfxHintId::Dying )
        pDocShell = nullptr;
    else if ( rHint.GetId() == SfxHintId::ScDBRangeRefreshed )
    {
        auto pRefreshHint = static_cast<const ScDBRangeRefreshedHint*>(&rHint);
        ScDBData* pDBData = GetDBData_Impl();
        ScImportParam aParam;
        pDBData->GetImportParam(aParam);
        if (aParam == pRefreshHint->GetImportParam())
            Refreshed_Impl();
    }
}
 
// Help functions
 
ScDBData* ScDatabaseRangeObj::GetDBData_Impl() const
{
    ScDBData* pRet = nullptr;
    if (pDocShell)
    {
        if (bIsUnnamed)
        {
            pRet = pDocShell->GetDocument().GetAnonymousDBData(aTab);
        }
        else
        {
            ScDBCollection* pNames = pDocShell->GetDocument().GetDBCollection();
            if (pNames)
            {
                ScDBData* p = pNames->getNamedDBs().findByUpperName(ScGlobal::getCharClass().uppercase(aName));
                if (p)
                    pRet = p;
            }
        }
    }
    return pRet;
}
 
// XNamed
 
OUString SAL_CALL ScDatabaseRangeObj::getName()
{
    SolarMutexGuard aGuard;
    return aName;
}
 
void SAL_CALL ScDatabaseRangeObj::setName( const OUString& aNewName )
{
    SolarMutexGuard aGuard;
    if (pDocShell)
    {
        ScDBDocFunc aFunc(*pDocShell);
        bool bOk = aFunc.RenameDBRange( aName, aNewName );
        if (bOk)
            aName = aNewName;
    }
}
 
// XDatabaseRange
 
table::CellRangeAddress SAL_CALL ScDatabaseRangeObj::getDataArea()
{
    SolarMutexGuard aGuard;
    table::CellRangeAddress aAddress;
    ScDBData* pData = GetDBData_Impl();
    if (pData)
    {
        ScRange aRange;
        pData->GetArea(aRange);
        aAddress.Sheet       = aRange.aStart.Tab();
        aAddress.StartColumn = aRange.aStart.Col();
        aAddress.StartRow    = aRange.aStart.Row();
        aAddress.EndColumn   = aRange.aEnd.Col();
        aAddress.EndRow      = aRange.aEnd.Row();
    }
    return aAddress;
}
 
void SAL_CALL ScDatabaseRangeObj::setDataArea( const table::CellRangeAddress& aDataArea )
{
    SolarMutexGuard aGuard;
    ScDBData* pData = GetDBData_Impl();
    if ( pDocShell && pData )
    {
        ScDBData aNewData( *pData );
        //! MoveTo ???
        aNewData.SetArea( aDataArea.Sheet, static_cast<SCCOL>(aDataArea.StartColumn), static_cast<SCROW>(aDataArea.StartRow),
                                           static_cast<SCCOL>(aDataArea.EndColumn), static_cast<SCROW>(aDataArea.EndRow) );
        ScDBDocFunc aFunc(*pDocShell);
        aFunc.ModifyDBData(aNewData);
    }
}
 
uno::Sequence<beans::PropertyValue> SAL_CALL ScDatabaseRangeObj::getSortDescriptor()
{
    SolarMutexGuard aGuard;
    ScSortParam aParam;
    const ScDBData* pData = GetDBData_Impl();
    if (pData)
    {
        pData->GetSortParam(aParam);
 
        //  SortDescriptor contains the counted fields inside the area
        ScRange aDBRange;
        pData->GetArea(aDBRange);
        SCCOLROW nFieldStart = aParam.bByRow ? static_cast<SCCOLROW>(aDBRange.aStart.Col()) : static_cast<SCCOLROW>(aDBRange.aStart.Row());
        for (sal_uInt16 i=0; i<aParam.GetSortKeyCount(); i++)
            if ( aParam.maKeyState[i].bDoSort && aParam.maKeyState[i].nField >= nFieldStart )
                aParam.maKeyState[i].nField -= nFieldStart;
    }
 
    uno::Sequence<beans::PropertyValue> aSeq( ScSortDescriptor::GetPropertyCount() );
    ScSortDescriptor::FillProperties( aSeq, aParam );
    return aSeq;
}
 
void ScDatabaseRangeObj::GetQueryParam(ScQueryParam& rQueryParam) const
{
    const ScDBData* pData = GetDBData_Impl();
    if (!pData)
        return;
 
    pData->GetQueryParam(rQueryParam);
 
    //  FilterDescriptor contains the counted fields inside the area
    ScRange aDBRange;
    pData->GetArea(aDBRange);
    SCCOLROW nFieldStart = rQueryParam.bByRow ? static_cast<SCCOLROW>(aDBRange.aStart.Col()) : static_cast<SCCOLROW>(aDBRange.aStart.Row());
    SCSIZE nCount = rQueryParam.GetEntryCount();
    for (SCSIZE i=0; i<nCount; i++)
    {
        ScQueryEntry& rEntry = rQueryParam.GetEntry(i);
        if (rEntry.bDoQuery && rEntry.nField >= nFieldStart)
            rEntry.nField -= nFieldStart;
    }
}
 
void ScDatabaseRangeObj::SetQueryParam(const ScQueryParam& rQueryParam)
{
    const ScDBData* pData = GetDBData_Impl();
    if (!pData)
        return;
 
    //  FilterDescriptor contains the counted fields inside the area
    ScQueryParam aParam(rQueryParam);
    ScRange aDBRange;
    pData->GetArea(aDBRange);
    SCCOLROW nFieldStart = aParam.bByRow ? static_cast<SCCOLROW>(aDBRange.aStart.Col()) : static_cast<SCCOLROW>(aDBRange.aStart.Row());
 
    SCSIZE nCount = aParam.GetEntryCount();
    for (SCSIZE i=0; i<nCount; i++)
    {
           ScQueryEntry& rEntry = aParam.GetEntry(i);
           if (rEntry.bDoQuery)
                   rEntry.nField += nFieldStart;
    }
 
    ScDBData aNewData( *pData );
    aNewData.SetQueryParam(aParam);
    aNewData.SetHeader(aParam.bHasHeader);      // not in ScDBData::SetQueryParam
    aNewData.SetTotals(aParam.bHasTotals);      // not in ScDBData::SetQueryParam
    ScDBDocFunc aFunc(*pDocShell);
    aFunc.ModifyDBData(aNewData);
}
 
uno::Reference<sheet::XSheetFilterDescriptor> SAL_CALL ScDatabaseRangeObj::getFilterDescriptor()
{
    SolarMutexGuard aGuard;
    return new ScRangeFilterDescriptor(pDocShell, this);
}
 
void ScDatabaseRangeObj::GetSubTotalParam(ScSubTotalParam& rSubTotalParam) const
{
    const ScDBData* pData = GetDBData_Impl();
    if (!pData)
        return;
 
    pData->GetSubTotalParam(rSubTotalParam);
 
    //  FilterDescriptor contains the counted fields inside the area
    ScRange aDBRange;
    pData->GetArea(aDBRange);
    SCCOL nFieldStart = aDBRange.aStart.Col();
    for (sal_uInt16 i=0; i<MAXSUBTOTAL; i++)
    {
        if ( rSubTotalParam.bGroupActive[i] )
        {
            if ( rSubTotalParam.nField[i] >= nFieldStart )
                rSubTotalParam.nField[i] = sal::static_int_cast<SCCOL>( rSubTotalParam.nField[i] - nFieldStart );
            for (SCCOL j=0; j<rSubTotalParam.nSubTotals[i]; j++)
                if ( rSubTotalParam.pSubTotals[i][j] >= nFieldStart )
                    rSubTotalParam.pSubTotals[i][j] =
                        sal::static_int_cast<SCCOL>( rSubTotalParam.pSubTotals[i][j] - nFieldStart );
        }
    }
}
 
void ScDatabaseRangeObj::SetSubTotalParam(const ScSubTotalParam& rSubTotalParam)
{
    const ScDBData* pData = GetDBData_Impl();
    if (!pData)
        return;
 
    //  FilterDescriptor contains the counted fields inside the area
    ScSubTotalParam aParam(rSubTotalParam);
    ScRange aDBRange;
    pData->GetArea(aDBRange);
    SCCOL nFieldStart = aDBRange.aStart.Col();
    for (sal_uInt16 i=0; i<MAXSUBTOTAL; i++)
    {
        if ( aParam.bGroupActive[i] )
        {
            aParam.nField[i] = sal::static_int_cast<SCCOL>( aParam.nField[i] + nFieldStart );
            for (SCCOL j=0; j<aParam.nSubTotals[i]; j++)
                aParam.pSubTotals[i][j] = sal::static_int_cast<SCCOL>( aParam.pSubTotals[i][j] + nFieldStart );
        }
    }
 
    ScDBData aNewData( *pData );
    aNewData.SetSubTotalParam(aParam);
    ScDBDocFunc aFunc(*pDocShell);
    aFunc.ModifyDBData(aNewData);
}
 
uno::Reference<sheet::XSubTotalDescriptor> SAL_CALL ScDatabaseRangeObj::getSubTotalDescriptor()
{
    SolarMutexGuard aGuard;
    return new ScRangeSubTotalDescriptor(this);
}
 
uno::Sequence<beans::PropertyValue> SAL_CALL ScDatabaseRangeObj::getImportDescriptor()
{
    SolarMutexGuard aGuard;
    ScImportParam aParam;
    const ScDBData* pData = GetDBData_Impl();
    if (pData)
        pData->GetImportParam(aParam);
 
    uno::Sequence<beans::PropertyValue> aSeq( ScImportDescriptor::GetPropertyCount() );
    ScImportDescriptor::FillProperties( aSeq, aParam );
    return aSeq;
}
 
// XRefreshable
 
void SAL_CALL ScDatabaseRangeObj::refresh()
{
    SolarMutexGuard aGuard;
    ScDBData* pData = GetDBData_Impl();
    if ( !(pDocShell && pData) )
        return;
 
    ScDBDocFunc aFunc(*pDocShell);
 
    // repeat import?
    bool bContinue = true;
    ScImportParam aImportParam;
    pData->GetImportParam( aImportParam );
    if (aImportParam.bImport && !pData->HasImportSelection())
    {
        SCTAB nTab;
        SCCOL nDummyCol;
        SCROW nDummyRow;
        pData->GetArea( nTab, nDummyCol,nDummyRow,nDummyCol,nDummyRow );
        bContinue = aFunc.DoImport( nTab, aImportParam, nullptr );   //! Api-Flag as parameter
    }
 
    // if no error then internal operations (sort, query, subtotal)
    if (bContinue)
        aFunc.RepeatDB( pData->GetName(), true, bIsUnnamed, aTab );
}
 
void SAL_CALL ScDatabaseRangeObj::addRefreshListener(
                                const uno::Reference<util::XRefreshListener >& xListener )
{
    SolarMutexGuard aGuard;
    aRefreshListeners.emplace_back( xListener );
 
    //  hold one additional ref to keep this object alive as long as there are listeners
    if ( aRefreshListeners.size() == 1 )
        acquire();
}
 
void SAL_CALL ScDatabaseRangeObj::removeRefreshListener(
                                const uno::Reference<util::XRefreshListener >& xListener )
{
    SolarMutexGuard aGuard;
    sal_uInt16 nCount = aRefreshListeners.size();
    for ( sal_uInt16 n=nCount; n--; )
    {
        uno::Reference<util::XRefreshListener>& rObj = aRefreshListeners[n];
        if ( rObj == xListener )
        {
            aRefreshListeners.erase( aRefreshListeners.begin() + n );
            if ( aRefreshListeners.empty() )
                release();                          // release ref for listeners
            break;
        }
    }
}
 
void ScDatabaseRangeObj::Refreshed_Impl()
{
    lang::EventObject aEvent;
    aEvent.Source = getXWeak();
    for (uno::Reference<util::XRefreshListener> & xRefreshListener : aRefreshListeners)
        xRefreshListener->refreshed( aEvent );
}
 
// XCellRangeSource
 
uno::Reference<table::XCellRange> SAL_CALL ScDatabaseRangeObj::getReferredCells()
{
    SolarMutexGuard aGuard;
    ScDBData* pData = GetDBData_Impl();
    if ( pData )
    {
        //! static function to create ScCellObj/ScCellRange on ScCellRangeObj ???
        ScRange aRange;
 
        pData->GetArea(aRange);
        if ( aRange.aStart == aRange.aEnd )
            return new ScCellObj( pDocShell, aRange.aStart );
        else
            return new ScCellRangeObj( pDocShell, aRange );
    }
    return nullptr;
}
 
// XPropertySet
 
uno::Reference<beans::XPropertySetInfo> SAL_CALL ScDatabaseRangeObj::getPropertySetInfo()
{
    SolarMutexGuard aGuard;
    static uno::Reference<beans::XPropertySetInfo> aRef(
        new SfxItemPropertySetInfo( aPropSet.getPropertyMap() ));
    return aRef;
}
 
void SAL_CALL ScDatabaseRangeObj::setPropertyValue(
                        const OUString& aPropertyName, const uno::Any& aValue )
{
    SolarMutexGuard aGuard;
    ScDBData* pData = GetDBData_Impl();
    if ( !(pDocShell && pData) )
        return;
 
    ScDBData aNewData( *pData );
    bool bDo = true;
 
    if ( aPropertyName == SC_UNONAME_KEEPFORM )
        aNewData.SetKeepFmt( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
    else if ( aPropertyName == SC_UNONAME_MOVCELLS )
        aNewData.SetDoSize( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
    else if ( aPropertyName == SC_UNONAME_STRIPDAT )
        aNewData.SetStripData( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
    else if (aPropertyName == SC_UNONAME_AUTOFLT )
    {
        bool bAutoFilter(ScUnoHelpFunctions::GetBoolFromAny( aValue ));
        aNewData.SetAutoFilter(bAutoFilter);
        ScRange aRange;
        aNewData.GetArea(aRange);
        ScDocument& rDoc = pDocShell->GetDocument();
        if (bAutoFilter)
            rDoc.ApplyFlagsTab( aRange.aStart.Col(), aRange.aStart.Row(),
                                 aRange.aEnd.Col(), aRange.aStart.Row(),
                                 aRange.aStart.Tab(), ScMF::Auto );
        else if (!bAutoFilter)
            rDoc.RemoveFlagsTab(aRange.aStart.Col(), aRange.aStart.Row(),
                                 aRange.aEnd.Col(), aRange.aStart.Row(),
                                 aRange.aStart.Tab(), ScMF::Auto );
        ScRange aPaintRange(aRange.aStart, aRange.aEnd);
        aPaintRange.aEnd.SetRow(aPaintRange.aStart.Row());
        pDocShell->PostPaint(aPaintRange, PaintPartFlags::Grid);
    }
    else if (aPropertyName == SC_UNONAME_USEFLTCRT )
    {
        if (ScUnoHelpFunctions::GetBoolFromAny( aValue ))
        {
            // only here to set bIsAdvanced in ScDBData
            ScRange aRange;
            (void)aNewData.GetAdvancedQuerySource(aRange);
            aNewData.SetAdvancedQuerySource(&aRange);
        }
        else
            aNewData.SetAdvancedQuerySource(nullptr);
    }
    else if (aPropertyName == SC_UNONAME_FLTCRT )
    {
        table::CellRangeAddress aRange;
        if (aValue >>= aRange)
        {
            ScRange aCoreRange;
            ScUnoConversion::FillScRange(aCoreRange, aRange);
 
            aNewData.SetAdvancedQuerySource(&aCoreRange);
        }
    }
    else if (aPropertyName == SC_UNONAME_FROMSELECT )
    {
        aNewData.SetImportSelection(::cppu::any2bool(aValue));
    }
    else if (aPropertyName == SC_UNONAME_REFPERIOD )
    {
        sal_Int32 nRefresh = 0;
        if (aValue >>= nRefresh)
        {
            ScDocument& rDoc = pDocShell->GetDocument();
            aNewData.SetRefreshDelay(nRefresh);
            if (rDoc.GetDBCollection())
            {
                aNewData.SetRefreshHandler( rDoc.GetDBCollection()->GetRefreshHandler() );
                aNewData.SetRefreshControl( &rDoc.GetRefreshTimerControlAddress() );
            }
        }
    }
    else if (aPropertyName == SC_UNONAME_CONRES )
    {
    }
    else if ( aPropertyName == SC_UNONAME_TOTALSROW )
        aNewData.SetTotals( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
    else if ( aPropertyName == SC_UNONAME_CONTHDR )
        aNewData.SetHeader( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
    else
        bDo = false;
 
    if (bDo)
    {
        ScDBDocFunc aFunc(*pDocShell);
        aFunc.ModifyDBData(aNewData);
    }
}
 
uno::Any SAL_CALL ScDatabaseRangeObj::getPropertyValue( const OUString& aPropertyName )
{
    SolarMutexGuard aGuard;
    uno::Any aRet;
    ScDBData* pData = GetDBData_Impl();
    if ( pData )
    {
        if ( aPropertyName == SC_UNONAME_KEEPFORM )
            aRet <<= pData->IsKeepFmt();
        else if ( aPropertyName == SC_UNONAME_MOVCELLS )
            aRet <<= pData->IsDoSize();
        else if ( aPropertyName == SC_UNONAME_STRIPDAT )
            aRet <<= pData->IsStripData();
        else if ( aPropertyName == SC_UNONAME_ISUSER )
        {
            //  all database ranges except "unnamed" are user defined
            aRet <<= (pData->GetName() != STR_DB_LOCAL_NONAME);
        }
        else if ( aPropertyName == SC_UNO_LINKDISPBIT )
        {
            //  no target bitmaps for individual entries (would be all equal)
            // ScLinkTargetTypeObj::SetLinkTargetBitmap( aRet, SC_LINKTARGETTYPE_DBAREA );
        }
        else if ( aPropertyName == SC_UNO_LINKDISPNAME )
            aRet <<= aName;
        else if (aPropertyName == SC_UNONAME_AUTOFLT )
        {
            bool bAutoFilter(GetDBData_Impl()->HasAutoFilter());
 
            aRet <<= bAutoFilter;
        }
        else if (aPropertyName == SC_UNONAME_USEFLTCRT )
        {
            ScRange aRange;
            bool bIsAdvancedSource(GetDBData_Impl()->GetAdvancedQuerySource(aRange));
 
            aRet <<= bIsAdvancedSource;
        }
        else if (aPropertyName == SC_UNONAME_FLTCRT )
        {
            table::CellRangeAddress aRange;
            ScRange aCoreRange;
            if (GetDBData_Impl()->GetAdvancedQuerySource(aCoreRange))
                ScUnoConversion::FillApiRange(aRange, aCoreRange);
 
            aRet <<= aRange;
        }
        else if (aPropertyName == SC_UNONAME_FROMSELECT )
        {
            aRet <<= GetDBData_Impl()->HasImportSelection();
        }
        else if (aPropertyName == SC_UNONAME_REFPERIOD )
        {
            sal_Int32 nRefresh(GetDBData_Impl()->GetRefreshDelaySeconds());
            aRet <<= nRefresh;
        }
        else if (aPropertyName == SC_UNONAME_CONRES )
        {
        }
        else if (aPropertyName == SC_UNONAME_TOKENINDEX )
        {
            // get index for use in formula tokens (read-only)
            aRet <<= static_cast<sal_Int32>(GetDBData_Impl()->GetIndex());
        }
        else if (aPropertyName == SC_UNONAME_TOTALSROW )
        {
            bool bTotals(GetDBData_Impl()->HasTotals());
 
            aRet <<= bTotals;
        }
        else if (aPropertyName == SC_UNONAME_CONTHDR )
        {
            bool bHeader(GetDBData_Impl()->HasHeader());
 
            aRet <<= bHeader;
        }
    }
    return aRet;
}
 
SC_IMPL_DUMMY_PROPERTY_LISTENER( ScDatabaseRangeObj )
 
// XServiceInfo
OUString SAL_CALL ScDatabaseRangeObj::getImplementationName()
{
    return u"ScDatabaseRangeObj"_ustr;
}
 
sal_Bool SAL_CALL ScDatabaseRangeObj::supportsService( const OUString& rServiceName )
{
    return cppu::supportsService(this, rServiceName);
}
 
uno::Sequence<OUString> SAL_CALL ScDatabaseRangeObj::getSupportedServiceNames()
{
    return {u"com.sun.star.sheet.DatabaseRange"_ustr,
            SCLINKTARGET_SERVICE};
}
 
ScDatabaseRangesObj::ScDatabaseRangesObj(ScDocShell* pDocSh) :
    pDocShell( pDocSh )
{
    pDocShell->GetDocument().AddUnoObject(*this);
}
 
ScDatabaseRangesObj::~ScDatabaseRangesObj()
{
    SolarMutexGuard g;
 
    if (pDocShell)
        pDocShell->GetDocument().RemoveUnoObject(*this);
}
 
void ScDatabaseRangesObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
{
    //  reference update does not matter here
 
    if ( rHint.GetId() == SfxHintId::Dying )
    {
        pDocShell = nullptr;
    }
}
 
// XDatabaseRanges
 
rtl::Reference<ScDatabaseRangeObj> ScDatabaseRangesObj::GetObjectByIndex_Impl(size_t nIndex)
{
    if (!pDocShell)
        return nullptr;
 
    ScDBCollection* pNames = pDocShell->GetDocument().GetDBCollection();
    if (!pNames)
        return nullptr;
 
    const ScDBCollection::NamedDBs& rDBs = pNames->getNamedDBs();
    if (rDBs.empty() || nIndex >= rDBs.size())
        return nullptr;
 
    ScDBCollection::NamedDBs::const_iterator itr = rDBs.begin();
    ::std::advance(itr, nIndex); // boundary check is done above.
    return new ScDatabaseRangeObj(pDocShell, (*itr)->GetName());
}
 
rtl::Reference<ScDatabaseRangeObj> ScDatabaseRangesObj::GetObjectByName_Impl(const OUString& aName)
{
    if ( pDocShell && hasByName(aName) )
    {
        return new ScDatabaseRangeObj( pDocShell, aName );
    }
    return nullptr;
}
 
void SAL_CALL ScDatabaseRangesObj::addNewByName( const OUString& aName,
                                        const table::CellRangeAddress& aRange )
{
    SolarMutexGuard aGuard;
    bool bDone = false;
    if (pDocShell)
    {
        ScDBDocFunc aFunc(*pDocShell);
 
        ScRange aNameRange( static_cast<SCCOL>(aRange.StartColumn), static_cast<SCROW>(aRange.StartRow), aRange.Sheet,
                            static_cast<SCCOL>(aRange.EndColumn),   static_cast<SCROW>(aRange.EndRow),   aRange.Sheet );
        bDone = aFunc.AddDBRange( aName, aNameRange );
    }
    if (!bDone)
        throw uno::RuntimeException();      // no other exceptions specified
}
 
void SAL_CALL ScDatabaseRangesObj::removeByName( const OUString& aName )
{
    SolarMutexGuard aGuard;
    bool bDone = false;
    if (pDocShell)
    {
        ScDBDocFunc aFunc(*pDocShell);
        bDone = aFunc.DeleteDBRange( aName );
    }
    if (!bDone)
        throw uno::RuntimeException();      // no other exceptions specified
}
 
// XEnumerationAccess
 
uno::Reference<container::XEnumeration> SAL_CALL ScDatabaseRangesObj::createEnumeration()
{
    SolarMutexGuard aGuard;
    return new ScIndexEnumeration(this, u"com.sun.star.sheet.DatabaseRangesEnumeration"_ustr);
}
 
// XIndexAccess
 
sal_Int32 SAL_CALL ScDatabaseRangesObj::getCount()
{
    SolarMutexGuard aGuard;
 
    //! need to omit "unnamed"?
 
    if (pDocShell)
    {
        ScDBCollection* pNames = pDocShell->GetDocument().GetDBCollection();
        if (pNames)
            return static_cast<sal_Int32>(pNames->getNamedDBs().size());
    }
    return 0;
}
 
uno::Any SAL_CALL ScDatabaseRangesObj::getByIndex( sal_Int32 nIndex )
{
    SolarMutexGuard aGuard;
    if (nIndex < 0)
        throw lang::IndexOutOfBoundsException();
 
    uno::Reference<sheet::XDatabaseRange> xRange(GetObjectByIndex_Impl(static_cast<size_t>(nIndex)));
    if (!xRange.is())
        throw lang::IndexOutOfBoundsException();
 
    return uno::Any(xRange);
}
 
uno::Type SAL_CALL ScDatabaseRangesObj::getElementType()
{
    return cppu::UnoType<sheet::XDatabaseRange>::get();
}
 
sal_Bool SAL_CALL ScDatabaseRangesObj::hasElements()
{
    SolarMutexGuard aGuard;
    return ( getCount() != 0 );
}
 
// XNameAccess
 
uno::Any SAL_CALL ScDatabaseRangesObj::getByName( const OUString& aName )
{
    SolarMutexGuard aGuard;
    uno::Reference<sheet::XDatabaseRange> xRange(GetObjectByName_Impl(aName));
    if (!xRange.is())
        throw container::NoSuchElementException();
 
    return uno::Any(xRange);
}
 
uno::Sequence<OUString> SAL_CALL ScDatabaseRangesObj::getElementNames()
{
    SolarMutexGuard aGuard;
 
    //! need to omit "unnamed"?
 
    if (pDocShell)
    {
        ScDBCollection* pNames = pDocShell->GetDocument().GetDBCollection();
        if (pNames)
        {
            const ScDBCollection::NamedDBs& rDBs = pNames->getNamedDBs();
            uno::Sequence<OUString> aSeq(rDBs.size());
            auto aSeqRange = asNonConstRange(aSeq);
            size_t i = 0;
            for (const auto& rDB : rDBs)
            {
                aSeqRange[i] = rDB->GetName();
                ++i;
            }
 
            return aSeq;
        }
    }
    return {};
}
 
sal_Bool SAL_CALL ScDatabaseRangesObj::hasByName( const OUString& aName )
{
    SolarMutexGuard aGuard;
 
    //! need to omit "unnamed"?
 
    if (pDocShell)
    {
        ScDBCollection* pNames = pDocShell->GetDocument().GetDBCollection();
        if (pNames)
            return pNames->getNamedDBs().findByUpperName(ScGlobal::getCharClass().uppercase(aName)) != nullptr;
    }
    return false;
}
 
ScUnnamedDatabaseRangesObj::ScUnnamedDatabaseRangesObj(ScDocShell* pDocSh) :
    pDocShell( pDocSh )
{
    pDocShell->GetDocument().AddUnoObject(*this);
}
 
ScUnnamedDatabaseRangesObj::~ScUnnamedDatabaseRangesObj()
{
    SolarMutexGuard g;
 
    if (pDocShell)
        pDocShell->GetDocument().RemoveUnoObject(*this);
}
 
void ScUnnamedDatabaseRangesObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
{
    //  reference update does not matter here
 
    if ( rHint.GetId() == SfxHintId::Dying )
    {
        pDocShell = nullptr;
    }
}
 
// XUnnamedDatabaseRanges
 
void ScUnnamedDatabaseRangesObj::setByTable( const table::CellRangeAddress& aRange )
{
    SolarMutexGuard aGuard;
    bool bDone = false;
    if (pDocShell)
    {
        if ( pDocShell->GetDocument().GetTableCount() <= aRange.Sheet )
            throw lang::IndexOutOfBoundsException();
 
        ScDBDocFunc aFunc(*pDocShell);
        ScRange aUnnamedRange( static_cast<SCCOL>(aRange.StartColumn), static_cast<SCROW>(aRange.StartRow), aRange.Sheet,
                            static_cast<SCCOL>(aRange.EndColumn),   static_cast<SCROW>(aRange.EndRow),   aRange.Sheet );
        bDone = aFunc.AddDBRange( STR_DB_LOCAL_NONAME, aUnnamedRange );
    }
    if (!bDone)
        throw uno::RuntimeException();      // no other exceptions specified
}
 
uno::Any ScUnnamedDatabaseRangesObj::getByTable( sal_Int32 nTab )
{
    SolarMutexGuard aGuard;
    if (!pDocShell)
        throw uno::RuntimeException();
 
    if ( pDocShell->GetDocument().GetTableCount() <= nTab )
        throw lang::IndexOutOfBoundsException();
    uno::Reference<sheet::XDatabaseRange> xRange(
        new ScDatabaseRangeObj(pDocShell, static_cast<SCTAB>(nTab)));
    if (!xRange.is())
        throw container::NoSuchElementException();
 
    return uno::Any(xRange);
}
 
sal_Bool ScUnnamedDatabaseRangesObj::hasByTable( sal_Int32 nTab )
{
    SolarMutexGuard aGuard;
    if (pDocShell)
    {
        if (pDocShell->GetDocument().GetTableCount() <= nTab)
            throw lang::IndexOutOfBoundsException();
        if (pDocShell->GetDocument().GetAnonymousDBData(static_cast<SCTAB>(nTab)))
            return true;
        return false;
    }
    else
        return false;
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V614 Uninitialized variable 'nVal' used.

V524 It is odd that the body of 'removePropertyChangeListener' function is fully equivalent to the body of 'addPropertyChangeListener' function.

V524 It is odd that the body of 'removeVetoableChangeListener' function is fully equivalent to the body of 'addVetoableChangeListener' function.

V524 It is odd that the body of 'removePropertyChangeListener' function is fully equivalent to the body of 'addPropertyChangeListener' function.

V524 It is odd that the body of 'removeVetoableChangeListener' function is fully equivalent to the body of 'addVetoableChangeListener' function.

V524 It is odd that the body of 'removePropertyChangeListener' function is fully equivalent to the body of 'addPropertyChangeListener' function.

V524 It is odd that the body of 'removeVetoableChangeListener' function is fully equivalent to the body of 'addVetoableChangeListener' function.

V1029 Numeric Truncation Error. Return value of the 'size' function is written to the 16-bit variable.