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

Quelle  addincol.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 <comphelper/processfactory.hxx>
#include <i18nlangtag/languagetag.hxx>
#include <utility>
#include <vcl/svapp.hxx>
#include <vcl/settings.hxx>
#include <sfx2/objsh.hxx>
#include <unotools/charclass.hxx>
#include <sal/log.hxx>
#include <o3tl/string_view.hxx>
#include <osl/diagnose.h>

#include <com/sun/star/container/XContentEnumerationAccess.hpp>
#include <com/sun/star/frame/XModel.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/lang/XServiceName.hpp>
#include <com/sun/star/lang/XSingleServiceFactory.hpp>
#include <com/sun/star/lang/XSingleComponentFactory.hpp>
#include <com/sun/star/reflection/XIdlClass.hpp>
#include <com/sun/star/beans/XIntrospectionAccess.hpp>
#include <com/sun/star/beans/theIntrospection.hpp>
#include <com/sun/star/beans/MethodConcept.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/beans/PropertyValue.hpp>
#include <com/sun/star/table/XCellRange.hpp>
#include <com/sun/star/lang/Locale.hpp>
#include <com/sun/star/sheet/XCompatibilityNames.hpp>
#include <com/sun/star/sheet/NoConvergenceException.hpp>
#include <com/sun/star/sheet/XAddIn.hpp>
#include <com/sun/star/sheet/XVolatileResult.hpp>

#include <addincol.hxx>
#include <addinhelpid.hxx>
#include <scmatrix.hxx>
#include <formula/errorcodes.hxx>
#include <formula/funcvarargs.h>
#include <optutil.hxx>
#include <addincfg.hxx>
#include <scmod.hxx>
#include <rangeseq.hxx>
#include <funcdesc.hxx>
#include <svl/sharedstring.hxx>
#include <formulaopt.hxx>
#include <compiler.hxx>
#include <document.hxx>
#include <memory>

using namespace com::sun::star;

#define SC_CALLERPOS_NONE   (-1)

ScUnoAddInFuncData::ScUnoAddInFuncData( const OUString& rNam, const OUString& rLoc,
                                        OUString aDesc,
                                        sal_uInt16 nCat, OUString sHelp,
                                        uno::Reference<reflection::XIdlMethod> xFunc,
                                        uno::Any aO,
                                        sal_Int32 nAC, const ScAddInArgDesc* pAD,
                                        sal_Int32 nCP ) :
    aOriginalName( rNam ),
    aLocalName( rLoc ),
    aUpperName( rNam ),
    aUpperLocal( rLoc ),
    aDescription(std::move( aDesc )),
    xFunction(std::move( xFunc )),
    aObject(std::move( aO )),
    nArgCount( nAC ),
    nCallerPos( nCP ),
    nCategory( nCat ),
    sHelpId(std::move( sHelp )),
    bCompInitialized( false )
{
    if ( nArgCount )
    {
        pArgDescs.reset( new ScAddInArgDesc[nArgCount] );
        for (sal_Int32 i=0; i<nArgCount; i++)
            pArgDescs[i] = pAD[i];
    }

    aUpperName = aUpperName.toAsciiUpperCase();  // programmatic name
    aUpperLocal = ScGlobal::getCharClass().uppercase(aUpperLocal);
}

ScUnoAddInFuncData::~ScUnoAddInFuncData()
{
}

const ::std::vector<ScUnoAddInFuncData::LocalizedName>& ScUnoAddInFuncData::GetCompNames() const
{
    if ( !bCompInitialized )
    {
        //  read sequence of compatibility names on demand

        uno::Reference<sheet::XAddIn> xAddIn;
        if ( aObject >>= xAddIn )
        {
            uno::Reference<sheet::XCompatibilityNames> xComp( xAddIn, uno::UNO_QUERY );
            if ( xComp.is() && xFunction.is() )
            {
                OUString aMethodName = xFunction->getName();
                const uno::Sequence< sheet::LocalizedName> aCompNames( xComp->getCompatibilityNames( aMethodName ));
                maCompNames.clear();
                for (const sheet::LocalizedName& rCompName : aCompNames)
                {
                    maCompNames.emplace_back(
                                LanguageTag::convertToBcp47( rCompName.Locale, false),
                                rCompName.Name);
                }
            }
        }

        bCompInitialized = true;        // also if not successful
    }
    return maCompNames;
}

void ScUnoAddInFuncData::SetCompNames( ::std::vector< ScUnoAddInFuncData::LocalizedName >&& rNew )
{
    OSL_ENSURE( !bCompInitialized, "SetCompNames after initializing" );

    maCompNames = std::move(rNew);

    bCompInitialized = true;
}

void ScUnoAddInFuncData::SetEnglishName( const OUString& rEnglishName )
{
    if (!rEnglishName.isEmpty())
        aUpperEnglish = ScCompiler::GetCharClassEnglish()->uppercase(rEnglishName);
    else
    {
        // A dumb fallback to not have an empty name, mainly just for the
        // assignment to ScFuncDesc::mxFuncName for the Function Wizard and
        // formula input tooltips.
        aUpperEnglish = aUpperLocal;
    }
}

bool ScUnoAddInFuncData::GetExcelName( const LanguageTag& rDestLang, OUString& ;rRetExcelName, bool bFallbackToAny ) const
{
    const ::std::vector<LocalizedName>& rCompNames = GetCompNames();
    if ( !rCompNames.empty() )
    {
        const OUString& aSearch( rDestLang.getBcp47());

        // First, check exact match without fallback overhead.
        ::std::vector<LocalizedName>::const_iterator itNames = std::find_if(rCompNames.begin(), rCompNames.end(),
            [&aSearch](const LocalizedName& rName) { return rName.maLocale == aSearch; });
        if (itNames != rCompNames.end())
        {
            rRetExcelName = (*itNames).maName;
            return true;
        }

        // For "en-US" try the most likely fallback of "en".
        if (aSearch == "en-US")
        {
            itNames = std::find_if(rCompNames.begin(), rCompNames.end(),
                    [](const LocalizedName& rName) { return rName.maLocale == "en"; });
            if (itNames != rCompNames.end())
            {
                rRetExcelName = (*itNames).maName;
                return true;
            }
        }

        // Try match of fallback search with fallback locales,
        // appending also 'en-US' and 'en' to search if not queried.
        ::std::vector< OUString > aFallbackSearch( rDestLang.getFallbackStrings( true));
        if (aSearch != "en-US")
        {
            aFallbackSearch.emplace_back("en-US");
            if (aSearch != "en")
            {
                aFallbackSearch.emplace_back("en");
            }
        }
        for (const auto& rSearch : aFallbackSearch)
        {
            for (const auto& rCompName : rCompNames)
            {
                ::std::vector< OUString > aFallbackLocales( LanguageTag( rCompName.maLocale).getFallbackStrings(true));
                if (std::find(aFallbackLocales.begin(), aFallbackLocales.end(), rSearch) != aFallbackLocales.end())
                {
                    rRetExcelName = rCompName.maName;
                    return true;
                }
            }
        }

        if (bFallbackToAny)
        {
            // Last resort, use first (default) entry.
            rRetExcelName = rCompNames[0].maName;
            return true;
        }
    }
    return false;
}

void ScUnoAddInFuncData::SetFunction( const uno::Reference< reflection::XIdlMethod>&&nbsp;rNewFunc, const uno::Any& rNewObj )
{
    xFunction = rNewFunc;
    aObject = rNewObj;
}

void ScUnoAddInFuncData::SetArguments( sal_Int32 nNewCount, const ScAddInArgDesc* pNewDescs )
{
    nArgCount = nNewCount;
    if ( nArgCount )
    {
        pArgDescs.reset( new ScAddInArgDesc[nArgCount] );
        for (sal_Int32 i=0; i<nArgCount; i++)
            pArgDescs[i] = pNewDescs[i];
    }
    else
        pArgDescs.reset();
}

void ScUnoAddInFuncData::SetCallerPos( sal_Int32 nNewPos )
{
    nCallerPos = nNewPos;
}

ScUnoAddInCollection::ScUnoAddInCollection() :
    nFuncCount( 0 ),
    bInitialized( false )
{
}

ScUnoAddInCollection::~ScUnoAddInCollection()
{
}

