tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / sc / source / core / tool / addincol.cxx
blob4dde075e43640e00b03c6ea96e4efe0292131154
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <comphelper/processfactory.hxx>
21 #include <i18nlangtag/languagetag.hxx>
22 #include <utility>
23 #include <vcl/svapp.hxx>
24 #include <vcl/settings.hxx>
25 #include <sfx2/objsh.hxx>
26 #include <unotools/charclass.hxx>
27 #include <sal/log.hxx>
28 #include <o3tl/string_view.hxx>
29 #include <osl/diagnose.h>
31 #include <com/sun/star/container/XContentEnumerationAccess.hpp>
32 #include <com/sun/star/frame/XModel.hpp>
33 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
34 #include <com/sun/star/lang/XServiceInfo.hpp>
35 #include <com/sun/star/lang/XServiceName.hpp>
36 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
37 #include <com/sun/star/lang/XSingleComponentFactory.hpp>
38 #include <com/sun/star/reflection/XIdlClass.hpp>
39 #include <com/sun/star/beans/XIntrospectionAccess.hpp>
40 #include <com/sun/star/beans/theIntrospection.hpp>
41 #include <com/sun/star/beans/MethodConcept.hpp>
42 #include <com/sun/star/beans/XPropertySet.hpp>
43 #include <com/sun/star/beans/PropertyValue.hpp>
44 #include <com/sun/star/table/XCellRange.hpp>
45 #include <com/sun/star/lang/Locale.hpp>
46 #include <com/sun/star/sheet/XCompatibilityNames.hpp>
47 #include <com/sun/star/sheet/NoConvergenceException.hpp>
48 #include <com/sun/star/sheet/XAddIn.hpp>
49 #include <com/sun/star/sheet/XVolatileResult.hpp>
51 #include <addincol.hxx>
52 #include <addinhelpid.hxx>
53 #include <scmatrix.hxx>
54 #include <formula/errorcodes.hxx>
55 #include <formula/funcvarargs.h>
56 #include <optutil.hxx>
57 #include <addincfg.hxx>
58 #include <scmod.hxx>
59 #include <rangeseq.hxx>
60 #include <funcdesc.hxx>
61 #include <svl/sharedstring.hxx>
62 #include <formulaopt.hxx>
63 #include <compiler.hxx>
64 #include <document.hxx>
65 #include <memory>
67 using namespace com::sun::star;
69 #define SC_CALLERPOS_NONE (-1)
71 ScUnoAddInFuncData::ScUnoAddInFuncData( const OUString& rNam, const OUString& rLoc,
72 OUString aDesc,
73 sal_uInt16 nCat, OUString sHelp,
74 uno::Reference<reflection::XIdlMethod> xFunc,
75 uno::Any aO,
76 sal_Int32 nAC, const ScAddInArgDesc* pAD,
77 sal_Int32 nCP ) :
78 aOriginalName( rNam ),
79 aLocalName( rLoc ),
80 aUpperName( rNam ),
81 aUpperLocal( rLoc ),
82 aDescription(std::move( aDesc )),
83 xFunction(std::move( xFunc )),
84 aObject(std::move( aO )),
85 nArgCount( nAC ),
86 nCallerPos( nCP ),
87 nCategory( nCat ),
88 sHelpId(std::move( sHelp )),
89 bCompInitialized( false )
91 if ( nArgCount )
93 pArgDescs.reset( new ScAddInArgDesc[nArgCount] );
94 for (sal_Int32 i=0; i<nArgCount; i++)
95 pArgDescs[i] = pAD[i];
98 aUpperName = aUpperName.toAsciiUpperCase(); // programmatic name
99 aUpperLocal = ScGlobal::getCharClass().uppercase(aUpperLocal);
102 ScUnoAddInFuncData::~ScUnoAddInFuncData()
106 const ::std::vector<ScUnoAddInFuncData::LocalizedName>& ScUnoAddInFuncData::GetCompNames() const
108 if ( !bCompInitialized )
110 // read sequence of compatibility names on demand
112 uno::Reference<sheet::XAddIn> xAddIn;
113 if ( aObject >>= xAddIn )
115 uno::Reference<sheet::XCompatibilityNames> xComp( xAddIn, uno::UNO_QUERY );
116 if ( xComp.is() && xFunction.is() )
118 OUString aMethodName = xFunction->getName();
119 const uno::Sequence< sheet::LocalizedName> aCompNames( xComp->getCompatibilityNames( aMethodName ));
120 maCompNames.clear();
121 for (const sheet::LocalizedName& rCompName : aCompNames)
123 maCompNames.emplace_back(
124 LanguageTag::convertToBcp47( rCompName.Locale, false),
125 rCompName.Name);
130 bCompInitialized = true; // also if not successful
132 return maCompNames;
135 void ScUnoAddInFuncData::SetCompNames( ::std::vector< ScUnoAddInFuncData::LocalizedName >&& rNew )
137 OSL_ENSURE( !bCompInitialized, "SetCompNames after initializing" );
139 maCompNames = std::move(rNew);
141 bCompInitialized = true;
144 void ScUnoAddInFuncData::SetEnglishName( const OUString& rEnglishName )
146 if (!rEnglishName.isEmpty())
147 aUpperEnglish = ScCompiler::GetCharClassEnglish()->uppercase(rEnglishName);
148 else
150 // A dumb fallback to not have an empty name, mainly just for the
151 // assignment to ScFuncDesc::mxFuncName for the Function Wizard and
152 // formula input tooltips.
153 aUpperEnglish = aUpperLocal;
157 bool ScUnoAddInFuncData::GetExcelName( const LanguageTag& rDestLang, OUString& rRetExcelName, bool bFallbackToAny ) const
159 const ::std::vector<LocalizedName>& rCompNames = GetCompNames();
160 if ( !rCompNames.empty() )
162 const OUString& aSearch( rDestLang.getBcp47());
164 // First, check exact match without fallback overhead.
165 ::std::vector<LocalizedName>::const_iterator itNames = std::find_if(rCompNames.begin(), rCompNames.end(),
166 [&aSearch](const LocalizedName& rName) { return rName.maLocale == aSearch; });
167 if (itNames != rCompNames.end())
169 rRetExcelName = (*itNames).maName;
170 return true;
173 // For "en-US" try the most likely fallback of "en".
174 if (aSearch == "en-US")
176 itNames = std::find_if(rCompNames.begin(), rCompNames.end(),
177 [](const LocalizedName& rName) { return rName.maLocale == "en"; });
178 if (itNames != rCompNames.end())
180 rRetExcelName = (*itNames).maName;
181 return true;
185 // Try match of fallback search with fallback locales,
186 // appending also 'en-US' and 'en' to search if not queried.
187 ::std::vector< OUString > aFallbackSearch( rDestLang.getFallbackStrings( true));
188 if (aSearch != "en-US")
190 aFallbackSearch.emplace_back("en-US");
191 if (aSearch != "en")
193 aFallbackSearch.emplace_back("en");
196 for (const auto& rSearch : aFallbackSearch)
198 for (const auto& rCompName : rCompNames)
200 ::std::vector< OUString > aFallbackLocales( LanguageTag( rCompName.maLocale).getFallbackStrings(true));
201 if (std::find(aFallbackLocales.begin(), aFallbackLocales.end(), rSearch) != aFallbackLocales.end())
203 rRetExcelName = rCompName.maName;
204 return true;
209 if (bFallbackToAny)
211 // Last resort, use first (default) entry.
212 rRetExcelName = rCompNames[0].maName;
213 return true;
216 return false;
219 void ScUnoAddInFuncData::SetFunction( const uno::Reference< reflection::XIdlMethod>& rNewFunc, const uno::Any& rNewObj )
221 xFunction = rNewFunc;
222 aObject = rNewObj;
225 void ScUnoAddInFuncData::SetArguments( sal_Int32 nNewCount, const ScAddInArgDesc* pNewDescs )
227 nArgCount = nNewCount;
228 if ( nArgCount )
230 pArgDescs.reset( new ScAddInArgDesc[nArgCount] );
231 for (sal_Int32 i=0; i<nArgCount; i++)
232 pArgDescs[i] = pNewDescs[i];
234 else
235 pArgDescs.reset();
238 void ScUnoAddInFuncData::SetCallerPos( sal_Int32 nNewPos )
240 nCallerPos = nNewPos;
243 ScUnoAddInCollection::ScUnoAddInCollection() :
244 nFuncCount( 0 ),
245 bInitialized( false )
249 ScUnoAddInCollection::~ScUnoAddInCollection()
253 void ScUnoAddInCollection::Clear()
255 pExactHashMap.reset();
256 pNameHashMap.reset();
257 pLocalHashMap.reset();
258 pEnglishHashMap.reset();
259 ppFuncData.reset();
260 nFuncCount = 0;
262 bInitialized = false;
265 void ScUnoAddInCollection::Initialize()
267 OSL_ENSURE( !bInitialized, "Initialize twice?" );
269 uno::Reference<lang::XMultiServiceFactory> xManager = comphelper::getProcessServiceFactory();
270 uno::Reference<container::XContentEnumerationAccess> xEnAc( xManager, uno::UNO_QUERY );
271 if ( xEnAc.is() )
273 uno::Reference<container::XEnumeration> xEnum =
274 xEnAc->createContentEnumeration( u"com.sun.star.sheet.AddIn"_ustr );
275 if ( xEnum.is() )
277 // loop through all AddIns
278 while ( xEnum->hasMoreElements() )
280 uno::Any aAddInAny = xEnum->nextElement();
284 uno::Reference<uno::XInterface> xIntFac;
285 aAddInAny >>= xIntFac;
286 if ( xIntFac.is() )
288 // #i59984# try XSingleComponentFactory in addition to (old) XSingleServiceFactory,
289 // passing the context to the component
291 uno::Reference<uno::XInterface> xInterface;
292 uno::Reference<uno::XComponentContext> xCtx(
293 comphelper::getComponentContext(xManager));
294 uno::Reference<lang::XSingleComponentFactory> xCFac( xIntFac, uno::UNO_QUERY );
295 if (xCFac.is())
297 xInterface = xCFac->createInstanceWithContext(xCtx);
298 if (xInterface.is())
299 ReadFromAddIn( xInterface );
302 if (!xInterface.is())
304 uno::Reference<lang::XSingleServiceFactory> xFac( xIntFac, uno::UNO_QUERY );
305 if ( xFac.is() )
307 xInterface = xFac->createInstance();
308 if (xInterface.is())
309 ReadFromAddIn( xInterface );
313 } catch ( const uno::Exception& ) {
314 SAL_WARN ( "sc", "Failed to initialize create instance of sheet.AddIn" );
320 // ReadConfiguration is called after looking at the AddIn implementations.
321 // Duplicated are skipped (by using the service information, they don't have to be updated again
322 // when argument information is needed).
323 ReadConfiguration();
325 bInitialized = true; // with or without functions
328 static sal_uInt16 lcl_GetCategory( std::u16string_view rName )
330 static const char* aFuncNames[SC_FUNCGROUP_COUNT] =
332 // array index = ID - 1 (ID starts at 1)
333 // all upper case
334 "Database", // ID_FUNCTION_GRP_DATABASE
335 "Date&Time", // ID_FUNCTION_GRP_DATETIME
336 "Financial", // ID_FUNCTION_GRP_FINANCIAL
337 "Information", // ID_FUNCTION_GRP_INFO
338 "Logical", // ID_FUNCTION_GRP_LOGIC
339 "Mathematical", // ID_FUNCTION_GRP_MATH
340 "Matrix", // ID_FUNCTION_GRP_MATRIX
341 "Statistical", // ID_FUNCTION_GRP_STATISTIC
342 "Spreadsheet", // ID_FUNCTION_GRP_TABLE
343 "Text", // ID_FUNCTION_GRP_TEXT
344 "Add-In" // ID_FUNCTION_GRP_ADDINS
346 for (sal_uInt16 i=0; i<SC_FUNCGROUP_COUNT; i++)
347 if ( o3tl::equalsAscii( rName, aFuncNames[i] ) )
348 return i+1; // IDs start at 1
350 return ID_FUNCTION_GRP_ADDINS; // if not found, use Add-In group
353 constexpr OUStringLiteral CFGPATH_ADDINS = u"Office.CalcAddIns/AddInInfo";
354 constexpr OUStringLiteral CFGSTR_ADDINFUNCTIONS = u"AddInFunctions";
356 #define CFG_FUNCPROP_DISPLAYNAME 0
357 #define CFG_FUNCPROP_DESCRIPTION 1
358 #define CFG_FUNCPROP_CATEGORY 2
359 #define CFG_FUNCPROP_COUNT 3
360 constexpr OUString CFGSTR_DISPLAYNAME = u"DisplayName"_ustr;
361 constexpr OUString CFGSTR_DESCRIPTION = u"Description"_ustr;
362 constexpr OUString CFGSTR_CATEGORY = u"Category"_ustr;
363 // CategoryDisplayName is ignored for now
365 constexpr OUStringLiteral CFGSTR_COMPATIBILITYNAME = u"CompatibilityName";
366 constexpr OUStringLiteral CFGSTR_PARAMETERS = u"Parameters";
368 void ScUnoAddInCollection::ReadConfiguration()
370 // called only from Initialize
372 ScAddInCfg& rAddInConfig = ScModule::get()->GetAddInCfg();
374 // Additional, temporary config item for the display names and
375 // compatibility names.
376 ScLinkConfigItem aAllLocalesConfig( CFGPATH_ADDINS, ConfigItemMode::AllLocales );
377 // CommitLink is not used (only reading values)
379 const OUString sSlash('/');
381 // get the list of add-ins (services)
382 const uno::Sequence<OUString> aServiceNames = rAddInConfig.GetNodeNames( u""_ustr );
384 for ( const OUString& aServiceName : aServiceNames )
386 ScUnoAddInHelpIdGenerator aHelpIdGenerator( aServiceName );
388 OUString aFunctionsPath(aServiceName + sSlash + CFGSTR_ADDINFUNCTIONS);
390 uno::Sequence<OUString> aFunctionNames = rAddInConfig.GetNodeNames( aFunctionsPath );
391 sal_Int32 nNewCount = aFunctionNames.getLength();
393 // allocate pointers
395 sal_Int32 nOld = nFuncCount;
396 nFuncCount = nNewCount+nOld;
397 if ( nOld )
399 std::unique_ptr<std::unique_ptr<ScUnoAddInFuncData>[]> ppNew(new std::unique_ptr<ScUnoAddInFuncData>[nFuncCount]);
400 for (sal_Int32 i=0; i<nOld; i++)
401 ppNew[i] = std::move(ppFuncData[i]);
402 ppFuncData = std::move(ppNew);
404 else
405 ppFuncData.reset( new std::unique_ptr<ScUnoAddInFuncData>[nFuncCount] );
407 //TODO: adjust bucket count?
408 if ( !pExactHashMap )
409 pExactHashMap.reset( new ScAddInHashMap );
410 if ( !pNameHashMap )
411 pNameHashMap.reset( new ScAddInHashMap );
412 if ( !pLocalHashMap )
413 pLocalHashMap.reset( new ScAddInHashMap );
414 if ( !pEnglishHashMap )
415 pEnglishHashMap.reset( new ScAddInHashMap );
417 //TODO: get the function information in a single call for all functions?
419 const OUString* pFuncNameArray = aFunctionNames.getConstArray();
420 for ( sal_Int32 nFuncPos = 0; nFuncPos < nNewCount; nFuncPos++ )
422 ppFuncData[nFuncPos+nOld] = nullptr;
424 // stored function name: (service name).(function)
425 OUString aFuncName = aServiceName + "." + pFuncNameArray[nFuncPos];
427 // skip the function if already known (read from old AddIn service)
429 if ( pExactHashMap->find( aFuncName ) == pExactHashMap->end() )
431 OUString aEnglishName;
432 OUString aLocalName;
433 OUString aDescription;
434 sal_uInt16 nCategory = ID_FUNCTION_GRP_ADDINS;
436 // get direct information on the function
438 OUString aFuncPropPath = aFunctionsPath + sSlash + pFuncNameArray[nFuncPos] + sSlash;
440 uno::Sequence<OUString> aFuncPropNames{
441 (aFuncPropPath + CFGSTR_DISPLAYNAME), // CFG_FUNCPROP_DISPLAYNAME
442 (aFuncPropPath + CFGSTR_DESCRIPTION), // CFG_FUNCPROP_DESCRIPTION
443 (aFuncPropPath + CFGSTR_CATEGORY)}; // CFG_FUNCPROP_CATEGORY
445 uno::Sequence<uno::Any> aFuncProperties = rAddInConfig.GetProperties( aFuncPropNames );
446 if ( aFuncProperties.getLength() == CFG_FUNCPROP_COUNT )
448 aFuncProperties[CFG_FUNCPROP_DISPLAYNAME] >>= aLocalName;
449 aFuncProperties[CFG_FUNCPROP_DESCRIPTION] >>= aDescription;
451 OUString aCategoryName;
452 aFuncProperties[CFG_FUNCPROP_CATEGORY] >>= aCategoryName;
453 nCategory = lcl_GetCategory( aCategoryName );
456 // get English display name
458 OUString aDisplayNamePath(aFuncPropPath + CFGSTR_DISPLAYNAME);
459 uno::Sequence<OUString> aDisplayNamePropNames( &aDisplayNamePath, 1 );
461 uno::Sequence<uno::Any> aDisplayNameProperties = aAllLocalesConfig.GetProperties( aDisplayNamePropNames );
462 if ( aDisplayNameProperties.getLength() == 1 )
464 uno::Sequence<beans::PropertyValue> aLocalEntries;
465 if ( aDisplayNameProperties[0] >>= aLocalEntries )
467 for (const beans::PropertyValue& rConfig : aLocalEntries)
469 // PropertyValue name is the locale ("convert" from
470 // string to canonicalize).
471 OUString aLocale( LanguageTag( rConfig.Name, true).getBcp47( false));
472 // PropertyValue value is the localized value (string in this case).
473 OUString aName;
474 rConfig.Value >>= aName;
475 // Accept 'en' and 'en-...' but prefer 'en-US'.
476 if (aLocale == "en-US" && !aName.isEmpty())
477 aEnglishName = aName;
478 else if (aEnglishName.isEmpty() && (aLocale == "en" || aLocale.startsWith("en-")))
479 aEnglishName = aName;
483 bool bNeedEnglish = aEnglishName.isEmpty();
485 // get compatibility names
487 ::std::vector<ScUnoAddInFuncData::LocalizedName> aCompNames;
489 OUString aCompPath(aFuncPropPath + CFGSTR_COMPATIBILITYNAME);
490 uno::Sequence<OUString> aCompPropNames( &aCompPath, 1 );
492 uno::Sequence<uno::Any> aCompProperties = aAllLocalesConfig.GetProperties( aCompPropNames );
493 if ( aCompProperties.getLength() == 1 )
495 uno::Sequence<beans::PropertyValue> aLocalEntries;
496 if ( aCompProperties[0] >>= aLocalEntries )
498 for (const beans::PropertyValue& rConfig : aLocalEntries)
500 // PropertyValue name is the locale ("convert" from
501 // string to canonicalize).
502 OUString aLocale( LanguageTag( rConfig.Name, true).getBcp47( false));
503 // PropertyValue value is the localized value (string in this case).
504 OUString aName;
505 rConfig.Value >>= aName;
506 if (!aName.isEmpty())
508 aCompNames.emplace_back( aLocale, aName);
509 if (bNeedEnglish)
511 // Accept 'en' and 'en-...' but prefer 'en-US'.
512 if (aLocale == "en-US")
513 aEnglishName = aName;
514 else if (aEnglishName.isEmpty() && (aLocale == "en" || aLocale.startsWith("en-")))
515 aEnglishName = aName;
522 // get argument info
524 std::unique_ptr<ScAddInArgDesc[]> pVisibleArgs;
525 sal_Int32 nVisibleCount = 0;
527 OUString aArgumentsPath(aFuncPropPath + CFGSTR_PARAMETERS);
529 const uno::Sequence<OUString> aArgumentNames = rAddInConfig.GetNodeNames( aArgumentsPath );
530 sal_Int32 nArgumentCount = aArgumentNames.getLength();
531 if ( nArgumentCount )
533 // get DisplayName and Description for each argument
534 uno::Sequence<OUString> aArgPropNames( nArgumentCount * 2 );
535 OUString* pPropNameArray = aArgPropNames.getArray();
537 sal_Int32 nIndex = 0;
538 for ( const OUString& rArgName : aArgumentNames )
540 OUString aOneArgPath = aArgumentsPath + sSlash + rArgName + sSlash;
542 pPropNameArray[nIndex++] = aOneArgPath
543 + CFGSTR_DISPLAYNAME;
544 pPropNameArray[nIndex++] = aOneArgPath
545 + CFGSTR_DESCRIPTION;
548 uno::Sequence<uno::Any> aArgProperties = rAddInConfig.GetProperties( aArgPropNames );
549 if ( aArgProperties.getLength() == aArgPropNames.getLength() )
551 const OUString* pArgNameArray = aArgumentNames.getConstArray();
552 const uno::Any* pPropArray = aArgProperties.getConstArray();
553 OUString sDisplayName;
554 OUString sDescription;
556 ScAddInArgDesc aDesc;
557 aDesc.eType = SC_ADDINARG_NONE; // arg type is not in configuration
558 aDesc.bOptional = false;
560 nVisibleCount = nArgumentCount;
561 pVisibleArgs.reset(new ScAddInArgDesc[nVisibleCount]);
563 nIndex = 0;
564 for ( sal_Int32 nArgument = 0; nArgument < nArgumentCount; nArgument++ )
566 pPropArray[nIndex++] >>= sDisplayName;
567 pPropArray[nIndex++] >>= sDescription;
569 aDesc.aInternalName = pArgNameArray[nArgument];
570 aDesc.aName = sDisplayName;
571 aDesc.aDescription = sDescription;
573 pVisibleArgs[nArgument] = aDesc;
578 OUString sHelpId = aHelpIdGenerator.GetHelpId( pFuncNameArray[nFuncPos] );
580 uno::Reference<reflection::XIdlMethod> xFunc; // remains empty
581 uno::Any aObject; // also empty
583 // create and insert into the array
585 ScUnoAddInFuncData* pData = new ScUnoAddInFuncData(
586 aFuncName, aLocalName, aDescription,
587 nCategory, sHelpId,
588 xFunc, aObject,
589 nVisibleCount, pVisibleArgs.get(), SC_CALLERPOS_NONE );
591 pData->SetCompNames( std::move(aCompNames) );
593 ppFuncData[nFuncPos+nOld].reset(pData);
595 pExactHashMap->emplace(
596 pData->GetOriginalName(),
597 pData );
598 pNameHashMap->emplace(
599 pData->GetUpperName(),
600 pData );
601 pLocalHashMap->emplace(
602 pData->GetUpperLocal(),
603 pData );
605 if (aEnglishName.isEmpty())
606 SAL_WARN("sc.core", "no English name for " << aLocalName << " " << aFuncName);
607 else
609 pEnglishHashMap->emplace(
610 ScCompiler::GetCharClassEnglish()->uppercase(aEnglishName),
611 pData );
613 pData->SetEnglishName(aEnglishName); // takes care of handling empty
619 void ScUnoAddInCollection::LoadComponent( const ScUnoAddInFuncData& rFuncData )
621 const OUString& aFullName = rFuncData.GetOriginalName();
622 sal_Int32 nPos = aFullName.lastIndexOf( '.' );
623 if ( nPos <= 0 )
624 return;
626 OUString aServiceName = aFullName.copy( 0, nPos );
630 uno::Reference<lang::XMultiServiceFactory> xServiceFactory = comphelper::getProcessServiceFactory();
631 uno::Reference<uno::XInterface> xInterface( xServiceFactory->createInstance( aServiceName ) );
633 if (xInterface.is())
634 UpdateFromAddIn( xInterface, aServiceName );
636 catch (const uno::Exception &)
638 SAL_WARN ("sc", "Failed to create addin component '"
639 << aServiceName << "'");
643 bool ScUnoAddInCollection::GetExcelName( const OUString& rCalcName,
644 LanguageType eDestLang, OUString& rRetExcelName )
646 const ScUnoAddInFuncData* pFuncData = GetFuncData( rCalcName );
647 if ( pFuncData )
648 return pFuncData->GetExcelName( LanguageTag( eDestLang), rRetExcelName);
649 return false;
652 bool ScUnoAddInCollection::GetCalcName( const OUString& rExcelName, OUString& rRetCalcName )
654 if (!bInitialized)
655 Initialize();
657 OUString aUpperCmp = ScGlobal::getCharClass().uppercase(rExcelName);
659 for (sal_Int32 i=0; i<nFuncCount; i++)
661 ScUnoAddInFuncData* pFuncData = ppFuncData[i].get();
662 if ( pFuncData )
664 const ::std::vector<ScUnoAddInFuncData::LocalizedName>& rNames = pFuncData->GetCompNames();
665 auto bFound = std::any_of(rNames.begin(), rNames.end(),
666 [&aUpperCmp](const ScUnoAddInFuncData::LocalizedName& rName) {
667 return ScGlobal::getCharClass().uppercase( rName.maName ) == aUpperCmp; });
668 if (bFound)
670 //TODO: store upper case for comparing?
672 // use the first function that has this name for any language
673 rRetCalcName = pFuncData->GetOriginalName();
674 return true;
678 return false;
681 static bool IsTypeName( std::u16string_view rName, const uno::Type& rType )
683 return rName == rType.getTypeName();
686 static bool lcl_ValidReturnType( const uno::Reference<reflection::XIdlClass>& xClass )
688 // this must match with ScUnoAddInCall::SetResult
690 if ( !xClass.is() ) return false;
692 switch (xClass->getTypeClass())
694 case uno::TypeClass_ANY: // variable type
695 case uno::TypeClass_ENUM: //TODO: ???
696 case uno::TypeClass_BOOLEAN:
697 case uno::TypeClass_CHAR:
698 case uno::TypeClass_BYTE:
699 case uno::TypeClass_SHORT:
700 case uno::TypeClass_UNSIGNED_SHORT:
701 case uno::TypeClass_LONG:
702 case uno::TypeClass_UNSIGNED_LONG:
703 case uno::TypeClass_FLOAT:
704 case uno::TypeClass_DOUBLE:
705 case uno::TypeClass_STRING:
706 return true; // values or string
708 case uno::TypeClass_INTERFACE:
710 // return type XInterface may contain a XVolatileResult
711 //TODO: XIdlClass needs getType() method!
713 OUString sName = xClass->getName();
714 return (
715 IsTypeName( sName, cppu::UnoType<sheet::XVolatileResult>::get()) ||
716 IsTypeName( sName, cppu::UnoType<uno::XInterface>::get()) );
719 default:
721 // nested sequences for arrays
722 //TODO: XIdlClass needs getType() method!
724 OUString sName = xClass->getName();
725 return (
726 IsTypeName( sName, cppu::UnoType<uno::Sequence< uno::Sequence<sal_Int32> >>::get() ) ||
727 IsTypeName( sName, cppu::UnoType<uno::Sequence< uno::Sequence<double> >>::get() ) ||
728 IsTypeName( sName, cppu::UnoType<uno::Sequence< uno::Sequence<OUString> >>::get() ) ||
729 IsTypeName( sName, cppu::UnoType<uno::Sequence< uno::Sequence<uno::Any> >>::get() ) );
734 static ScAddInArgumentType lcl_GetArgType( const uno::Reference<reflection::XIdlClass>& xClass )
736 if (!xClass.is())
737 return SC_ADDINARG_NONE;
739 uno::TypeClass eType = xClass->getTypeClass();
741 if ( eType == uno::TypeClass_LONG ) //TODO: other integer types?
742 return SC_ADDINARG_INTEGER;
744 if ( eType == uno::TypeClass_DOUBLE )
745 return SC_ADDINARG_DOUBLE;
747 if ( eType == uno::TypeClass_STRING )
748 return SC_ADDINARG_STRING;
750 //TODO: XIdlClass needs getType() method!
751 OUString sName = xClass->getName();
753 if (IsTypeName( sName, cppu::UnoType<uno::Sequence< uno::Sequence<sal_Int32> >>::get() ))
754 return SC_ADDINARG_INTEGER_ARRAY;
756 if (IsTypeName( sName, cppu::UnoType<uno::Sequence< uno::Sequence<double> >>::get() ))
757 return SC_ADDINARG_DOUBLE_ARRAY;
759 if (IsTypeName( sName, cppu::UnoType<uno::Sequence< uno::Sequence<OUString> >>::get() ))
760 return SC_ADDINARG_STRING_ARRAY;
762 if (IsTypeName( sName, cppu::UnoType<uno::Sequence< uno::Sequence<uno::Any> >>::get() ))
763 return SC_ADDINARG_MIXED_ARRAY;
765 if (IsTypeName( sName, cppu::UnoType<uno::Any>::get()))
766 return SC_ADDINARG_VALUE_OR_ARRAY;
768 if (IsTypeName( sName, cppu::UnoType<table::XCellRange>::get()))
769 return SC_ADDINARG_CELLRANGE;
771 if (IsTypeName( sName, cppu::UnoType<beans::XPropertySet>::get()))
772 return SC_ADDINARG_CALLER;
774 if (IsTypeName( sName, cppu::UnoType<uno::Sequence<uno::Any>>::get() ))
775 return SC_ADDINARG_VARARGS;
777 return SC_ADDINARG_NONE;
780 void ScUnoAddInCollection::ReadFromAddIn( const uno::Reference<uno::XInterface>& xInterface )
782 uno::Reference<sheet::XAddIn> xAddIn( xInterface, uno::UNO_QUERY );
783 uno::Reference<lang::XServiceName> xName( xInterface, uno::UNO_QUERY );
784 if ( !(xAddIn.is() && xName.is()) )
785 return;
787 // Even if GetUseEnglishFunctionName() would return true, do not set the
788 // locale to en-US to get English function names as that also would mix in
789 // English descriptions and parameter names. Also, setting a locale will
790 // reinitialize the Add-In completely, so switching back and forth isn't a
791 // good idea either.
792 xAddIn->setLocale( Application::GetSettings().GetUILanguageTag().getLocale());
794 // Instead, in a second run with 'en-US' obtain English names.
795 struct FuncNameData
797 OUString aFuncU;
798 ScUnoAddInFuncData* pData;
800 std::vector<FuncNameData> aFuncNameData;
802 OUString aServiceName( xName->getServiceName() );
803 ScUnoAddInHelpIdGenerator aHelpIdGenerator( aServiceName );
805 //TODO: pass XIntrospection to ReadFromAddIn
807 const uno::Reference<uno::XComponentContext>& xContext = comphelper::getProcessComponentContext();
809 uno::Reference<beans::XIntrospection> xIntro = beans::theIntrospection::get( xContext );
810 uno::Any aObject;
811 aObject <<= xAddIn;
812 uno::Reference<beans::XIntrospectionAccess> xAcc = xIntro->inspect(aObject);
813 if (!xAcc.is())
814 return;
816 uno::Sequence< uno::Reference<reflection::XIdlMethod> > aMethods =
817 xAcc->getMethods( beans::MethodConcept::ALL );
818 sal_Int32 nNewCount = aMethods.getLength();
819 if ( !nNewCount )
820 return;
822 sal_Int32 nOld = nFuncCount;
823 nFuncCount = nNewCount+nOld;
824 if ( nOld )
826 std::unique_ptr<std::unique_ptr<ScUnoAddInFuncData>[]> ppNew(new std::unique_ptr<ScUnoAddInFuncData>[nFuncCount]);
827 for (sal_Int32 i=0; i<nOld; i++)
828 ppNew[i] = std::move(ppFuncData[i]);
829 ppFuncData = std::move(ppNew);
831 else
832 ppFuncData.reset(new std::unique_ptr<ScUnoAddInFuncData>[nFuncCount]);
834 //TODO: adjust bucket count?
835 if ( !pExactHashMap )
836 pExactHashMap.reset( new ScAddInHashMap );
837 if ( !pNameHashMap )
838 pNameHashMap.reset( new ScAddInHashMap );
839 if ( !pLocalHashMap )
840 pLocalHashMap.reset( new ScAddInHashMap );
841 if ( !pEnglishHashMap )
842 pEnglishHashMap.reset( new ScAddInHashMap );
844 const uno::Reference<reflection::XIdlMethod>* pArray = aMethods.getConstArray();
845 for (sal_Int32 nFuncPos=0; nFuncPos<nNewCount; nFuncPos++)
847 ppFuncData[nFuncPos+nOld] = nullptr;
849 uno::Reference<reflection::XIdlMethod> xFunc = pArray[nFuncPos];
850 if (xFunc.is())
852 // leave out internal functions
853 uno::Reference<reflection::XIdlClass> xClass =
854 xFunc->getDeclaringClass();
855 bool bSkip = true;
856 if ( xClass.is() )
858 //TODO: XIdlClass needs getType() method!
859 OUString sName = xClass->getName();
860 bSkip = (
861 IsTypeName( sName,
862 cppu::UnoType<uno::XInterface>::get()) ||
863 IsTypeName( sName,
864 cppu::UnoType<lang::XServiceName>::get()) ||
865 IsTypeName( sName,
866 cppu::UnoType<lang::XServiceInfo>::get()) ||
867 IsTypeName( sName,
868 cppu::UnoType<sheet::XAddIn>::get()) );
870 if (!bSkip)
872 uno::Reference<reflection::XIdlClass> xReturn =
873 xFunc->getReturnType();
874 if ( !lcl_ValidReturnType( xReturn ) )
875 bSkip = true;
877 if (!bSkip)
879 OUString aFuncU = xFunc->getName();
881 // stored function name: (service name).(function)
882 OUString aFuncName = aServiceName + "." + aFuncU;
884 bool bValid = true;
885 sal_Int32 nVisibleCount = 0;
886 sal_Int32 nCallerPos = SC_CALLERPOS_NONE;
888 uno::Sequence<reflection::ParamInfo> aParams =
889 xFunc->getParameterInfos();
890 sal_Int32 nParamCount = aParams.getLength();
891 const reflection::ParamInfo* pParArr = aParams.getConstArray();
892 sal_Int32 nParamPos;
893 for (nParamPos=0; nParamPos<nParamCount; nParamPos++)
895 if ( pParArr[nParamPos].aMode != reflection::ParamMode_IN )
896 bValid = false;
897 uno::Reference<reflection::XIdlClass> xParClass =
898 pParArr[nParamPos].aType;
899 ScAddInArgumentType eArgType = lcl_GetArgType( xParClass );
900 if ( eArgType == SC_ADDINARG_NONE )
901 bValid = false;
902 else if ( eArgType == SC_ADDINARG_CALLER )
903 nCallerPos = nParamPos;
904 else
905 ++nVisibleCount;
907 if (bValid)
909 sal_uInt16 nCategory = lcl_GetCategory(
910 xAddIn->getProgrammaticCategoryName( aFuncU ) );
912 OUString sHelpId = aHelpIdGenerator.GetHelpId( aFuncU );
914 OUString aLocalName;
917 aLocalName = xAddIn->
918 getDisplayFunctionName( aFuncU );
920 catch(uno::Exception&)
922 aLocalName = "###";
925 OUString aDescription;
928 aDescription = xAddIn->
929 getFunctionDescription( aFuncU );
931 catch(uno::Exception&)
933 aDescription = "###";
936 std::unique_ptr<ScAddInArgDesc[]> pVisibleArgs;
937 if ( nVisibleCount > 0 )
939 ScAddInArgDesc aDesc;
940 pVisibleArgs.reset(new ScAddInArgDesc[nVisibleCount]);
941 sal_Int32 nDestPos = 0;
942 for (nParamPos=0; nParamPos<nParamCount; nParamPos++)
944 uno::Reference<reflection::XIdlClass> xParClass =
945 pParArr[nParamPos].aType;
946 ScAddInArgumentType eArgType = lcl_GetArgType( xParClass );
947 if ( eArgType != SC_ADDINARG_CALLER )
949 OUString aArgName;
952 aArgName = xAddIn->
953 getDisplayArgumentName( aFuncU, nParamPos );
955 catch(uno::Exception&)
957 aArgName = "###";
959 OUString aArgDesc;
962 aArgDesc = xAddIn->
963 getArgumentDescription( aFuncU, nParamPos );
965 catch(uno::Exception&)
967 aArgDesc = "###";
970 bool bOptional =
971 ( eArgType == SC_ADDINARG_VALUE_OR_ARRAY ||
972 eArgType == SC_ADDINARG_VARARGS );
974 aDesc.eType = eArgType;
975 aDesc.aName = aArgName;
976 aDesc.aDescription = aArgDesc;
977 aDesc.bOptional = bOptional;
978 //TODO: initialize aInternalName only from config?
979 aDesc.aInternalName = pParArr[nParamPos].aName;
981 pVisibleArgs[nDestPos++] = aDesc;
984 OSL_ENSURE( nDestPos==nVisibleCount, "wrong count" );
987 ppFuncData[nFuncPos+nOld].reset( new ScUnoAddInFuncData(
988 aFuncName, aLocalName, aDescription,
989 nCategory, sHelpId,
990 xFunc, aObject,
991 nVisibleCount, pVisibleArgs.get(), nCallerPos ) );
993 ScUnoAddInFuncData* pData = ppFuncData[nFuncPos+nOld].get();
994 pExactHashMap->emplace(
995 pData->GetOriginalName(),
996 pData );
997 pNameHashMap->emplace(
998 pData->GetUpperName(),
999 pData );
1000 pLocalHashMap->emplace(
1001 pData->GetUpperLocal(),
1002 pData );
1004 aFuncNameData.push_back({aFuncU, pData});
1010 const LanguageTag aEnglishLanguageTag(LANGUAGE_ENGLISH_US);
1011 xAddIn->setLocale( aEnglishLanguageTag.getLocale());
1012 for (const auto& rFunc : aFuncNameData)
1014 OUString aEnglishName;
1017 aEnglishName = xAddIn->getDisplayFunctionName( rFunc.aFuncU );
1019 catch(uno::Exception&)
1022 if (aEnglishName.isEmpty()
1023 && rFunc.pData->GetExcelName( aEnglishLanguageTag, aEnglishName, false /*bFallbackToAny*/))
1025 // Check our known suffixes and append if not present. Note this
1026 // depends on localization (that should not add such suffix, but..)
1027 // and is really only a last resort.
1028 if (rFunc.pData->GetLocalName().endsWith("_ADD") && !aEnglishName.endsWith("_ADD"))
1029 aEnglishName += "_ADD";
1030 else if (rFunc.pData->GetLocalName().endsWith("_EXCEL2003") && !aEnglishName.endsWith("_EXCEL2003"))
1031 aEnglishName += "_EXCEL2003";
1032 SAL_WARN("sc.core", "obtaining English name for " << rFunc.pData->GetLocalName() << " "
1033 << rFunc.pData->GetOriginalName() << " as ExcelName '" << aEnglishName << "'");
1035 SAL_WARN_IF(aEnglishName.isEmpty(), "sc.core", "no English name for "
1036 << rFunc.pData->GetLocalName() << " " << rFunc.pData->GetOriginalName());
1037 rFunc.pData->SetEnglishName(aEnglishName); // takes care of handling empty
1038 pEnglishHashMap->emplace( rFunc.pData->GetUpperEnglish(), rFunc.pData);
1042 static void lcl_UpdateFunctionList( const ScFunctionList& rFunctionList, const ScUnoAddInFuncData& rFuncData,
1043 bool bEnglishFunctionNames )
1045 // as used in FillFunctionDescFromData
1046 const OUString& aCompare = (bEnglishFunctionNames ? rFuncData.GetUpperEnglish() : rFuncData.GetUpperLocal());
1048 sal_uLong nCount = rFunctionList.GetCount();
1049 for (sal_uLong nPos=0; nPos<nCount; nPos++)
1051 const ScFuncDesc* pDesc = rFunctionList.GetFunction( nPos );
1052 if ( pDesc && pDesc->mxFuncName && *pDesc->mxFuncName == aCompare )
1054 ScUnoAddInCollection::FillFunctionDescFromData( rFuncData, *const_cast<ScFuncDesc*>(pDesc),
1055 bEnglishFunctionNames);
1056 break;
1061 static const ScAddInArgDesc* lcl_FindArgDesc( const ScUnoAddInFuncData& rFuncData, std::u16string_view rArgIntName )
1063 sal_Int32 nArgCount = rFuncData.GetArgumentCount();
1064 const ScAddInArgDesc* pArguments = rFuncData.GetArguments();
1065 for (sal_Int32 nPos=0; nPos<nArgCount; nPos++)
1067 if ( pArguments[nPos].aInternalName == rArgIntName )
1068 return &pArguments[nPos];
1070 return nullptr;
1073 void ScUnoAddInCollection::UpdateFromAddIn( const uno::Reference<uno::XInterface>& xInterface,
1074 std::u16string_view rServiceName )
1076 const bool bEnglishFunctionNames = ScModule::get()->GetFormulaOptions().GetUseEnglishFuncName();
1077 uno::Reference<lang::XLocalizable> xLoc( xInterface, uno::UNO_QUERY );
1078 if ( xLoc.is() ) // optional in new add-ins
1079 xLoc->setLocale( Application::GetSettings().GetUILanguageTag().getLocale());
1081 // if function list was already initialized, it must be updated
1083 ScFunctionList* pFunctionList = nullptr;
1084 if ( ScGlobal::HasStarCalcFunctionList() )
1085 pFunctionList = ScGlobal::GetStarCalcFunctionList();
1087 // only get the function information from Introspection
1089 const uno::Reference<uno::XComponentContext>& xContext = comphelper::getProcessComponentContext();
1091 uno::Reference<beans::XIntrospection> xIntro = beans::theIntrospection::get(xContext);
1092 uno::Any aObject;
1093 aObject <<= xInterface;
1094 uno::Reference<beans::XIntrospectionAccess> xAcc = xIntro->inspect(aObject);
1095 if (!xAcc.is())
1096 return;
1098 const uno::Sequence< uno::Reference<reflection::XIdlMethod> > aMethods =
1099 xAcc->getMethods( beans::MethodConcept::ALL );
1100 for (const uno::Reference<reflection::XIdlMethod>& xFunc : aMethods)
1102 if (xFunc.is())
1104 OUString aFuncU = xFunc->getName();
1106 // stored function name: (service name).(function)
1107 OUString aFuncName = OUString::Concat(rServiceName) + "." + aFuncU;
1109 // internal names are skipped because no FuncData exists
1110 ScUnoAddInFuncData* pOldData = const_cast<ScUnoAddInFuncData*>( GetFuncData( aFuncName ) );
1111 if ( pOldData )
1113 // Create new (complete) argument info.
1114 // As in ReadFromAddIn, the reflection information is authoritative.
1115 // Local names and descriptions from pOldData are looked up using the
1116 // internal argument name.
1118 bool bValid = true;
1119 sal_Int32 nVisibleCount = 0;
1120 sal_Int32 nCallerPos = SC_CALLERPOS_NONE;
1122 const uno::Sequence<reflection::ParamInfo> aParams =
1123 xFunc->getParameterInfos();
1124 sal_Int32 nParamCount = aParams.getLength();
1125 const reflection::ParamInfo* pParArr = aParams.getConstArray();
1126 for (sal_Int32 nParamPos=0; nParamPos<nParamCount; nParamPos++)
1128 if ( pParArr[nParamPos].aMode != reflection::ParamMode_IN )
1129 bValid = false;
1130 uno::Reference<reflection::XIdlClass> xParClass =
1131 pParArr[nParamPos].aType;
1132 ScAddInArgumentType eArgType = lcl_GetArgType( xParClass );
1133 if ( eArgType == SC_ADDINARG_NONE )
1134 bValid = false;
1135 else if ( eArgType == SC_ADDINARG_CALLER )
1136 nCallerPos = nParamPos;
1137 else
1138 ++nVisibleCount;
1140 if (bValid)
1142 std::unique_ptr<ScAddInArgDesc[]> pVisibleArgs;
1143 if ( nVisibleCount > 0 )
1145 ScAddInArgDesc aDesc;
1146 pVisibleArgs.reset(new ScAddInArgDesc[nVisibleCount]);
1147 sal_Int32 nDestPos = 0;
1148 for (const auto& rParam : aParams)
1150 uno::Reference<reflection::XIdlClass> xParClass =
1151 rParam.aType;
1152 ScAddInArgumentType eArgType = lcl_GetArgType( xParClass );
1153 if ( eArgType != SC_ADDINARG_CALLER )
1155 const ScAddInArgDesc* pOldArgDesc =
1156 lcl_FindArgDesc( *pOldData, rParam.aName );
1157 if ( pOldArgDesc )
1159 aDesc.aName = pOldArgDesc->aName;
1160 aDesc.aDescription = pOldArgDesc->aDescription;
1162 else
1163 aDesc.aName = aDesc.aDescription = "###";
1165 bool bOptional =
1166 ( eArgType == SC_ADDINARG_VALUE_OR_ARRAY ||
1167 eArgType == SC_ADDINARG_VARARGS );
1169 aDesc.eType = eArgType;
1170 aDesc.bOptional = bOptional;
1171 //TODO: initialize aInternalName only from config?
1172 aDesc.aInternalName = rParam.aName;
1174 pVisibleArgs[nDestPos++] = aDesc;
1177 OSL_ENSURE( nDestPos==nVisibleCount, "wrong count" );
1180 pOldData->SetFunction( xFunc, aObject );
1181 pOldData->SetArguments( nVisibleCount, pVisibleArgs.get() );
1182 pOldData->SetCallerPos( nCallerPos );
1184 if ( pFunctionList )
1185 lcl_UpdateFunctionList( *pFunctionList, *pOldData, bEnglishFunctionNames );
1192 OUString ScUnoAddInCollection::FindFunction( const OUString& rUpperName, bool bLocalFirst )
1194 if (!bInitialized)
1195 Initialize();
1197 if (nFuncCount == 0)
1198 return OUString();
1200 if ( bLocalFirst )
1202 // Only scan local names (used for entering formulas).
1204 ScAddInHashMap::const_iterator iLook( pLocalHashMap->find( rUpperName ) );
1205 if ( iLook != pLocalHashMap->end() )
1206 return iLook->second->GetOriginalName();
1208 else
1210 // First scan international programmatic names (used when calling a
1211 // function).
1213 ScAddInHashMap::const_iterator iLook( pNameHashMap->find( rUpperName ) );
1214 if ( iLook != pNameHashMap->end() )
1215 return iLook->second->GetOriginalName();
1217 // Then scan English names (as FunctionAccess API could expect).
1219 iLook = pEnglishHashMap->find( rUpperName );
1220 if ( iLook != pEnglishHashMap->end() )
1221 return iLook->second->GetOriginalName();
1223 // After that, scan all local names; either to allow replacing old
1224 // AddIns with Uno, or for functions where the AddIn did not provide an
1225 // English name.
1227 iLook = pLocalHashMap->find( rUpperName );
1228 if ( iLook != pLocalHashMap->end() )
1229 return iLook->second->GetOriginalName();
1232 return OUString();
1235 const ScUnoAddInFuncData* ScUnoAddInCollection::GetFuncData( const OUString& rName, bool bComplete )
1237 if (!bInitialized)
1238 Initialize();
1240 // rName must be the exact internal name
1242 ScAddInHashMap::const_iterator iLook( pExactHashMap->find( rName ) );
1243 if ( iLook != pExactHashMap->end() )
1245 const ScUnoAddInFuncData* pFuncData = iLook->second;
1247 if ( bComplete && !pFuncData->GetFunction().is() ) //TODO: extra flag?
1248 LoadComponent( *pFuncData );
1250 return pFuncData;
1253 return nullptr;
1256 const ScUnoAddInFuncData* ScUnoAddInCollection::GetFuncData( sal_Int32 nIndex )
1258 if (!bInitialized)
1259 Initialize();
1261 if (nIndex < nFuncCount)
1262 return ppFuncData[nIndex].get();
1263 return nullptr;
1266 void ScUnoAddInCollection::LocalizeString( OUString& rName )
1268 if (!bInitialized)
1269 Initialize();
1271 // modify rName - input: exact name
1273 ScAddInHashMap::const_iterator iLook( pExactHashMap->find( rName ) );
1274 if ( iLook != pExactHashMap->end() )
1275 rName = iLook->second->GetUpperLocal(); //TODO: upper?
1278 sal_Int32 ScUnoAddInCollection::GetFuncCount()
1280 if (!bInitialized)
1281 Initialize();
1283 return nFuncCount;
1286 bool ScUnoAddInCollection::FillFunctionDesc( sal_Int32 nFunc, ScFuncDesc& rDesc, bool bEnglishFunctionNames )
1288 if (!bInitialized)
1289 Initialize();
1291 if (nFunc >= nFuncCount || !ppFuncData[nFunc])
1292 return false;
1294 const ScUnoAddInFuncData& rFuncData = *ppFuncData[nFunc];
1296 return FillFunctionDescFromData( rFuncData, rDesc, bEnglishFunctionNames );
1299 bool ScUnoAddInCollection::FillFunctionDescFromData( const ScUnoAddInFuncData& rFuncData, ScFuncDesc& rDesc,
1300 bool bEnglishFunctionNames )
1302 rDesc.Clear();
1304 bool bIncomplete = !rFuncData.GetFunction().is(); //TODO: extra flag?
1306 sal_Int32 nArgCount = rFuncData.GetArgumentCount();
1307 if ( nArgCount > SAL_MAX_UINT16 )
1308 return false;
1310 if ( bIncomplete )
1311 nArgCount = 0; // if incomplete, fill without argument info (no wrong order)
1313 // nFIndex is set from outside
1315 rDesc.mxFuncName = (bEnglishFunctionNames ? rFuncData.GetUpperEnglish() : rFuncData.GetUpperLocal());
1316 rDesc.nCategory = rFuncData.GetCategory();
1317 rDesc.sHelpId = rFuncData.GetHelpId();
1319 OUString aDesc = rFuncData.GetDescription();
1320 if (aDesc.isEmpty())
1321 aDesc = rFuncData.GetLocalName(); // use name if no description is available
1322 rDesc.mxFuncDesc = aDesc ;
1324 // AddInArgumentType_CALLER is already left out in FuncData
1326 rDesc.nArgCount = static_cast<sal_uInt16>(nArgCount);
1327 if ( nArgCount )
1329 bool bMultiple = false;
1330 const ScAddInArgDesc* pArgs = rFuncData.GetArguments();
1332 rDesc.maDefArgNames.clear();
1333 rDesc.maDefArgNames.resize(nArgCount);
1334 rDesc.maDefArgDescs.clear();
1335 rDesc.maDefArgDescs.resize(nArgCount);
1336 rDesc.pDefArgFlags = new ScFuncDesc::ParameterFlags[nArgCount];
1337 for ( sal_Int32 nArg=0; nArg<nArgCount; nArg++ )
1339 rDesc.maDefArgNames[nArg] = pArgs[nArg].aName;
1340 rDesc.maDefArgDescs[nArg] = pArgs[nArg].aDescription;
1341 rDesc.pDefArgFlags[nArg].bOptional = pArgs[nArg].bOptional;
1343 // no empty names...
1344 if (rDesc.maDefArgNames[nArg].isEmpty())
1345 rDesc.maDefArgNames[nArg] = "arg" + OUString::number(nArg + 1);
1347 // last argument repeated?
1348 if ( nArg+1 == nArgCount && ( pArgs[nArg].eType == SC_ADDINARG_VARARGS ) )
1349 bMultiple = true;
1352 if ( bMultiple )
1353 rDesc.nArgCount += VAR_ARGS - 1; // VAR_ARGS means just one repeated arg
1356 rDesc.bIncomplete = bIncomplete;
1358 return true;
1361 ScUnoAddInCall::ScUnoAddInCall( ScDocument& rDoc, ScUnoAddInCollection& rColl, const OUString& rName,
1362 sal_Int32 nParamCount ) :
1363 mrDoc( rDoc ),
1364 bValidCount( false ),
1365 nErrCode( FormulaError::NoCode ), // before function was called
1366 bHasString( true ),
1367 fValue( 0.0 ),
1368 xMatrix( nullptr )
1370 pFuncData = rColl.GetFuncData( rName, true ); // need fully initialized data
1371 OSL_ENSURE( pFuncData, "Function Data missing" );
1372 if ( !pFuncData )
1373 return;
1375 sal_Int32 nDescCount = pFuncData->GetArgumentCount();
1376 const ScAddInArgDesc* pArgs = pFuncData->GetArguments();
1378 // is aVarArg sequence needed?
1379 if ( nParamCount >= nDescCount && nDescCount > 0 &&
1380 pArgs[nDescCount-1].eType == SC_ADDINARG_VARARGS )
1382 sal_Int32 nVarCount = nParamCount - ( nDescCount - 1 ); // size of last argument
1383 aVarArg.realloc( nVarCount );
1384 bValidCount = true;
1386 else if ( nParamCount <= nDescCount )
1388 // all args behind nParamCount must be optional
1389 bValidCount = true;
1390 for (sal_Int32 i=nParamCount; i<nDescCount; i++)
1391 if ( !pArgs[i].bOptional )
1392 bValidCount = false;
1394 // else invalid (too many arguments)
1396 if ( bValidCount )
1397 aArgs.realloc( nDescCount ); // sequence must always match function signature
1400 ScUnoAddInCall::~ScUnoAddInCall()
1402 // pFuncData is deleted with ScUnoAddInCollection
1405 ScAddInArgumentType ScUnoAddInCall::GetArgType( sal_Int32 nPos )
1407 if ( pFuncData )
1409 sal_Int32 nCount = pFuncData->GetArgumentCount();
1410 const ScAddInArgDesc* pArgs = pFuncData->GetArguments();
1412 // if last arg is sequence, use "any" type
1413 if ( nCount > 0 && nPos >= nCount-1 && pArgs[nCount-1].eType == SC_ADDINARG_VARARGS )
1414 return SC_ADDINARG_VALUE_OR_ARRAY;
1416 if ( nPos < nCount )
1417 return pArgs[nPos].eType;
1419 return SC_ADDINARG_VALUE_OR_ARRAY; //TODO: error code !!!!
1422 bool ScUnoAddInCall::NeedsCaller() const
1424 return pFuncData && pFuncData->GetCallerPos() != SC_CALLERPOS_NONE;
1427 void ScUnoAddInCall::SetCaller( const uno::Reference<uno::XInterface>& rInterface )
1429 xCaller = rInterface;
1432 void ScUnoAddInCall::SetCallerFromObjectShell( const SfxObjectShell* pObjSh )
1434 if (pObjSh)
1436 uno::Reference<uno::XInterface> xInt( pObjSh->GetBaseModel(), uno::UNO_QUERY );
1437 SetCaller( xInt );
1441 void ScUnoAddInCall::SetParam( sal_Int32 nPos, const uno::Any& rValue )
1443 if ( !pFuncData )
1444 return;
1446 sal_Int32 nCount = pFuncData->GetArgumentCount();
1447 const ScAddInArgDesc* pArgs = pFuncData->GetArguments();
1448 if ( nCount > 0 && nPos >= nCount-1 && pArgs[nCount-1].eType == SC_ADDINARG_VARARGS )
1450 sal_Int32 nVarPos = nPos-(nCount-1);
1451 if ( nVarPos < aVarArg.getLength() )
1452 aVarArg.getArray()[nVarPos] = rValue;
1453 else
1455 OSL_FAIL("wrong argument number");
1458 else if ( nPos < aArgs.getLength() )
1459 aArgs.getArray()[nPos] = rValue;
1460 else
1462 OSL_FAIL("wrong argument number");
1466 void ScUnoAddInCall::ExecuteCall()
1468 if ( !pFuncData )
1469 return;
1471 sal_Int32 nCount = pFuncData->GetArgumentCount();
1472 const ScAddInArgDesc* pArgs = pFuncData->GetArguments();
1473 if ( nCount > 0 && pArgs[nCount-1].eType == SC_ADDINARG_VARARGS )
1475 // insert aVarArg as last argument
1476 //TODO: after inserting caller (to prevent copying twice)?
1478 OSL_ENSURE( aArgs.getLength() == nCount, "wrong argument count" );
1479 aArgs.getArray()[nCount-1] <<= aVarArg;
1482 if ( pFuncData->GetCallerPos() != SC_CALLERPOS_NONE )
1484 uno::Any aCallerAny;
1485 aCallerAny <<= xCaller;
1487 sal_Int32 nUserLen = aArgs.getLength();
1488 sal_Int32 nCallPos = pFuncData->GetCallerPos();
1489 if (nCallPos>nUserLen) // should not happen
1491 OSL_FAIL("wrong CallPos");
1492 nCallPos = nUserLen;
1495 sal_Int32 nDestLen = nUserLen + 1;
1496 uno::Sequence<uno::Any> aRealArgs( nDestLen );
1497 uno::Any* pDest = aRealArgs.getArray();
1499 pDest = std::copy_n(std::cbegin(aArgs), nCallPos, pDest);
1500 *pDest = std::move(aCallerAny);
1501 std::copy(std::next(std::cbegin(aArgs), nCallPos), std::cend(aArgs), std::next(pDest));
1503 ExecuteCallWithArgs( aRealArgs );
1505 else
1506 ExecuteCallWithArgs( aArgs );
1509 void ScUnoAddInCall::ExecuteCallWithArgs(uno::Sequence<uno::Any>& rCallArgs)
1511 // rCallArgs may not match argument descriptions (because of caller)
1513 uno::Reference<reflection::XIdlMethod> xFunction;
1514 uno::Any aObject;
1515 if ( pFuncData )
1517 xFunction = pFuncData->GetFunction();
1518 aObject = pFuncData->GetObject();
1521 if ( !xFunction.is() )
1522 return;
1524 uno::Any aAny;
1525 nErrCode = FormulaError::NONE;
1529 aAny = xFunction->invoke( aObject, rCallArgs );
1531 catch(lang::IllegalArgumentException&)
1533 nErrCode = FormulaError::IllegalArgument;
1535 catch(const reflection::InvocationTargetException& rWrapped)
1537 if ( rWrapped.TargetException.getValueType().equals(
1538 cppu::UnoType<lang::IllegalArgumentException>::get()) )
1539 nErrCode = FormulaError::IllegalArgument;
1540 else if ( rWrapped.TargetException.getValueType().equals(
1541 cppu::UnoType<sheet::NoConvergenceException>::get()) )
1542 nErrCode = FormulaError::NoConvergence;
1543 else
1544 nErrCode = FormulaError::NoValue;
1546 catch(uno::Exception&)
1548 nErrCode = FormulaError::NoValue;
1551 if (nErrCode == FormulaError::NONE)
1552 SetResult( aAny ); // convert result to Calc types
1555 template <typename T>
1556 static sal_Int32 lcl_GetMaxColCount(const uno::Sequence< uno::Sequence<T> >* pRowSeq)
1558 if (!pRowSeq->hasElements())
1559 return 0;
1561 auto pRow = std::max_element(pRowSeq->begin(), pRowSeq->end(),
1562 [](const uno::Sequence<T>& a, const uno::Sequence<T>& b) {
1563 return a.getLength() < b.getLength(); });
1564 return pRow->getLength();
1567 void ScUnoAddInCall::SetResult( const uno::Any& rNewRes )
1569 nErrCode = FormulaError::NONE;
1570 xVarRes = nullptr;
1572 // Reflection* pRefl = rNewRes.getReflection();
1574 uno::TypeClass eClass = rNewRes.getValueTypeClass();
1575 const uno::Type& aType = rNewRes.getValueType();
1576 switch (eClass)
1578 case uno::TypeClass_VOID:
1579 nErrCode = FormulaError::NotAvailable; // #NA
1580 break;
1582 case uno::TypeClass_ENUM:
1583 case uno::TypeClass_BOOLEAN:
1584 case uno::TypeClass_CHAR:
1585 case uno::TypeClass_BYTE:
1586 case uno::TypeClass_SHORT:
1587 case uno::TypeClass_UNSIGNED_SHORT:
1588 case uno::TypeClass_LONG:
1589 case uno::TypeClass_UNSIGNED_LONG:
1590 case uno::TypeClass_FLOAT:
1591 case uno::TypeClass_DOUBLE:
1593 uno::TypeClass eMyClass;
1594 ScApiTypeConversion::ConvertAnyToDouble( fValue, eMyClass, rNewRes);
1595 bHasString = false;
1597 break;
1599 case uno::TypeClass_STRING:
1601 rNewRes >>= aString;
1602 bHasString = true;
1604 break;
1606 case uno::TypeClass_INTERFACE:
1608 //TODO: directly extract XVolatileResult from any?
1609 uno::Reference<uno::XInterface> xInterface;
1610 rNewRes >>= xInterface;
1611 if ( xInterface.is() )
1612 xVarRes.set( xInterface, uno::UNO_QUERY );
1614 if (!xVarRes.is())
1615 nErrCode = FormulaError::NoValue; // unknown interface
1617 break;
1619 default:
1620 if ( aType.equals( cppu::UnoType<uno::Sequence< uno::Sequence<sal_Int32> >>::get() ) )
1622 const uno::Sequence< uno::Sequence<sal_Int32> >* pRowSeq = nullptr;
1624 //TODO: use pointer from any!
1625 uno::Sequence< uno::Sequence<sal_Int32> > aSequence;
1626 if ( rNewRes >>= aSequence )
1627 pRowSeq = &aSequence;
1629 if ( pRowSeq )
1631 sal_Int32 nRowCount = pRowSeq->getLength();
1632 sal_Int32 nMaxColCount = lcl_GetMaxColCount(pRowSeq);
1633 if ( nMaxColCount && nRowCount )
1635 const uno::Sequence<sal_Int32>* pRowArr = pRowSeq->getConstArray();
1636 xMatrix = new ScMatrix(
1637 static_cast<SCSIZE>(nMaxColCount),
1638 static_cast<SCSIZE>(nRowCount), 0.0);
1639 for (sal_Int32 nRow=0; nRow<nRowCount; nRow++)
1641 sal_Int32 nColCount = pRowArr[nRow].getLength();
1642 const sal_Int32* pColArr = pRowArr[nRow].getConstArray();
1643 for (sal_Int32 nCol=0; nCol<nColCount; nCol++)
1644 xMatrix->PutDouble( pColArr[nCol],
1645 static_cast<SCSIZE>(nCol),
1646 static_cast<SCSIZE>(nRow) );
1647 for (sal_Int32 nCol=nColCount; nCol<nMaxColCount; nCol++)
1648 xMatrix->PutDouble( 0.0,
1649 static_cast<SCSIZE>(nCol),
1650 static_cast<SCSIZE>(nRow) );
1655 else if ( aType.equals( cppu::UnoType<uno::Sequence< uno::Sequence<double> >>::get() ) )
1657 const uno::Sequence< uno::Sequence<double> >* pRowSeq = nullptr;
1659 //TODO: use pointer from any!
1660 uno::Sequence< uno::Sequence<double> > aSequence;
1661 if ( rNewRes >>= aSequence )
1662 pRowSeq = &aSequence;
1664 if ( pRowSeq )
1666 sal_Int32 nRowCount = pRowSeq->getLength();
1667 sal_Int32 nMaxColCount = lcl_GetMaxColCount(pRowSeq);
1668 if ( nMaxColCount && nRowCount )
1670 const uno::Sequence<double>* pRowArr = pRowSeq->getConstArray();
1671 xMatrix = new ScMatrix(
1672 static_cast<SCSIZE>(nMaxColCount),
1673 static_cast<SCSIZE>(nRowCount), 0.0);
1674 for (sal_Int32 nRow=0; nRow<nRowCount; nRow++)
1676 sal_Int32 nColCount = pRowArr[nRow].getLength();
1677 const double* pColArr = pRowArr[nRow].getConstArray();
1678 for (sal_Int32 nCol=0; nCol<nColCount; nCol++)
1679 xMatrix->PutDouble( pColArr[nCol],
1680 static_cast<SCSIZE>(nCol),
1681 static_cast<SCSIZE>(nRow) );
1682 for (sal_Int32 nCol=nColCount; nCol<nMaxColCount; nCol++)
1683 xMatrix->PutDouble( 0.0,
1684 static_cast<SCSIZE>(nCol),
1685 static_cast<SCSIZE>(nRow) );
1690 else if ( aType.equals( cppu::UnoType<uno::Sequence< uno::Sequence<OUString> >>::get() ) )
1692 const uno::Sequence< uno::Sequence<OUString> >* pRowSeq = nullptr;
1694 //TODO: use pointer from any!
1695 uno::Sequence< uno::Sequence<OUString> > aSequence;
1696 if ( rNewRes >>= aSequence )
1697 pRowSeq = &aSequence;
1699 if ( pRowSeq )
1701 sal_Int32 nRowCount = pRowSeq->getLength();
1702 sal_Int32 nMaxColCount = lcl_GetMaxColCount(pRowSeq);
1703 if ( nMaxColCount && nRowCount )
1705 const uno::Sequence<OUString>* pRowArr = pRowSeq->getConstArray();
1706 xMatrix = new ScMatrix(
1707 static_cast<SCSIZE>(nMaxColCount),
1708 static_cast<SCSIZE>(nRowCount), 0.0);
1709 for (sal_Int32 nRow=0; nRow<nRowCount; nRow++)
1711 sal_Int32 nColCount = pRowArr[nRow].getLength();
1712 const OUString* pColArr = pRowArr[nRow].getConstArray();
1713 for (sal_Int32 nCol=0; nCol<nColCount; nCol++)
1715 xMatrix->PutString(
1716 mrDoc.GetSharedStringPool().intern(pColArr[nCol]),
1717 static_cast<SCSIZE>(nCol), static_cast<SCSIZE>(nRow));
1719 for (sal_Int32 nCol=nColCount; nCol<nMaxColCount; nCol++)
1721 xMatrix->PutString(
1722 svl::SharedString::getEmptyString(),
1723 static_cast<SCSIZE>(nCol), static_cast<SCSIZE>(nRow));
1729 else if ( aType.equals( cppu::UnoType<uno::Sequence< uno::Sequence<uno::Any> >>::get() ) )
1731 xMatrix = ScSequenceToMatrix::CreateMixedMatrix( rNewRes );
1734 if (!xMatrix) // no array found
1735 nErrCode = FormulaError::NoValue; //TODO: code for error in return type???
1739 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */