1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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>
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>
67 using namespace com::sun::star
;
69 #define SC_CALLERPOS_NONE (-1)
71 ScUnoAddInFuncData::ScUnoAddInFuncData( const OUString
& rNam
, const OUString
& rLoc
,
73 sal_uInt16 nCat
, OUString sHelp
,
74 uno::Reference
<reflection::XIdlMethod
> xFunc
,
76 sal_Int32 nAC
, const ScAddInArgDesc
* pAD
,
78 aOriginalName( rNam
),
82 aDescription(std::move( aDesc
)),
83 xFunction(std::move( xFunc
)),
84 aObject(std::move( aO
)),
88 sHelpId(std::move( sHelp
)),
89 bCompInitialized( false )
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
));
121 for (const sheet::LocalizedName
& rCompName
: aCompNames
)
123 maCompNames
.emplace_back(
124 LanguageTag::convertToBcp47( rCompName
.Locale
, false),
130 bCompInitialized
= true; // also if not successful
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
);
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
;
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
;
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");
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
;
211 // Last resort, use first (default) entry.
212 rRetExcelName
= rCompNames
[0].maName
;
219 void ScUnoAddInFuncData::SetFunction( const uno::Reference
< reflection::XIdlMethod
>& rNewFunc
, const uno::Any
& rNewObj
)
221 xFunction
= rNewFunc
;
225 void ScUnoAddInFuncData::SetArguments( sal_Int32 nNewCount
, const ScAddInArgDesc
* pNewDescs
)
227 nArgCount
= nNewCount
;
230 pArgDescs
.reset( new ScAddInArgDesc
[nArgCount
] );
231 for (sal_Int32 i
=0; i
<nArgCount
; i
++)
232 pArgDescs
[i
] = pNewDescs
[i
];
238 void ScUnoAddInFuncData::SetCallerPos( sal_Int32 nNewPos
)
240 nCallerPos
= nNewPos
;
243 ScUnoAddInCollection::ScUnoAddInCollection() :
245 bInitialized( false )
249 ScUnoAddInCollection::~ScUnoAddInCollection()
253 void ScUnoAddInCollection::Clear()
255 pExactHashMap
.reset();
256 pNameHashMap
.reset();
257 pLocalHashMap
.reset();
258 pEnglishHashMap
.reset();
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
);
273 uno::Reference
<container::XEnumeration
> xEnum
=
274 xEnAc
->createContentEnumeration( u
"com.sun.star.sheet.AddIn"_ustr
);
277 // loop through all AddIns
278 while ( xEnum
->hasMoreElements() )
280 uno::Any aAddInAny
= xEnum
->nextElement();
284 uno::Reference
<uno::XInterface
> xIntFac
;
285 aAddInAny
>>= xIntFac
;
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
);
297 xInterface
= xCFac
->createInstanceWithContext(xCtx
);
299 ReadFromAddIn( xInterface
);
302 if (!xInterface
.is())
304 uno::Reference
<lang::XSingleServiceFactory
> xFac( xIntFac
, uno::UNO_QUERY
);
307 xInterface
= xFac
->createInstance();
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).
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)
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();
395 sal_Int32 nOld
= nFuncCount
;
396 nFuncCount
= nNewCount
+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
);
405 ppFuncData
.reset( new std::unique_ptr
<ScUnoAddInFuncData
>[nFuncCount
] );
407 //TODO: adjust bucket count?
408 if ( !pExactHashMap
)
409 pExactHashMap
.reset( new ScAddInHashMap
);
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
;
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).
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).
505 rConfig
.Value
>>= aName
;
506 if (!aName
.isEmpty())
508 aCompNames
.emplace_back( aLocale
, aName
);
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
;
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
]);
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
,
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(),
598 pNameHashMap
->emplace(
599 pData
->GetUpperName(),
601 pLocalHashMap
->emplace(
602 pData
->GetUpperLocal(),
605 if (aEnglishName
.isEmpty())
606 SAL_WARN("sc.core", "no English name for " << aLocalName
<< " " << aFuncName
);
609 pEnglishHashMap
->emplace(
610 ScCompiler::GetCharClassEnglish()->uppercase(aEnglishName
),
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( '.' );
626 OUString aServiceName
= aFullName
.copy( 0, nPos
);
630 uno::Reference
<lang::XMultiServiceFactory
> xServiceFactory
= comphelper::getProcessServiceFactory();
631 uno::Reference
<uno::XInterface
> xInterface( xServiceFactory
->createInstance( aServiceName
) );
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
);
648 return pFuncData
->GetExcelName( LanguageTag( eDestLang
), rRetExcelName
);
652 bool ScUnoAddInCollection::GetCalcName( const OUString
& rExcelName
, OUString
& rRetCalcName
)
657 OUString aUpperCmp
= ScGlobal::getCharClass().uppercase(rExcelName
);
659 for (sal_Int32 i
=0; i
<nFuncCount
; i
++)
661 ScUnoAddInFuncData
* pFuncData
= ppFuncData
[i
].get();
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
; });
670 //TODO: store upper case for comparing?
672 // use the first function that has this name for any language
673 rRetCalcName
= pFuncData
->GetOriginalName();
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();
715 IsTypeName( sName
, cppu::UnoType
<sheet::XVolatileResult
>::get()) ||
716 IsTypeName( sName
, cppu::UnoType
<uno::XInterface
>::get()) );
721 // nested sequences for arrays
722 //TODO: XIdlClass needs getType() method!
724 OUString sName
= xClass
->getName();
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
)
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()) )
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
792 xAddIn
->setLocale( Application::GetSettings().GetUILanguageTag().getLocale());
794 // Instead, in a second run with 'en-US' obtain English names.
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
);
812 uno::Reference
<beans::XIntrospectionAccess
> xAcc
= xIntro
->inspect(aObject
);
816 uno::Sequence
< uno::Reference
<reflection::XIdlMethod
> > aMethods
=
817 xAcc
->getMethods( beans::MethodConcept::ALL
);
818 sal_Int32 nNewCount
= aMethods
.getLength();
822 sal_Int32 nOld
= nFuncCount
;
823 nFuncCount
= nNewCount
+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
);
832 ppFuncData
.reset(new std::unique_ptr
<ScUnoAddInFuncData
>[nFuncCount
]);
834 //TODO: adjust bucket count?
835 if ( !pExactHashMap
)
836 pExactHashMap
.reset( new ScAddInHashMap
);
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
];
852 // leave out internal functions
853 uno::Reference
<reflection::XIdlClass
> xClass
=
854 xFunc
->getDeclaringClass();
858 //TODO: XIdlClass needs getType() method!
859 OUString sName
= xClass
->getName();
862 cppu::UnoType
<uno::XInterface
>::get()) ||
864 cppu::UnoType
<lang::XServiceName
>::get()) ||
866 cppu::UnoType
<lang::XServiceInfo
>::get()) ||
868 cppu::UnoType
<sheet::XAddIn
>::get()) );
872 uno::Reference
<reflection::XIdlClass
> xReturn
=
873 xFunc
->getReturnType();
874 if ( !lcl_ValidReturnType( xReturn
) )
879 OUString aFuncU
= xFunc
->getName();
881 // stored function name: (service name).(function)
882 OUString aFuncName
= aServiceName
+ "." + aFuncU
;
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();
893 for (nParamPos
=0; nParamPos
<nParamCount
; nParamPos
++)
895 if ( pParArr
[nParamPos
].aMode
!= reflection::ParamMode_IN
)
897 uno::Reference
<reflection::XIdlClass
> xParClass
=
898 pParArr
[nParamPos
].aType
;
899 ScAddInArgumentType eArgType
= lcl_GetArgType( xParClass
);
900 if ( eArgType
== SC_ADDINARG_NONE
)
902 else if ( eArgType
== SC_ADDINARG_CALLER
)
903 nCallerPos
= nParamPos
;
909 sal_uInt16 nCategory
= lcl_GetCategory(
910 xAddIn
->getProgrammaticCategoryName( aFuncU
) );
912 OUString sHelpId
= aHelpIdGenerator
.GetHelpId( aFuncU
);
917 aLocalName
= xAddIn
->
918 getDisplayFunctionName( aFuncU
);
920 catch(uno::Exception
&)
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
)
953 getDisplayArgumentName( aFuncU
, nParamPos
);
955 catch(uno::Exception
&)
963 getArgumentDescription( aFuncU
, nParamPos
);
965 catch(uno::Exception
&)
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
,
991 nVisibleCount
, pVisibleArgs
.get(), nCallerPos
) );
993 ScUnoAddInFuncData
* pData
= ppFuncData
[nFuncPos
+nOld
].get();
994 pExactHashMap
->emplace(
995 pData
->GetOriginalName(),
997 pNameHashMap
->emplace(
998 pData
->GetUpperName(),
1000 pLocalHashMap
->emplace(
1001 pData
->GetUpperLocal(),
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
);
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
];
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
);
1093 aObject
<<= xInterface
;
1094 uno::Reference
<beans::XIntrospectionAccess
> xAcc
= xIntro
->inspect(aObject
);
1098 const uno::Sequence
< uno::Reference
<reflection::XIdlMethod
> > aMethods
=
1099 xAcc
->getMethods( beans::MethodConcept::ALL
);
1100 for (const uno::Reference
<reflection::XIdlMethod
>& xFunc
: aMethods
)
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
) );
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.
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
)
1130 uno::Reference
<reflection::XIdlClass
> xParClass
=
1131 pParArr
[nParamPos
].aType
;
1132 ScAddInArgumentType eArgType
= lcl_GetArgType( xParClass
);
1133 if ( eArgType
== SC_ADDINARG_NONE
)
1135 else if ( eArgType
== SC_ADDINARG_CALLER
)
1136 nCallerPos
= nParamPos
;
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
=
1152 ScAddInArgumentType eArgType
= lcl_GetArgType( xParClass
);
1153 if ( eArgType
!= SC_ADDINARG_CALLER
)
1155 const ScAddInArgDesc
* pOldArgDesc
=
1156 lcl_FindArgDesc( *pOldData
, rParam
.aName
);
1159 aDesc
.aName
= pOldArgDesc
->aName
;
1160 aDesc
.aDescription
= pOldArgDesc
->aDescription
;
1163 aDesc
.aName
= aDesc
.aDescription
= "###";
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
)
1197 if (nFuncCount
== 0)
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();
1210 // First scan international programmatic names (used when calling a
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
1227 iLook
= pLocalHashMap
->find( rUpperName
);
1228 if ( iLook
!= pLocalHashMap
->end() )
1229 return iLook
->second
->GetOriginalName();
1235 const ScUnoAddInFuncData
* ScUnoAddInCollection::GetFuncData( const OUString
& rName
, bool bComplete
)
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
);
1256 const ScUnoAddInFuncData
* ScUnoAddInCollection::GetFuncData( sal_Int32 nIndex
)
1261 if (nIndex
< nFuncCount
)
1262 return ppFuncData
[nIndex
].get();
1266 void ScUnoAddInCollection::LocalizeString( OUString
& rName
)
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()
1286 bool ScUnoAddInCollection::FillFunctionDesc( sal_Int32 nFunc
, ScFuncDesc
& rDesc
, bool bEnglishFunctionNames
)
1291 if (nFunc
>= nFuncCount
|| !ppFuncData
[nFunc
])
1294 const ScUnoAddInFuncData
& rFuncData
= *ppFuncData
[nFunc
];
1296 return FillFunctionDescFromData( rFuncData
, rDesc
, bEnglishFunctionNames
);
1299 bool ScUnoAddInCollection::FillFunctionDescFromData( const ScUnoAddInFuncData
& rFuncData
, ScFuncDesc
& rDesc
,
1300 bool bEnglishFunctionNames
)
1304 bool bIncomplete
= !rFuncData
.GetFunction().is(); //TODO: extra flag?
1306 sal_Int32 nArgCount
= rFuncData
.GetArgumentCount();
1307 if ( nArgCount
> SAL_MAX_UINT16
)
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
);
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
) )
1353 rDesc
.nArgCount
+= VAR_ARGS
- 1; // VAR_ARGS means just one repeated arg
1356 rDesc
.bIncomplete
= bIncomplete
;
1361 ScUnoAddInCall::ScUnoAddInCall( ScDocument
& rDoc
, ScUnoAddInCollection
& rColl
, const OUString
& rName
,
1362 sal_Int32 nParamCount
) :
1364 bValidCount( false ),
1365 nErrCode( FormulaError::NoCode
), // before function was called
1370 pFuncData
= rColl
.GetFuncData( rName
, true ); // need fully initialized data
1371 OSL_ENSURE( pFuncData
, "Function Data missing" );
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
);
1386 else if ( nParamCount
<= nDescCount
)
1388 // all args behind nParamCount must be optional
1390 for (sal_Int32 i
=nParamCount
; i
<nDescCount
; i
++)
1391 if ( !pArgs
[i
].bOptional
)
1392 bValidCount
= false;
1394 // else invalid (too many arguments)
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
)
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
)
1436 uno::Reference
<uno::XInterface
> xInt( pObjSh
->GetBaseModel(), uno::UNO_QUERY
);
1441 void ScUnoAddInCall::SetParam( sal_Int32 nPos
, const uno::Any
& rValue
)
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
;
1455 OSL_FAIL("wrong argument number");
1458 else if ( nPos
< aArgs
.getLength() )
1459 aArgs
.getArray()[nPos
] = rValue
;
1462 OSL_FAIL("wrong argument number");
1466 void ScUnoAddInCall::ExecuteCall()
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
);
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
;
1517 xFunction
= pFuncData
->GetFunction();
1518 aObject
= pFuncData
->GetObject();
1521 if ( !xFunction
.is() )
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
;
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())
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
;
1572 // Reflection* pRefl = rNewRes.getReflection();
1574 uno::TypeClass eClass
= rNewRes
.getValueTypeClass();
1575 const uno::Type
& aType
= rNewRes
.getValueType();
1578 case uno::TypeClass_VOID
:
1579 nErrCode
= FormulaError::NotAvailable
; // #NA
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
);
1599 case uno::TypeClass_STRING
:
1601 rNewRes
>>= aString
;
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
);
1615 nErrCode
= FormulaError::NoValue
; // unknown interface
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
;
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
;
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
;
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
++)
1716 mrDoc
.GetSharedStringPool().intern(pColArr
[nCol
]),
1717 static_cast<SCSIZE
>(nCol
), static_cast<SCSIZE
>(nRow
));
1719 for (sal_Int32 nCol
=nColCount
; nCol
<nMaxColCount
; nCol
++)
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: */