void ScUnoAddInCollection::Clear()
{
    pExactHashMap.reset();
    pNameHashMap.reset();
    pLocalHashMap.reset();
    pEnglishHashMap.reset();
    ppFuncData.reset();
    nFuncCount = 0;

    bInitialized = false;
}

void ScUnoAddInCollection::Initialize()
{
    OSL_ENSURE( !bInitialized, "Initialize twice?" );

    uno::Reference<lang::XMultiServiceFactory> xManager = comphelper::getProcessServiceFactory();
    uno::Reference<container::XContentEnumerationAccess> xEnAc( xManager, uno::UNO_QUERY );
    if ( xEnAc.is() )
    {
        uno::Reference<container::XEnumeration> xEnum =
                        xEnAc->createContentEnumeration( u"com.sun.star.sheet.AddIn"_ustr );
        if ( xEnum.is() )
        {
            //  loop through all AddIns
            while ( xEnum->hasMoreElements() )
            {
                uno::Any aAddInAny = xEnum->nextElement();

                try
                {
                    uno::Reference<uno::XInterface> xIntFac;
                    aAddInAny >>= xIntFac;
                    if ( xIntFac.is() )
                    {
                        // #i59984# try XSingleComponentFactory in addition to (old) XSingleServiceFactory,
                        // passing the context to the component

                        uno::Reference<uno::XInterface> xInterface;
                        uno::Reference<uno::XComponentContext> xCtx(
                            comphelper::getComponentContext(xManager));
                        uno::Reference<lang::XSingleComponentFactory> xCFac( xIntFac, uno::UNO_QUERY );
                        if (xCFac.is())
                        {
                            xInterface = xCFac->createInstanceWithContext(xCtx);
                            if (xInterface.is())
                                ReadFromAddIn( xInterface );
                        }

                        if (!xInterface.is())
                        {
                            uno::Reference<lang::XSingleServiceFactory> xFac( xIntFac, uno::UNO_QUERY );
                            if ( xFac.is() )
                            {
                                xInterface = xFac->createInstance();
                                if (xInterface.is())
                                    ReadFromAddIn( xInterface );
                            }
                        }
                    }
                } catch ( const uno::Exception& ) {
                    SAL_WARN ( "sc""Failed to initialize create instance of sheet.AddIn" );
                }
            }
        }
    }

    // ReadConfiguration is called after looking at the AddIn implementations.
    // Duplicated are skipped (by using the service information, they don't have to be updated again
    // when argument information is needed).
    ReadConfiguration();

    bInitialized = true;        // with or without functions
}

static sal_uInt16 lcl_GetCategory( std::u16string_view rName )
{
    static const char* aFuncNames[SC_FUNCGROUP_COUNT] =
    {
        //  array index = ID - 1 (ID starts at 1)
        //  all upper case
        "Database",         // ID_FUNCTION_GRP_DATABASE
        "Date&Time",        // ID_FUNCTION_GRP_DATETIME
        "Financial",        // ID_FUNCTION_GRP_FINANCIAL
        "Information",      // ID_FUNCTION_GRP_INFO
        "Logical",          // ID_FUNCTION_GRP_LOGIC
        "Mathematical",     // ID_FUNCTION_GRP_MATH
        "Matrix",           // ID_FUNCTION_GRP_MATRIX
        "Statistical",      // ID_FUNCTION_GRP_STATISTIC
        "Spreadsheet",      // ID_FUNCTION_GRP_TABLE
        "Text",             // ID_FUNCTION_GRP_TEXT
        "Add-In"            // ID_FUNCTION_GRP_ADDINS
    };
    for (sal_uInt16 i=0; i<SC_FUNCGROUP_COUNT; i++)
        if ( o3tl::equalsAscii( rName, aFuncNames[i] ) )
            return i+1;                             // IDs start at 1

    return ID_FUNCTION_GRP_ADDINS;  // if not found, use Add-In group
}

constexpr OUStringLiteral CFGPATH_ADDINS = u"Office.CalcAddIns/AddInInfo";
constexpr OUStringLiteral CFGSTR_ADDINFUNCTIONS = u"AddInFunctions";

#define CFG_FUNCPROP_DISPLAYNAME    0
#define CFG_FUNCPROP_DESCRIPTION    1
#define CFG_FUNCPROP_CATEGORY       2
#define CFG_FUNCPROP_COUNT          3
constexpr OUString CFGSTR_DISPLAYNAME = u"DisplayName"_ustr;
constexpr OUString CFGSTR_DESCRIPTION = u"Description"_ustr;
constexpr OUString CFGSTR_CATEGORY = u"Category"_ustr;
// CategoryDisplayName is ignored for now

constexpr OUStringLiteral CFGSTR_COMPATIBILITYNAME = u"CompatibilityName";
constexpr OUStringLiteral CFGSTR_PARAMETERS = u"Parameters";

