/* -*- 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>& 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 = SC_MOD()->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>& 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 = SC_MOD()->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 = 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 )
{
const uno::Sequence<OUString>* 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 OUString* pColArr = pRowArr[nRow].getConstArray();
for (sal_Int32 nCol=0; nCol<nColCount; nCol++)
{
xMatrix->PutString(
mrDoc.GetSharedStringPool().intern(pColArr[nCol]),
static_cast<SCSIZE>(nCol), static_cast<SCSIZE>(nRow));
}
for (sal_Int32 nCol=nColCount; nCol<nMaxColCount; nCol++)
{
xMatrix->PutString(
svl::SharedString::getEmptyString(),
static_cast<SCSIZE>(nCol), static_cast<SCSIZE>(nRow));
}
}
}
}
}
else if ( aType.equals( cppu::UnoType<uno::Sequence< uno::Sequence<uno::Any> >>::get() ) )
{
xMatrix = ScSequenceToMatrix::CreateMixedMatrix( rNewRes );
}
if (!xMatrix) // no array found
nErrCode = FormulaError::NoValue; //TODO: code for error in return type???
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V554 Incorrect use of unique_ptr. The memory allocated with 'new' will be cleaned using 'delete []'.