void ScUnoAddInCollection::ReadConfiguration()
{
    // called only from Initialize

    ScAddInCfg& rAddInConfig = ScModule::get()->GetAddInCfg();

    // Additional, temporary config item for the display names and
    // compatibility names.
    ScLinkConfigItem aAllLocalesConfig( CFGPATH_ADDINS, ConfigItemMode::AllLocales );
    // CommitLink is not used (only reading values)

    const OUString sSlash('/');

    // get the list of add-ins (services)
    const uno::Sequence<OUString> aServiceNames = rAddInConfig.GetNodeNames( u""_ustr );

    for ( const OUString& aServiceName : aServiceNames )
    {
        ScUnoAddInHelpIdGenerator aHelpIdGenerator( aServiceName );

        OUString aFunctionsPath(aServiceName + sSlash + CFGSTR_ADDINFUNCTIONS);

        uno::Sequence<OUString> aFunctionNames = rAddInConfig.GetNodeNames( aFunctionsPath );
        sal_Int32 nNewCount = aFunctionNames.getLength();

        // allocate pointers

        sal_Int32 nOld = nFuncCount;
        nFuncCount = nNewCount+nOld;
        if ( nOld )
        {
            std::unique_ptr<std::unique_ptr<ScUnoAddInFuncData>[]> ppNew(new std::unique_ptr<ScUnoAddInFuncData>[nFuncCount]);
            for (sal_Int32 i=0; i<nOld; i++)
                ppNew[i] = std::move(ppFuncData[i]);
            ppFuncData = std::move(ppNew);
        }
        else
            ppFuncData.reset( new std::unique_ptr<ScUnoAddInFuncData>[nFuncCount] );

        //TODO: adjust bucket count?
        if ( !pExactHashMap )
            pExactHashMap.reset( new ScAddInHashMap );
        if ( !pNameHashMap )
            pNameHashMap.reset( new ScAddInHashMap );
        if ( !pLocalHashMap )
            pLocalHashMap.reset( new ScAddInHashMap );
        if ( !pEnglishHashMap )
            pEnglishHashMap.reset( new ScAddInHashMap );

        //TODO: get the function information in a single call for all functions?

        const OUString* pFuncNameArray = aFunctionNames.getConstArray();
        for ( sal_Int32 nFuncPos = 0; nFuncPos < nNewCount; nFuncPos++ )
        {
            ppFuncData[nFuncPos+nOld] = nullptr;

            // stored function name: (service name).(function)
            OUString aFuncName = aServiceName + "." + pFuncNameArray[nFuncPos];

            // skip the function if already known (read from old AddIn service)

            if ( pExactHashMap->find( aFuncName ) == pExactHashMap->end() )
            {
                OUString aEnglishName;
                OUString aLocalName;
                OUString aDescription;
                sal_uInt16 nCategory = ID_FUNCTION_GRP_ADDINS;

                // get direct information on the function

                OUString aFuncPropPath = aFunctionsPath + sSlash + pFuncNameArray[nFuncPos] + sSlash;

                uno::Sequence<OUString> aFuncPropNames{
                    (aFuncPropPath + CFGSTR_DISPLAYNAME), // CFG_FUNCPROP_DISPLAYNAME
                    (aFuncPropPath + CFGSTR_DESCRIPTION), // CFG_FUNCPROP_DESCRIPTION
                    (aFuncPropPath + CFGSTR_CATEGORY)};   // CFG_FUNCPROP_CATEGORY

                uno::Sequence<uno::Any> aFuncProperties = rAddInConfig.GetProperties( aFuncPropNames );
                if ( aFuncProperties.getLength() == CFG_FUNCPROP_COUNT )
                {
                    aFuncProperties[CFG_FUNCPROP_DISPLAYNAME] >>= aLocalName;
                    aFuncProperties[CFG_FUNCPROP_DESCRIPTION] >>= aDescription;

                    OUString aCategoryName;
                    aFuncProperties[CFG_FUNCPROP_CATEGORY] >>= aCategoryName;
                    nCategory = lcl_GetCategory( aCategoryName );
                }

                // get English display name

                OUString aDisplayNamePath(aFuncPropPath + CFGSTR_DISPLAYNAME);
                uno::Sequence<OUString> aDisplayNamePropNames( &aDisplayNamePath, 1 );

                uno::Sequence<uno::Any> aDisplayNameProperties = aAllLocalesConfig.GetProperties( aDisplayNamePropNames );
                if ( aDisplayNameProperties.getLength() == 1 )
                {
                    uno::Sequence<beans::PropertyValue> aLocalEntries;
                    if ( aDisplayNameProperties[0] >>= aLocalEntries )
                    {
                        for (const beans::PropertyValue& rConfig : aLocalEntries)
                        {
                            // PropertyValue name is the locale ("convert" from
                            // string to canonicalize).
                            OUString aLocale( LanguageTag( rConfig.Name, true).getBcp47( false));
                            // PropertyValue value is the localized value (string in this case).
                            OUString aName;
                            rConfig.Value >>= aName;
                            // Accept 'en' and 'en-...' but prefer 'en-US'.
                            if (aLocale == "en-US" && !aName.isEmpty())
                                aEnglishName = aName;
                            else if (aEnglishName.isEmpty() && (aLocale == "en" || aLocale.startsWith("en-")))
                                aEnglishName = aName;
                        }
                    }
                }
                bool bNeedEnglish = aEnglishName.isEmpty();

                // get compatibility names

                ::std::vector<ScUnoAddInFuncData::LocalizedName> aCompNames;

                OUString aCompPath(aFuncPropPath + CFGSTR_COMPATIBILITYNAME);
                uno::Sequence<OUString> aCompPropNames( &aCompPath, 1 );

                uno::Sequence<uno::Any> aCompProperties = aAllLocalesConfig.GetProperties( aCompPropNames );
                if ( aCompProperties.getLength() == 1 )
                {
                    uno::Sequence<beans::PropertyValue> aLocalEntries;
                    if ( aCompProperties[0] >>= aLocalEntries )
                    {
                        for (const beans::PropertyValue& rConfig : aLocalEntries)
                        {
                            // PropertyValue name is the locale ("convert" from
                            // string to canonicalize).
                            OUString aLocale( LanguageTag( rConfig.Name, true).getBcp47( false));
                            // PropertyValue value is the localized value (string in this case).
                            OUString aName;
                            rConfig.Value >>= aName;
                            if (!aName.isEmpty())
                            {
                                aCompNames.emplace_back( aLocale, aName);
                                if (bNeedEnglish)
                                {
                                    // Accept 'en' and 'en-...' but prefer 'en-US'.
                                    if (aLocale == "en-US")
                                        aEnglishName = aName;
                                    else if (aEnglishName.isEmpty() && (aLocale == "en" || aLocale.startsWith("en-")))
                                        aEnglishName = aName;
                                }
                            }
                        }
                    }
                }

                // get argument info

                std::unique_ptr<ScAddInArgDesc[]> pVisibleArgs;
                sal_Int32 nVisibleCount = 0;

                OUString aArgumentsPath(aFuncPropPath + CFGSTR_PARAMETERS);

                const uno::Sequence<OUString> aArgumentNames = rAddInConfig.GetNodeNames( aArgumentsPath );
                sal_Int32 nArgumentCount = aArgumentNames.getLength();
                if ( nArgumentCount )
                {
                    // get DisplayName and Description for each argument
                    uno::Sequence<OUString> aArgPropNames( nArgumentCount * 2 );
                    OUString* pPropNameArray = aArgPropNames.getArray();

                    sal_Int32 nIndex = 0;
                    for ( const OUString& rArgName : aArgumentNames )
                    {
                        OUString aOneArgPath = aArgumentsPath + sSlash + rArgName + sSlash;

                        pPropNameArray[nIndex++] = aOneArgPath
                            + CFGSTR_DISPLAYNAME;
                        pPropNameArray[nIndex++] = aOneArgPath
                            + CFGSTR_DESCRIPTION;
                    }

                    uno::Sequence<uno::Any> aArgProperties = rAddInConfig.GetProperties( aArgPropNames );
                    if ( aArgProperties.getLength() == aArgPropNames.getLength() )
                    {
                        const OUString* pArgNameArray = aArgumentNames.getConstArray();
                        const uno::Any* pPropArray = aArgProperties.getConstArray();
                        OUString sDisplayName;
                        OUString sDescription;

                        ScAddInArgDesc aDesc;
                        aDesc.eType = SC_ADDINARG_NONE;     // arg type is not in configuration
                        aDesc.bOptional = false;

                        nVisibleCount = nArgumentCount;
                        pVisibleArgs.reset(new ScAddInArgDesc[nVisibleCount]);

                        nIndex = 0;
                        for ( sal_Int32 nArgument = 0; nArgument < nArgumentCount; nArgument++ )
                        {
                            pPropArray[nIndex++] >>= sDisplayName;
                            pPropArray[nIndex++] >>= sDescription;

                            aDesc.aInternalName = pArgNameArray[nArgument];
                            aDesc.aName         = sDisplayName;
                            aDesc.aDescription  = sDescription;

                            pVisibleArgs[nArgument] = aDesc;
                        }
                    }
                }

                OUString sHelpId = aHelpIdGenerator.GetHelpId( pFuncNameArray[nFuncPos] );

                uno::Reference<reflection::XIdlMethod> xFunc;       // remains empty
                uno::Any aObject;                                   // also empty

                // create and insert into the array

                ScUnoAddInFuncData* pData = new ScUnoAddInFuncData(
                    aFuncName, aLocalName, aDescription,
                    nCategory, sHelpId,
                    xFunc, aObject,
                    nVisibleCount, pVisibleArgs.get(), SC_CALLERPOS_NONE );

                pData->SetCompNames( std::move(aCompNames) );

                ppFuncData[nFuncPos+nOld].reset(pData);

                pExactHashMap->emplace(
                            pData->GetOriginalName(),
                            pData );
                pNameHashMap->emplace(
                            pData->GetUpperName(),
                            pData );
                pLocalHashMap->emplace(
                            pData->GetUpperLocal(),
                            pData );

                if (aEnglishName.isEmpty())
                    SAL_WARN("sc.core""no English name for " << aLocalName << " " << aFuncName);
                else
                {
                    pEnglishHashMap->emplace(
                            ScCompiler::GetCharClassEnglish()->uppercase(aEnglishName),
                            pData );
                }
                pData->SetEnglishName(aEnglishName);    // takes care of handling empty
            }
        }
    }
}

void ScUnoAddInCollection::LoadComponent( const ScUnoAddInFuncData& rFuncData )
{
    const OUString& aFullName = rFuncData.GetOriginalName();
    sal_Int32 nPos = aFullName.lastIndexOf( '.' );
    if ( nPos <= 0 )
        return;

    OUString aServiceName = aFullName.copy( 0, nPos );

    try
    {
        uno::Reference<lang::XMultiServiceFactory> xServiceFactory = comphelper::getProcessServiceFactory();
        uno::Reference<uno::XInterface> xInterface( xServiceFactory->createInstance( aServiceName ) );

        if (xInterface.is())
            UpdateFromAddIn( xInterface, aServiceName );
    }
    catch (const uno::Exception &)
    {
        SAL_WARN ("sc""Failed to create addin component '"
                  << aServiceName << "'");
    }
}

bool ScUnoAddInCollection::GetExcelName( const OUString& rCalcName,
                                        LanguageType eDestLang, OUString& rRetExcelName )
{
    const ScUnoAddInFuncData* pFuncData = GetFuncData( rCalcName );
    if ( pFuncData )
        return pFuncData->GetExcelName( LanguageTag( eDestLang), rRetExcelName);
    return false;
}

bool ScUnoAddInCollection::GetCalcName( const OUString& rExcelName, OUString& rRetCalcName )
{
    if (!bInitialized)
        Initialize();

    OUString aUpperCmp = ScGlobal::getCharClass().uppercase(rExcelName);

    for (sal_Int32 i=0; i<nFuncCount; i++)
    {
        ScUnoAddInFuncData* pFuncData = ppFuncData[i].get();
        if ( pFuncData )
        {
            const ::std::vector<ScUnoAddInFuncData::LocalizedName>& rNames = pFuncData->GetCompNames();
            auto bFound = std::any_of(rNames.begin(), rNames.end(),
                [&aUpperCmp](const ScUnoAddInFuncData::LocalizedName& rName) {
                    return ScGlobal::getCharClass().uppercase( rName.maName ) == aUpperCmp; });
            if (bFound)
            {
                //TODO: store upper case for comparing?

                //  use the first function that has this name for any language
                rRetCalcName = pFuncData->GetOriginalName();
                return true;
            }
        }
    }
    return false;
}

static bool IsTypeName( std::u16string_view rName, const uno::Type& rType )
{
    return rName == rType.getTypeName();
}

static bool lcl_ValidReturnType( const uno::Reference<reflection::XIdlClass>& xClass )
{
    //  this must match with ScUnoAddInCall::SetResult

    if ( !xClass.is() ) return false;

    switch (xClass->getTypeClass())
    {
        case uno::TypeClass_ANY:                // variable type
        case uno::TypeClass_ENUM:               //TODO: ???
        case uno::TypeClass_BOOLEAN:
        case uno::TypeClass_CHAR:
        case uno::TypeClass_BYTE:
        case uno::TypeClass_SHORT:
        case uno::TypeClass_UNSIGNED_SHORT:
        case uno::TypeClass_LONG:
        case uno::TypeClass_UNSIGNED_LONG:
        case uno::TypeClass_FLOAT:
        case uno::TypeClass_DOUBLE:
        case uno::TypeClass_STRING:
            return true;                        // values or string

        case uno::TypeClass_INTERFACE:
            {
                //  return type XInterface may contain a XVolatileResult
                //TODO: XIdlClass needs getType() method!

                OUString sName = xClass->getName();
                return (
                    IsTypeName( sName, cppu::UnoType<sheet::XVolatileResult>::get()) ||
                    IsTypeName( sName, cppu::UnoType<uno::XInterface>::get()) );
            }

        default:
            {
                //  nested sequences for arrays
                //TODO: XIdlClass needs getType() method!

                OUString sName = xClass->getName();
                return (
                    IsTypeName( sName, cppu::UnoType<uno::Sequence< uno::Sequence<sal_Int32> >>::get() ) ||
                    IsTypeName( sName, cppu::UnoType<uno::Sequence< uno::Sequence<double> >>::get() ) ||
                    IsTypeName( sName, cppu::UnoType<uno::Sequence< uno::Sequence<OUString> >>::get() ) ||
                    IsTypeName( sName, cppu::UnoType<uno::Sequence< uno::Sequence<uno::Any> >>::get() ) );
            }
    }
}

static ScAddInArgumentType lcl_GetArgType( const uno::Reference<reflection::XIdlClass>&&nbsp;xClass )
{
    if (!xClass.is())
        return SC_ADDINARG_NONE;

    uno::TypeClass eType = xClass->getTypeClass();

    if ( eType == uno::TypeClass_LONG )             //TODO: other integer types?
        return SC_ADDINARG_INTEGER;

    if ( eType == uno::TypeClass_DOUBLE )
        return SC_ADDINARG_DOUBLE;

    if ( eType == uno::TypeClass_STRING )
        return SC_ADDINARG_STRING;

    //TODO: XIdlClass needs getType() method!
    OUString sName = xClass->getName();

    if (IsTypeName( sName, cppu::UnoType<uno::Sequence< uno::Sequence<sal_Int32> >>::get() ))
        return SC_ADDINARG_INTEGER_ARRAY;

    if (IsTypeName( sName, cppu::UnoType<uno::Sequence< uno::Sequence<double> >>::get() ))
        return SC_ADDINARG_DOUBLE_ARRAY;

    if (IsTypeName( sName, cppu::UnoType<uno::Sequence< uno::Sequence<OUString> >>::get() ))
        return SC_ADDINARG_STRING_ARRAY;

    if (IsTypeName( sName, cppu::UnoType<uno::Sequence< uno::Sequence<uno::Any> >>::get() ))
        return SC_ADDINARG_MIXED_ARRAY;

    if (IsTypeName( sName, cppu::UnoType<uno::Any>::get()))
        return SC_ADDINARG_VALUE_OR_ARRAY;

    if (IsTypeName( sName, cppu::UnoType<table::XCellRange>::get()))
        return SC_ADDINARG_CELLRANGE;

    if (IsTypeName( sName, cppu::UnoType<beans::XPropertySet>::get()))
        return SC_ADDINARG_CALLER;

    if (IsTypeName( sName, cppu::UnoType<uno::Sequence<uno::Any>>::get() ))
        return SC_ADDINARG_VARARGS;

    return SC_ADDINARG_NONE;
}

void ScUnoAddInCollection::ReadFromAddIn( const uno::Reference<uno::XInterface>& xInterface )
{
    uno::Reference<sheet::XAddIn> xAddIn( xInterface, uno::UNO_QUERY );
    uno::Reference<lang::XServiceName> xName( xInterface, uno::UNO_QUERY );
    if ( !(xAddIn.is() && xName.is()) )
        return;

    // Even if GetUseEnglishFunctionName() would return true, do not set the
    // locale to en-US to get English function names as that also would mix in
    // English descriptions and parameter names. Also, setting a locale will
    // reinitialize the Add-In completely, so switching back and forth isn't a
    // good idea either.
    xAddIn->setLocale( Application::GetSettings().GetUILanguageTag().getLocale());

    // Instead, in a second run with 'en-US' obtain English names.
    struct FuncNameData
    {
        OUString            aFuncU;
        ScUnoAddInFuncData* pData;
    };
    std::vector<FuncNameData> aFuncNameData;

    OUString aServiceName( xName->getServiceName() );
    ScUnoAddInHelpIdGenerator aHelpIdGenerator( aServiceName );

    //TODO: pass XIntrospection to ReadFromAddIn

    const uno::Reference<uno::XComponentContext>& xContext = comphelper::getProcessComponentContext();

    uno::Reference<beans::XIntrospection> xIntro = beans::theIntrospection::get( xContext );
    uno::Any aObject;
    aObject <<= xAddIn;
    uno::Reference<beans::XIntrospectionAccess> xAcc = xIntro->inspect(aObject);
    if (!xAcc.is())
        return;

    uno::Sequence< uno::Reference<reflection::XIdlMethod> > aMethods =
            xAcc->getMethods( beans::MethodConcept::ALL );
    sal_Int32 nNewCount = aMethods.getLength();
    if ( !nNewCount )
        return;

    sal_Int32 nOld = nFuncCount;
    nFuncCount = nNewCount+nOld;
    if ( nOld )
    {
        std::unique_ptr<std::unique_ptr<ScUnoAddInFuncData>[]> ppNew(new std::unique_ptr<ScUnoAddInFuncData>[nFuncCount]);
        for (sal_Int32 i=0; i<nOld; i++)
            ppNew[i] = std::move(ppFuncData[i]);
        ppFuncData = std::move(ppNew);
    }
    else
        ppFuncData.reset(new std::unique_ptr<ScUnoAddInFuncData>[nFuncCount]);

    //TODO: adjust bucket count?
    if ( !pExactHashMap )
        pExactHashMap.reset( new ScAddInHashMap );
    if ( !pNameHashMap )
        pNameHashMap.reset( new ScAddInHashMap );
    if ( !pLocalHashMap )
        pLocalHashMap.reset( new ScAddInHashMap );
    if ( !pEnglishHashMap )
        pEnglishHashMap.reset( new ScAddInHashMap );

    const uno::Reference<reflection::XIdlMethod>* pArray = aMethods.getConstArray();
    for (sal_Int32 nFuncPos=0; nFuncPos<nNewCount; nFuncPos++)
    {
        ppFuncData[nFuncPos+nOld] = nullptr;

        uno::Reference<reflection::XIdlMethod> xFunc = pArray[nFuncPos];
        if (xFunc.is())
        {
            //  leave out internal functions
            uno::Reference<reflection::XIdlClass> xClass =
                            xFunc->getDeclaringClass();
            bool bSkip = true;
            if ( xClass.is() )
            {
                //TODO: XIdlClass needs getType() method!
                OUString sName = xClass->getName();
                bSkip = (
                    IsTypeName( sName,
                        cppu::UnoType<uno::XInterface>::get()) ||
                    IsTypeName( sName,
                        cppu::UnoType<lang::XServiceName>::get()) ||
                    IsTypeName( sName,
                        cppu::UnoType<lang::XServiceInfo>::get()) ||
                    IsTypeName( sName,
                        cppu::UnoType<sheet::XAddIn>::get()) );
            }
            if (!bSkip)
            {
                uno::Reference<reflection::XIdlClass> xReturn =
                            xFunc->getReturnType();
                if ( !lcl_ValidReturnType( xReturn ) )
                    bSkip = true;
            }
            if (!bSkip)
            {
                OUString aFuncU = xFunc->getName();

                // stored function name: (service name).(function)
                OUString aFuncName = aServiceName + "." + aFuncU;

                bool bValid = true;
                sal_Int32 nVisibleCount = 0;
                sal_Int32 nCallerPos = SC_CALLERPOS_NONE;

                uno::Sequence<reflection::ParamInfo> aParams =
                        xFunc->getParameterInfos();
                sal_Int32 nParamCount = aParams.getLength();
                const reflection::ParamInfo* pParArr = aParams.getConstArray();
                sal_Int32 nParamPos;
                for (nParamPos=0; nParamPos<nParamCount; nParamPos++)
                {
                    if ( pParArr[nParamPos].aMode != reflection::ParamMode_IN )
                        bValid = false;
                    uno::Reference<reflection::XIdlClass> xParClass =
                                pParArr[nParamPos].aType;
                    ScAddInArgumentType eArgType = lcl_GetArgType( xParClass );
                    if ( eArgType == SC_ADDINARG_NONE )
                        bValid = false;
                    else if ( eArgType == SC_ADDINARG_CALLER )
                        nCallerPos = nParamPos;
                    else
                        ++nVisibleCount;
                }
                if (bValid)
                {
                    sal_uInt16 nCategory = lcl_GetCategory(
                            xAddIn->getProgrammaticCategoryName( aFuncU ) );

                    OUString sHelpId = aHelpIdGenerator.GetHelpId( aFuncU );

                    OUString aLocalName;
                    try
                    {
                        aLocalName = xAddIn->
                            getDisplayFunctionName( aFuncU );
                    }
                    catch(uno::Exception&)
                    {
                        aLocalName = "###";
                    }

                    OUString aDescription;
                    try
                    {
                        aDescription = xAddIn->
                            getFunctionDescription( aFuncU );
                    }
                    catch(uno::Exception&)
                    {
                        aDescription = "###";
                    }

                    std::unique_ptr<ScAddInArgDesc[]> pVisibleArgs;
                    if ( nVisibleCount > 0 )
                    {
                        ScAddInArgDesc aDesc;
                        pVisibleArgs.reset(new ScAddInArgDesc[nVisibleCount]);
                        sal_Int32 nDestPos = 0;
                        for (nParamPos=0; nParamPos<nParamCount; nParamPos++)
                        {
                            uno::Reference<reflection::XIdlClass> xParClass =
                                pParArr[nParamPos].aType;
                            ScAddInArgumentType eArgType = lcl_GetArgType( xParClass );
                            if ( eArgType != SC_ADDINARG_CALLER )
                            {
                                OUString aArgName;
                                try
                                {
                                    aArgName = xAddIn->
                                        getDisplayArgumentName( aFuncU, nParamPos );
                                }
                                catch(uno::Exception&)
                                {
                                    aArgName = "###";
                                }
                                OUString aArgDesc;
                                try
                                {
                                    aArgDesc = xAddIn->
                                        getArgumentDescription( aFuncU, nParamPos );
                                }
                                catch(uno::Exception&)
                                {
                                    aArgDesc = "###";
                                }

                                bool bOptional =
                                    ( eArgType == SC_ADDINARG_VALUE_OR_ARRAY ||
                                      eArgType == SC_ADDINARG_VARARGS );

                                aDesc.eType = eArgType;
                                aDesc.aName = aArgName;
                                aDesc.aDescription = aArgDesc;
                                aDesc.bOptional = bOptional;
                                //TODO: initialize aInternalName only from config?
                                aDesc.aInternalName = pParArr[nParamPos].aName;

                                pVisibleArgs[nDestPos++] = aDesc;
                            }
                        }
                        OSL_ENSURE( nDestPos==nVisibleCount, "wrong count" );
                    }

                    ppFuncData[nFuncPos+nOld].reset( new ScUnoAddInFuncData(
                        aFuncName, aLocalName, aDescription,
                        nCategory, sHelpId,
                        xFunc, aObject,
                        nVisibleCount, pVisibleArgs.get(), nCallerPos ) );

                    ScUnoAddInFuncData* pData = ppFuncData[nFuncPos+nOld].get();
                    pExactHashMap->emplace(
                                pData->GetOriginalName(),
                                pData );
                    pNameHashMap->emplace(
                                pData->GetUpperName(),
                                pData );
                    pLocalHashMap->emplace(
                                pData->GetUpperLocal(),
                                pData );

                    aFuncNameData.push_back({aFuncU, pData});
                }
            }
        }
    }

    const LanguageTag aEnglishLanguageTag(LANGUAGE_ENGLISH_US);
    xAddIn->setLocale( aEnglishLanguageTag.getLocale());
    for (const auto& rFunc : aFuncNameData)
    {
        OUString aEnglishName;
        try
        {
            aEnglishName = xAddIn->getDisplayFunctionName( rFunc.aFuncU );
        }
        catch(uno::Exception&)
        {
        }
        if (aEnglishName.isEmpty()
                && rFunc.pData->GetExcelName( aEnglishLanguageTag, aEnglishName, false /*bFallbackToAny*/))
        {
            // Check our known suffixes and append if not present. Note this
            // depends on localization (that should not add such suffix, but..)
            // and is really only a last resort.
            if (rFunc.pData->GetLocalName().endsWith("_ADD") && !aEnglishName.endsWith("_ADD"))
                aEnglishName += "_ADD";
            else if (rFunc.pData->GetLocalName().endsWith("_EXCEL2003") && !aEnglishName.endsWith("_EXCEL2003"))
                aEnglishName += "_EXCEL2003";
            SAL_WARN("sc.core""obtaining English name for " << rFunc.pData->GetLocalName() << " "
                    << rFunc.pData->GetOriginalName() << " as ExcelName '" << aEnglishName << "'");
        }
        SAL_WARN_IF(aEnglishName.isEmpty(), "sc.core""no English name for "
                << rFunc.pData->GetLocalName() << " " << rFunc.pData->GetOriginalName());
        rFunc.pData->SetEnglishName(aEnglishName);  // takes care of handling empty
        pEnglishHashMap->emplace( rFunc.pData->GetUpperEnglish(), rFunc.pData);
    }
}

static void lcl_UpdateFunctionList( const ScFunctionList& rFunctionList, const ScUnoAddInFuncData& rFuncData,
        bool bEnglishFunctionNames )
{
    // as used in FillFunctionDescFromData
    const OUString& aCompare = (bEnglishFunctionNames ? rFuncData.GetUpperEnglish() : rFuncData.GetUpperLocal());

    sal_uLong nCount = rFunctionList.GetCount();
    for (sal_uLong nPos=0; nPos<nCount; nPos++)
    {
        const ScFuncDesc* pDesc = rFunctionList.GetFunction( nPos );
        if ( pDesc && pDesc->mxFuncName && *pDesc->mxFuncName == aCompare )
        {
            ScUnoAddInCollection::FillFunctionDescFromData( rFuncData, *const_cast<ScFuncDesc*>(pDesc),
                    bEnglishFunctionNames);
            break;
        }
    }
}

static const ScAddInArgDesc* lcl_FindArgDesc( const ScUnoAddInFuncData& rFuncData, std::u16string_view rArgIntName )
{
    sal_Int32 nArgCount = rFuncData.GetArgumentCount();
    const ScAddInArgDesc* pArguments = rFuncData.GetArguments();
    for (sal_Int32 nPos=0; nPos<nArgCount; nPos++)
    {
        if ( pArguments[nPos].aInternalName == rArgIntName )
            return &pArguments[nPos];
    }
    return nullptr;
}

void ScUnoAddInCollection::UpdateFromAddIn( const uno::Reference<uno::XInterface>& ;xInterface,
                                            std::u16string_view rServiceName )
{
    const bool bEnglishFunctionNames = ScModule::get()->GetFormulaOptions().GetUseEnglishFuncName();
    uno::Reference<lang::XLocalizable> xLoc( xInterface, uno::UNO_QUERY );
    if ( xLoc.is() )        // optional in new add-ins
        xLoc->setLocale( Application::GetSettings().GetUILanguageTag().getLocale());

    // if function list was already initialized, it must be updated

    ScFunctionList* pFunctionList = nullptr;
    if ( ScGlobal::HasStarCalcFunctionList() )
        pFunctionList = ScGlobal::GetStarCalcFunctionList();

    // only get the function information from Introspection

    const uno::Reference<uno::XComponentContext>& xContext = comphelper::getProcessComponentContext();

    uno::Reference<beans::XIntrospection> xIntro = beans::theIntrospection::get(xContext);
    uno::Any aObject;
    aObject <<= xInterface;
    uno::Reference<beans::XIntrospectionAccess> xAcc = xIntro->inspect(aObject);
    if (!xAcc.is())
        return;

    const uno::Sequence< uno::Reference<reflection::XIdlMethod> > aMethods =
            xAcc->getMethods( beans::MethodConcept::ALL );
    for (const uno::Reference<reflection::XIdlMethod>& xFunc : aMethods)
    {
        if (xFunc.is())
        {
            OUString aFuncU = xFunc->getName();

            // stored function name: (service name).(function)
            OUString aFuncName = OUString::Concat(rServiceName) + "." + aFuncU;

            // internal names are skipped because no FuncData exists
            ScUnoAddInFuncData* pOldData = const_cast<ScUnoAddInFuncData*>( GetFuncData( aFuncName ) );
            if ( pOldData )
            {
                // Create new (complete) argument info.
                // As in ReadFromAddIn, the reflection information is authoritative.
                // Local names and descriptions from pOldData are looked up using the
                // internal argument name.

                bool bValid = true;
                sal_Int32 nVisibleCount = 0;
                sal_Int32 nCallerPos = SC_CALLERPOS_NONE;

                const uno::Sequence<reflection::ParamInfo> aParams =
                        xFunc->getParameterInfos();
                sal_Int32 nParamCount = aParams.getLength();
                const reflection::ParamInfo* pParArr = aParams.getConstArray();
                for (sal_Int32 nParamPos=0; nParamPos<nParamCount; nParamPos++)
                {
                    if ( pParArr[nParamPos].aMode != reflection::ParamMode_IN )
                        bValid = false;
                    uno::Reference<reflection::XIdlClass> xParClass =
                                pParArr[nParamPos].aType;
                    ScAddInArgumentType eArgType = lcl_GetArgType( xParClass );
                    if ( eArgType == SC_ADDINARG_NONE )
                        bValid = false;
                    else if ( eArgType == SC_ADDINARG_CALLER )
                        nCallerPos = nParamPos;
                    else
                        ++nVisibleCount;
                }
                if (bValid)
                {
                    std::unique_ptr<ScAddInArgDesc[]> pVisibleArgs;
                    if ( nVisibleCount > 0 )
                    {
                        ScAddInArgDesc aDesc;
                        pVisibleArgs.reset(new ScAddInArgDesc[nVisibleCount]);
                        sal_Int32 nDestPos = 0;
                        for (const auto& rParam : aParams)
                        {
                            uno::Reference<reflection::XIdlClass> xParClass =
                                rParam.aType;
                            ScAddInArgumentType eArgType = lcl_GetArgType( xParClass );
                            if ( eArgType != SC_ADDINARG_CALLER )
                            {
                                const ScAddInArgDesc* pOldArgDesc =
                                    lcl_FindArgDesc( *pOldData, rParam.aName );
                                if ( pOldArgDesc )
                                {
                                    aDesc.aName = pOldArgDesc->aName;
                                    aDesc.aDescription = pOldArgDesc->aDescription;
                                }
                                else
                                    aDesc.aName = aDesc.aDescription = "###";

                                bool bOptional =
                                    ( eArgType == SC_ADDINARG_VALUE_OR_ARRAY ||
                                      eArgType == SC_ADDINARG_VARARGS );

                                aDesc.eType = eArgType;
                                aDesc.bOptional = bOptional;
                                //TODO: initialize aInternalName only from config?
                                aDesc.aInternalName = rParam.aName;

                                pVisibleArgs[nDestPos++] = aDesc;
                            }
                        }
                        OSL_ENSURE( nDestPos==nVisibleCount, "wrong count" );
                    }

                    pOldData->SetFunction( xFunc, aObject );
                    pOldData->SetArguments( nVisibleCount, pVisibleArgs.get() );
                    pOldData->SetCallerPos( nCallerPos );

                    if ( pFunctionList )
                        lcl_UpdateFunctionList( *pFunctionList, *pOldData, bEnglishFunctionNames );
                }
            }
        }
    }
}

OUString ScUnoAddInCollection::FindFunction( const OUString& rUpperName, bool bLocalFirst )
{
    if (!bInitialized)
        Initialize();

    if (nFuncCount == 0)
        return OUString();

    if ( bLocalFirst )
    {
        // Only scan local names (used for entering formulas).

        ScAddInHashMap::const_iterator iLook( pLocalHashMap->find( rUpperName ) );
        if ( iLook != pLocalHashMap->end() )
            return iLook->second->GetOriginalName();
    }
    else
    {
        // First scan international programmatic names (used when calling a
        // function).

        ScAddInHashMap::const_iterator iLook( pNameHashMap->find( rUpperName ) );
        if ( iLook != pNameHashMap->end() )
            return iLook->second->GetOriginalName();

        // Then scan English names (as FunctionAccess API could expect).

        iLook = pEnglishHashMap->find( rUpperName );
        if ( iLook != pEnglishHashMap->end() )
            return iLook->second->GetOriginalName();

        // After that, scan all local names; either to allow replacing old
        // AddIns with Uno, or for functions where the AddIn did not provide an
        // English name.

        iLook = pLocalHashMap->find( rUpperName );
        if ( iLook != pLocalHashMap->end() )
            return iLook->second->GetOriginalName();
    }

    return OUString();
}

const ScUnoAddInFuncData* ScUnoAddInCollection::GetFuncData( const OUString& rName, bool bComplete )
{
    if (!bInitialized)
        Initialize();

    //  rName must be the exact internal name

    ScAddInHashMap::const_iterator iLook( pExactHashMap->find( rName ) );
    if ( iLook != pExactHashMap->end() )
    {
        const ScUnoAddInFuncData* pFuncData = iLook->second;

        if ( bComplete && !pFuncData->GetFunction().is() )           //TODO: extra flag?
            LoadComponent( *pFuncData );

        return pFuncData;
    }

    return nullptr;
}

const ScUnoAddInFuncData* ScUnoAddInCollection::GetFuncData( sal_Int32 nIndex )
{
    if (!bInitialized)
        Initialize();

    if (nIndex < nFuncCount)
        return ppFuncData[nIndex].get();
    return nullptr;
}

void ScUnoAddInCollection::LocalizeString( OUString& rName )
{
    if (!bInitialized)
        Initialize();

    //  modify rName - input: exact name

    ScAddInHashMap::const_iterator iLook( pExactHashMap->find( rName ) );
    if ( iLook != pExactHashMap->end() )
        rName = iLook->second->GetUpperLocal();         //TODO: upper?
}

sal_Int32 ScUnoAddInCollection::GetFuncCount()
{
    if (!bInitialized)
        Initialize();

    return nFuncCount;
}

bool ScUnoAddInCollection::FillFunctionDesc( sal_Int32 nFunc, ScFuncDesc& rDesc, bool bEnglishFunctionNames )
{
    if (!bInitialized)
        Initialize();

    if (nFunc >= nFuncCount || !ppFuncData[nFunc])
        return false;

    const ScUnoAddInFuncData& rFuncData = *ppFuncData[nFunc];

    return FillFunctionDescFromData( rFuncData, rDesc, bEnglishFunctionNames );
}

bool ScUnoAddInCollection::FillFunctionDescFromData( const ScUnoAddInFuncData& rFuncData, ScFuncDesc& rDesc,
        bool bEnglishFunctionNames )
{
    rDesc.Clear();

    bool bIncomplete = !rFuncData.GetFunction().is();       //TODO: extra flag?

    sal_Int32 nArgCount = rFuncData.GetArgumentCount();
    if ( nArgCount > SAL_MAX_UINT16 )
        return false;

    if ( bIncomplete )
        nArgCount = 0;      // if incomplete, fill without argument info (no wrong order)

    // nFIndex is set from outside

    rDesc.mxFuncName = (bEnglishFunctionNames ? rFuncData.GetUpperEnglish() : rFuncData.GetUpperLocal());
    rDesc.nCategory = rFuncData.GetCategory();
    rDesc.sHelpId = rFuncData.GetHelpId();

    OUString aDesc = rFuncData.GetDescription();
    if (aDesc.isEmpty())
        aDesc = rFuncData.GetLocalName();      // use name if no description is available
    rDesc.mxFuncDesc = aDesc ;

    // AddInArgumentType_CALLER is already left out in FuncData

    rDesc.nArgCount = static_cast<sal_uInt16>(nArgCount);
    if ( nArgCount )
    {
        bool bMultiple = false;
        const ScAddInArgDesc* pArgs = rFuncData.GetArguments();

        rDesc.maDefArgNames.clear();
        rDesc.maDefArgNames.resize(nArgCount);
        rDesc.maDefArgDescs.clear();
        rDesc.maDefArgDescs.resize(nArgCount);
        rDesc.pDefArgFlags   = new ScFuncDesc::ParameterFlags[nArgCount];
        for ( sal_Int32 nArg=0; nArg<nArgCount; nArg++ )
        {
            rDesc.maDefArgNames[nArg] = pArgs[nArg].aName;
            rDesc.maDefArgDescs[nArg] = pArgs[nArg].aDescription;
            rDesc.pDefArgFlags[nArg].bOptional = pArgs[nArg].bOptional;

            // no empty names...
            if (rDesc.maDefArgNames[nArg].isEmpty())
                rDesc.maDefArgNames[nArg] = "arg" + OUString::number(nArg + 1);

            //  last argument repeated?
            if ( nArg+1 == nArgCount && ( pArgs[nArg].eType == SC_ADDINARG_VARARGS ) )
                bMultiple = true;
        }

        if ( bMultiple )
            rDesc.nArgCount += VAR_ARGS - 1;    // VAR_ARGS means just one repeated arg
    }

    rDesc.bIncomplete = bIncomplete;

    return true;
}

ScUnoAddInCall::ScUnoAddInCall( ScDocument& rDoc, ScUnoAddInCollection& rColl, const OUString& rName,
                                sal_Int32 nParamCount ) :
    mrDoc( rDoc ),
    bValidCount( false ),
    nErrCode( FormulaError::NoCode ),      // before function was called
    bHasString( true ),
    fValue( 0.0 ),
    xMatrix( nullptr )
{
    pFuncData = rColl.GetFuncData( rName, true );           // need fully initialized data
    OSL_ENSURE( pFuncData, "Function Data missing" );
    if ( !pFuncData )
        return;

    sal_Int32 nDescCount = pFuncData->GetArgumentCount();
    const ScAddInArgDesc* pArgs = pFuncData->GetArguments();

    //  is aVarArg sequence needed?
    if ( nParamCount >= nDescCount && nDescCount > 0 &&
         pArgs[nDescCount-1].eType == SC_ADDINARG_VARARGS )
    {
        sal_Int32 nVarCount = nParamCount - ( nDescCount - 1 );  // size of last argument
        aVarArg.realloc( nVarCount );
        bValidCount = true;
    }
    else if ( nParamCount <= nDescCount )
    {
        //  all args behind nParamCount must be optional
        bValidCount = true;
        for (sal_Int32 i=nParamCount; i<nDescCount; i++)
            if ( !pArgs[i].bOptional )
                bValidCount = false;
    }
    // else invalid (too many arguments)

    if ( bValidCount )
        aArgs.realloc( nDescCount );    // sequence must always match function signature
}

ScUnoAddInCall::~ScUnoAddInCall()
{
    // pFuncData is deleted with ScUnoAddInCollection
}

ScAddInArgumentType ScUnoAddInCall::GetArgType( sal_Int32 nPos )
{
    if ( pFuncData )
    {
        sal_Int32 nCount = pFuncData->GetArgumentCount();
        const ScAddInArgDesc* pArgs = pFuncData->GetArguments();

        // if last arg is sequence, use "any" type
        if ( nCount > 0 && nPos >= nCount-1 && pArgs[nCount-1].eType == SC_ADDINARG_VARARGS )
            return SC_ADDINARG_VALUE_OR_ARRAY;

        if ( nPos < nCount )
            return pArgs[nPos].eType;
    }
    return SC_ADDINARG_VALUE_OR_ARRAY;      //TODO: error code !!!!
}

bool ScUnoAddInCall::NeedsCaller() const
{
    return pFuncData && pFuncData->GetCallerPos() != SC_CALLERPOS_NONE;
}

void ScUnoAddInCall::SetCaller( const uno::Reference<uno::XInterface>& rInterface )
{
    xCaller = rInterface;
}

void ScUnoAddInCall::SetCallerFromObjectShell( const SfxObjectShell* pObjSh )
{
    if (pObjSh)
    {
        uno::Reference<uno::XInterface> xInt( pObjSh->GetBaseModel(), uno::UNO_QUERY );
        SetCaller( xInt );
    }
}

void ScUnoAddInCall::SetParam( sal_Int32 nPos, const uno::Any& rValue )
{
    if ( !pFuncData )
        return;

    sal_Int32 nCount = pFuncData->GetArgumentCount();
    const ScAddInArgDesc* pArgs = pFuncData->GetArguments();
    if ( nCount > 0 && nPos >= nCount-1 && pArgs[nCount-1].eType == SC_ADDINARG_VARARGS )
    {
        sal_Int32 nVarPos = nPos-(nCount-1);
        if ( nVarPos < aVarArg.getLength() )
            aVarArg.getArray()[nVarPos] = rValue;
        else
        {
            OSL_FAIL("wrong argument number");
        }
    }
    else if ( nPos < aArgs.getLength() )
        aArgs.getArray()[nPos] = rValue;
    else
    {
        OSL_FAIL("wrong argument number");
    }
}

void ScUnoAddInCall::ExecuteCall()
{
    if ( !pFuncData )
        return;

    sal_Int32 nCount = pFuncData->GetArgumentCount();
    const ScAddInArgDesc* pArgs = pFuncData->GetArguments();
    if ( nCount > 0 && pArgs[nCount-1].eType == SC_ADDINARG_VARARGS )
    {
        //  insert aVarArg as last argument
        //TODO: after inserting caller (to prevent copying twice)?

        OSL_ENSURE( aArgs.getLength() == nCount, "wrong argument count" );
        aArgs.getArray()[nCount-1] <<= aVarArg;
    }

    if ( pFuncData->GetCallerPos() != SC_CALLERPOS_NONE )
    {
        uno::Any aCallerAny;
        aCallerAny <<= xCaller;

        sal_Int32 nUserLen = aArgs.getLength();
        sal_Int32 nCallPos = pFuncData->GetCallerPos();
        if (nCallPos>nUserLen)                          // should not happen
        {
            OSL_FAIL("wrong CallPos");
            nCallPos = nUserLen;
        }

        sal_Int32 nDestLen = nUserLen + 1;
        uno::Sequence<uno::Any> aRealArgs( nDestLen );
        uno::Any* pDest = aRealArgs.getArray();

        pDest = std::copy_n(std::cbegin(aArgs), nCallPos, pDest);
        *pDest = std::move(aCallerAny);
        std::copy(std::next(std::cbegin(aArgs), nCallPos), std::cend(aArgs), std::next(pDest));

        ExecuteCallWithArgs( aRealArgs );
    }
    else
        ExecuteCallWithArgs( aArgs );
}

void ScUnoAddInCall::ExecuteCallWithArgs(uno::Sequence<uno::Any>& rCallArgs)
{
    //  rCallArgs may not match argument descriptions (because of caller)

    uno::Reference<reflection::XIdlMethod> xFunction;
    uno::Any aObject;
    if ( pFuncData )
    {
        xFunction = pFuncData->GetFunction();
        aObject = pFuncData->GetObject();
    }

    if ( !xFunction.is() )
        return;

    uno::Any aAny;
    nErrCode = FormulaError::NONE;

    try
    {
        aAny = xFunction->invoke( aObject, rCallArgs );
    }
    catch(lang::IllegalArgumentException&)
    {
        nErrCode = FormulaError::IllegalArgument;
    }
    catch(const reflection::InvocationTargetException& rWrapped)
    {
        if ( rWrapped.TargetException.getValueType().equals(
                cppu::UnoType<lang::IllegalArgumentException>::get()) )
            nErrCode = FormulaError::IllegalArgument;
        else if ( rWrapped.TargetException.getValueType().equals(
                cppu::UnoType<sheet::NoConvergenceException>::get()) )
            nErrCode = FormulaError::NoConvergence;
        else
            nErrCode = FormulaError::NoValue;
    }
    catch(uno::Exception&)
    {
        nErrCode = FormulaError::NoValue;
    }

    if (nErrCode == FormulaError::NONE)
        SetResult( aAny );      // convert result to Calc types
}

template <typename T>
static sal_Int32 lcl_GetMaxColCount(const uno::Sequence< uno::Sequence<T> >* pRowSeq)
{
    if (!pRowSeq->hasElements())
        return 0;

    auto pRow = std::max_element(pRowSeq->begin(), pRowSeq->end(),
        [](const uno::Sequence<T>& a, const uno::Sequence<T>& b) {
            return a.getLength() < b.getLength(); });
    return pRow->getLength();
}

void ScUnoAddInCall::SetResult( const uno::Any& rNewRes )
{
    nErrCode = FormulaError::NONE;
    xVarRes = nullptr;

    // Reflection* pRefl = rNewRes.getReflection();

    uno::TypeClass eClass = rNewRes.getValueTypeClass();
    const uno::Type& aType = rNewRes.getValueType();
    switch (eClass)
    {
        case uno::TypeClass_VOID:
            nErrCode = FormulaError::NotAvailable;         // #NA
            break;

        case uno::TypeClass_ENUM:
        case uno::TypeClass_BOOLEAN:
        case uno::TypeClass_CHAR:
        case uno::TypeClass_BYTE:
        case uno::TypeClass_SHORT:
        case uno::TypeClass_UNSIGNED_SHORT:
        case uno::TypeClass_LONG:
        case uno::TypeClass_UNSIGNED_LONG:
        case uno::TypeClass_FLOAT:
        case uno::TypeClass_DOUBLE:
            {
                uno::TypeClass eMyClass;
                ScApiTypeConversion::ConvertAnyToDouble( fValue, eMyClass, rNewRes);
                bHasString = false;
            }
            break;

        case uno::TypeClass_STRING:
            {
                rNewRes >>= aString;
                bHasString = true;
            }
            break;

        case uno::TypeClass_INTERFACE:
            {
                //TODO: directly extract XVolatileResult from any?
                uno::Reference<uno::XInterface> xInterface;
                rNewRes >>= xInterface;
                if ( xInterface.is() )
                    xVarRes.set( xInterface, uno::UNO_QUERY );

                if (!xVarRes.is())
                    nErrCode = FormulaError::NoValue;          // unknown interface
            }
            break;

        default:
            if ( aType.equals( cppu::UnoType<uno::Sequence< uno::Sequence<sal_Int32> >>::get() ) )
            {
                const uno::Sequence< uno::Sequence<sal_Int32> >* pRowSeq = nullptr;

                //TODO: use pointer from any!
                uno::Sequence< uno::Sequence<sal_Int32> > aSequence;
                if ( rNewRes >>= aSequence )
                    pRowSeq = &aSequence;

                if ( pRowSeq )
                {
                    sal_Int32 nRowCount = pRowSeq->getLength();
                    sal_Int32 nMaxColCount = lcl_GetMaxColCount(pRowSeq);
                    if ( nMaxColCount && nRowCount )
                    {
                        const uno::Sequence<sal_Int32>* pRowArr = pRowSeq->getConstArray();
                        xMatrix = new ScMatrix(
                                static_cast<SCSIZE>(nMaxColCount),
                                static_cast<SCSIZE>(nRowCount), 0.0);
                        for (sal_Int32 nRow=0; nRow<nRowCount; nRow++)
                        {
                            sal_Int32 nColCount = pRowArr[nRow].getLength();
                            const sal_Int32* pColArr = pRowArr[nRow].getConstArray();
                            for (sal_Int32 nCol=0; nCol<nColCount; nCol++)
                                xMatrix->PutDouble( pColArr[nCol],
                                        static_cast<SCSIZE>(nCol),
                                        static_cast<SCSIZE>(nRow) );
                            for (sal_Int32 nCol=nColCount; nCol<nMaxColCount; nCol++)
                                xMatrix->PutDouble( 0.0,
                                        static_cast<SCSIZE>(nCol),
                                        static_cast<SCSIZE>(nRow) );
                        }
                    }
                }
            }
            else if ( aType.equals( cppu::UnoType<uno::Sequence< uno::Sequence<double> >>::get() ) )
            {
                const uno::Sequence< uno::Sequence<double> >* pRowSeq = nullptr;

                //TODO: use pointer from any!
                uno::Sequence< uno::Sequence<double> > aSequence;
                if ( rNewRes >>= aSequence )
                    pRowSeq = &aSequence;

                if ( pRowSeq )
                {
                    sal_Int32 nRowCount = pRowSeq->getLength();
                    sal_Int32 nMaxColCount = lcl_GetMaxColCount(pRowSeq);
                    if ( nMaxColCount && nRowCount )
                    {
                        const uno::Sequence<double>* pRowArr = pRowSeq->getConstArray();
                        xMatrix = new ScMatrix(
                                static_cast<SCSIZE>(nMaxColCount),
                                static_cast<SCSIZE>(nRowCount), 0.0);
                        for (sal_Int32 nRow=0; nRow<nRowCount; nRow++)
                        {
                            sal_Int32 nColCount = pRowArr[nRow].getLength();
                            const double* pColArr = pRowArr[nRow].getConstArray();
                            for (sal_Int32 nCol=0; nCol<nColCount; nCol++)
                                xMatrix->PutDouble( pColArr[nCol],
                                        static_cast<SCSIZE>(nCol),
                                        static_cast<SCSIZE>(nRow) );
                            for (sal_Int32 nCol=nColCount; nCol<nMaxColCount; nCol++)
                                xMatrix->PutDouble( 0.0,
                                        static_cast<SCSIZE>(nCol),
                                        static_cast<SCSIZE>(nRow) );
                        }
                    }
                }
            }
            else if ( aType.equals( cppu::UnoType<uno::Sequence< uno::Sequence<OUString> >>::get() ) )
            {
                const uno::Sequence< uno::Sequence<OUString> >* pRowSeq = nullptr;

                //TODO: use pointer from any!
                uno::Sequence< uno::Sequence<OUString> > aSequence;
                if ( rNewRes >>= aSequence )
                    pRowSeq = &aSequence;

                if ( pRowSeq )
                {
                    sal_Int32 nRowCount = pRowSeq->getLength();
                    sal_Int32 nMaxColCount = lcl_GetMaxColCount(pRowSeq);
                    if ( nMaxColCount && nRowCount )
                    {
--> --------------------

--> maximum size reached

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

Messung V0.5
C=92 H=89 G=90

¤ Dauer der Verarbeitung: 0.23 Sekunden  (vorverarbeitet)  ¤

*© 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.