/************************************************************** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_sc.hxx" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "addincol.hxx" #include "addinhelpid.hxx" #include "compiler.hxx" #include "scmatrix.hxx" #include "addinlis.hxx" #include "formula/errorcodes.hxx" #include "scfuncs.hrc" #include "optutil.hxx" #include "addincfg.hxx" #include "scmod.hxx" #include "rangeseq.hxx" #include "funcdesc.hxx" using namespace com::sun::star; //------------------------------------------------------------------------ #define SC_CALLERPOS_NONE (-1) #define SCADDINSUPPLIER_SERVICE "com.sun.star.sheet.AddIn" //------------------------------------------------------------------------ //------------------------------------------------------------------------ ScUnoAddInFuncData::ScUnoAddInFuncData( const String& rNam, const String& rLoc, const String& rDesc, sal_uInt16 nCat, const rtl::OString& sHelp, const uno::Reference& rFunc, const uno::Any& rO, long nAC, const ScAddInArgDesc* pAD, long nCP ) : aOriginalName( rNam ), aLocalName( rLoc ), aUpperName( rNam ), aUpperLocal( rLoc ), aDescription( rDesc ), xFunction( rFunc ), aObject( rO ), nArgCount( nAC ), nCallerPos( nCP ), nCategory( nCat ), sHelpId( sHelp ), bCompInitialized( sal_False ) { if ( nArgCount ) { pArgDescs = new ScAddInArgDesc[nArgCount]; for (long i=0; itoUpper(aUpperName); ScGlobal::pCharClass->toUpper(aUpperLocal); } ScUnoAddInFuncData::~ScUnoAddInFuncData() { delete[] pArgDescs; } const uno::Sequence& ScUnoAddInFuncData::GetCompNames() const { if ( !bCompInitialized ) { // read sequence of compatibility names on demand uno::Reference xAddIn; if ( aObject >>= xAddIn ) { uno::Reference xComp( xAddIn, uno::UNO_QUERY ); if ( xComp.is() && xFunction.is() ) { rtl::OUString aMethodName = xFunction->getName(); aCompNames = xComp->getCompatibilityNames( aMethodName ); // change all locale entries to default case // (language in lower case, country in upper case) // for easier searching long nSeqLen = aCompNames.getLength(); if ( nSeqLen ) { sheet::LocalizedName* pArray = aCompNames.getArray(); for (long i=0; i& rNew ) { DBG_ASSERT( !bCompInitialized, "SetCompNames after initializing" ); aCompNames = rNew; // change all locale entries to default case // (language in lower case, country in upper case) // for easier searching long nSeqLen = aCompNames.getLength(); if ( nSeqLen ) { sheet::LocalizedName* pArray = aCompNames.getArray(); for (long i=0; i& rSequence = GetCompNames(); long nSeqLen = rSequence.getLength(); if ( nSeqLen ) { const sheet::LocalizedName* pArray = rSequence.getConstArray(); long i; rtl::OUString aLangStr, aCountryStr; MsLangId::convertLanguageToIsoNames( eDestLang, aLangStr, aCountryStr ); rtl::OUString aUserLang = aLangStr.toAsciiLowerCase(); rtl::OUString aUserCountry = aCountryStr.toAsciiUpperCase(); // first check for match of both language and country for ( i=0; i& rNewFunc, const uno::Any& rNewObj ) { xFunction = rNewFunc; aObject = rNewObj; } void ScUnoAddInFuncData::SetArguments( long nNewCount, const ScAddInArgDesc* pNewDescs ) { delete[] pArgDescs; nArgCount = nNewCount; if ( nArgCount ) { pArgDescs = new ScAddInArgDesc[nArgCount]; for (long i=0; i getContext(uno::Reference xMSF) { uno::Reference xCtx; try { uno::Reference xPropset(xMSF, uno::UNO_QUERY); xPropset->getPropertyValue( ::rtl::OUString::createFromAscii("DefaultContext")) >>= xCtx; } catch ( uno::Exception & ) { } return xCtx; } void ScUnoAddInCollection::Initialize() { DBG_ASSERT( !bInitialized, "Initialize twice?" ); uno::Reference xManager = comphelper::getProcessServiceFactory(); uno::Reference xEnAc( xManager, uno::UNO_QUERY ); if ( xEnAc.is() ) { uno::Reference xEnum = xEnAc->createContentEnumeration( rtl::OUString::createFromAscii(SCADDINSUPPLIER_SERVICE) ); if ( xEnum.is() ) { // loop through all AddIns while ( xEnum->hasMoreElements() ) { uno::Any aAddInAny = xEnum->nextElement(); //? if ( aAddInAny.getReflection()->getTypeClass() == uno::TypeClass_INTERFACE ) { uno::Reference xIntFac; aAddInAny >>= xIntFac; if ( xIntFac.is() ) { // #i59984# try XSingleComponentFactory in addition to (old) XSingleServiceFactory, // passing the context to the component uno::Reference xInterface; uno::Reference xCtx = getContext(xManager); uno::Reference xCFac( xIntFac, uno::UNO_QUERY ); if (xCtx.is() && xCFac.is()) { xInterface = xCFac->createInstanceWithContext(xCtx); if (xInterface.is()) ReadFromAddIn( xInterface ); } if (!xInterface.is()) { uno::Reference xFac( xIntFac, uno::UNO_QUERY ); if ( xFac.is() ) { xInterface = xFac->createInstance(); if (xInterface.is()) ReadFromAddIn( xInterface ); } } } } } } } // ReadConfiguration is called after looking at the AddIn implementations. // Duplicated are skipped (by using the service information, they don't have to be updated again // when argument information is needed). ReadConfiguration(); bInitialized = sal_True; // with or without functions } // ----------------------------------------------------------------------------- sal_uInt16 lcl_GetCategory( const String& rName ) { static const sal_Char* aFuncNames[SC_FUNCGROUP_COUNT] = { // array index = ID - 1 (ID starts at 1) // all upper case "Database", // ID_FUNCTION_GRP_DATABASE "Date&Time", // ID_FUNCTION_GRP_DATETIME "Financial", // ID_FUNCTION_GRP_FINANZ "Information", // ID_FUNCTION_GRP_INFO "Logical", // ID_FUNCTION_GRP_LOGIC "Mathematical", // ID_FUNCTION_GRP_MATH "Matrix", // ID_FUNCTION_GRP_MATRIX "Statistical", // ID_FUNCTION_GRP_STATISTIC "Spreadsheet", // ID_FUNCTION_GRP_TABLE "Text", // ID_FUNCTION_GRP_TEXT "Add-In" // ID_FUNCTION_GRP_ADDINS }; for (sal_uInt16 i=0; iGetAddInCfg(); // additional, temporary config item for the compatibility names ScLinkConfigItem aAllLocalesConfig( rtl::OUString::createFromAscii( CFGPATH_ADDINS ), CONFIG_MODE_ALL_LOCALES ); // CommitLink is not used (only reading values) const rtl::OUString sSlash('/'); // get the list of add-ins (services) rtl::OUString aEmptyString; uno::Sequence aServiceNames = rAddInConfig.GetNodeNames( aEmptyString ); sal_Int32 nServiceCount = aServiceNames.getLength(); for ( sal_Int32 nService = 0; nService < nServiceCount; nService++ ) { rtl::OUString aServiceName = aServiceNames[nService]; ScUnoAddInHelpIdGenerator aHelpIdGenerator( aServiceName ); rtl::OUString aFunctionsPath = aServiceName; aFunctionsPath += sSlash; aFunctionsPath += rtl::OUString::createFromAscii( CFGSTR_ADDINFUNCTIONS ); uno::Sequence aFunctionNames = rAddInConfig.GetNodeNames( aFunctionsPath ); sal_Int32 nNewCount = aFunctionNames.getLength(); // allocate pointers long nOld = nFuncCount; nFuncCount = nNewCount+nOld; if ( nOld ) { ScUnoAddInFuncData** ppNew = new ScUnoAddInFuncData*[nFuncCount]; for (long i=0; ifind( aFuncName ) == pExactHashMap->end() ) { rtl::OUString aLocalName; rtl::OUString aDescription; sal_uInt16 nCategory = ID_FUNCTION_GRP_ADDINS; // get direct information on the function rtl::OUString aFuncPropPath = aFunctionsPath; aFuncPropPath += sSlash; aFuncPropPath += pFuncNameArray[nFuncPos]; aFuncPropPath += sSlash; uno::Sequence aFuncPropNames(CFG_FUNCPROP_COUNT); rtl::OUString* pNameArray = aFuncPropNames.getArray(); pNameArray[CFG_FUNCPROP_DISPLAYNAME] = aFuncPropPath; pNameArray[CFG_FUNCPROP_DISPLAYNAME] += rtl::OUString::createFromAscii( CFGSTR_DISPLAYNAME ); pNameArray[CFG_FUNCPROP_DESCRIPTION] = aFuncPropPath; pNameArray[CFG_FUNCPROP_DESCRIPTION] += rtl::OUString::createFromAscii( CFGSTR_DESCRIPTION ); pNameArray[CFG_FUNCPROP_CATEGORY] = aFuncPropPath; pNameArray[CFG_FUNCPROP_CATEGORY] += rtl::OUString::createFromAscii( CFGSTR_CATEGORY ); uno::Sequence aFuncProperties = rAddInConfig.GetProperties( aFuncPropNames ); if ( aFuncProperties.getLength() == CFG_FUNCPROP_COUNT ) { aFuncProperties[CFG_FUNCPROP_DISPLAYNAME] >>= aLocalName; aFuncProperties[CFG_FUNCPROP_DESCRIPTION] >>= aDescription; rtl::OUString aCategoryName; aFuncProperties[CFG_FUNCPROP_CATEGORY] >>= aCategoryName; nCategory = lcl_GetCategory( aCategoryName ); } // get compatibility names uno::Sequence aCompNames; rtl::OUString aCompPath = aFuncPropPath; aCompPath += rtl::OUString::createFromAscii( CFGSTR_COMPATIBILITYNAME ); uno::Sequence aCompPropNames( &aCompPath, 1 ); uno::Sequence aCompProperties = aAllLocalesConfig.GetProperties( aCompPropNames ); if ( aCompProperties.getLength() == 1 ) { uno::Sequence aLocalEntries; if ( aCompProperties[0] >>= aLocalEntries ) { sal_Int32 nLocaleCount = aLocalEntries.getLength(); aCompNames.realloc( nLocaleCount ); const beans::PropertyValue* pConfigArray = aLocalEntries.getConstArray(); sheet::LocalizedName* pCompArray = aCompNames.getArray(); for ( sal_Int32 nLocale = 0; nLocale < nLocaleCount; nLocale++ ) { const sal_Unicode cLocaleSep = '-'; // separator in configuration locale strings // PropertyValue name is the locale (convert from string to Locale struct) const rtl::OUString& rLocaleStr = pConfigArray[nLocale].Name; lang::Locale& rLocale = pCompArray[nLocale].Locale; sal_Int32 nSepPos = rLocaleStr.indexOf( cLocaleSep ); if ( nSepPos >= 0 ) { rLocale.Language = rLocaleStr.copy( 0, nSepPos ); rLocale.Country = rLocaleStr.copy( nSepPos+1 ); } else rLocale.Language = rLocaleStr; // leave country empty (default ctor from sequence) // PropertyValue value is the localized value (string in this case) pConfigArray[nLocale].Value >>= pCompArray[nLocale].Name; } } } // get argument info ScAddInArgDesc* pVisibleArgs = NULL; long nVisibleCount = 0; long nCallerPos = SC_CALLERPOS_NONE; rtl::OUString aArgumentsPath = aFuncPropPath; aArgumentsPath += rtl::OUString::createFromAscii( CFGSTR_PARAMETERS ); uno::Sequence aArgumentNames = rAddInConfig.GetNodeNames( aArgumentsPath ); sal_Int32 nArgumentCount = aArgumentNames.getLength(); if ( nArgumentCount ) { // get DisplayName and Description for each argument uno::Sequence aArgPropNames( nArgumentCount * 2 ); rtl::OUString* pPropNameArray = aArgPropNames.getArray(); sal_Int32 nArgument; sal_Int32 nIndex = 0; const rtl::OUString* pArgNameArray = aArgumentNames.getConstArray(); for ( nArgument = 0; nArgument < nArgumentCount; nArgument++ ) { rtl::OUString aOneArgPath = aArgumentsPath; aOneArgPath += sSlash; aOneArgPath += pArgNameArray[nArgument]; aOneArgPath += sSlash; pPropNameArray[nIndex] = aOneArgPath; pPropNameArray[nIndex++] += rtl::OUString::createFromAscii( CFGSTR_DISPLAYNAME ); pPropNameArray[nIndex] = aOneArgPath; pPropNameArray[nIndex++] += rtl::OUString::createFromAscii( CFGSTR_DESCRIPTION ); } uno::Sequence aArgProperties = rAddInConfig.GetProperties( aArgPropNames ); if ( aArgProperties.getLength() == aArgPropNames.getLength() ) { const uno::Any* pPropArray = aArgProperties.getConstArray(); rtl::OUString sDisplayName; rtl::OUString sDescription; ScAddInArgDesc aDesc; aDesc.eType = SC_ADDINARG_NONE; // arg type is not in configuration aDesc.bOptional = sal_False; nVisibleCount = nArgumentCount; pVisibleArgs = new ScAddInArgDesc[nVisibleCount]; nIndex = 0; for ( nArgument = 0; nArgument < nArgumentCount; nArgument++ ) { pPropArray[nIndex++] >>= sDisplayName; pPropArray[nIndex++] >>= sDescription; aDesc.aInternalName = pArgNameArray[nArgument]; aDesc.aName = sDisplayName; aDesc.aDescription = sDescription; pVisibleArgs[nArgument] = aDesc; } } } rtl::OString sHelpId = aHelpIdGenerator.GetHelpId( pFuncNameArray[nFuncPos] ); uno::Reference xFunc; // remains empty uno::Any aObject; // also empty // create and insert into the array ScUnoAddInFuncData* pData = new ScUnoAddInFuncData( aFuncName, aLocalName, aDescription, nCategory, sHelpId, xFunc, aObject, nVisibleCount, pVisibleArgs, nCallerPos ); pData->SetCompNames( aCompNames ); ppFuncData[nFuncPos+nOld] = pData; pExactHashMap->insert( ScAddInHashMap::value_type( pData->GetOriginalName(), pData ) ); pNameHashMap->insert( ScAddInHashMap::value_type( pData->GetUpperName(), pData ) ); pLocalHashMap->insert( ScAddInHashMap::value_type( pData->GetUpperLocal(), pData ) ); delete[] pVisibleArgs; } } } } void ScUnoAddInCollection::LoadComponent( const ScUnoAddInFuncData& rFuncData ) { String aFullName = rFuncData.GetOriginalName(); xub_StrLen nPos = aFullName.SearchBackward( (sal_Unicode) '.' ); if ( nPos != STRING_NOTFOUND && nPos > 0 ) { String aServiceName = aFullName.Copy( 0, nPos ); uno::Reference xServiceFactory = comphelper::getProcessServiceFactory(); uno::Reference xInterface( xServiceFactory->createInstance( aServiceName ) ); if (xInterface.is()) UpdateFromAddIn( xInterface, aServiceName ); } } sal_Bool ScUnoAddInCollection::GetExcelName( const String& rCalcName, LanguageType eDestLang, String& rRetExcelName ) { const ScUnoAddInFuncData* pFuncData = GetFuncData( rCalcName ); if ( pFuncData ) return pFuncData->GetExcelName( eDestLang, rRetExcelName); return sal_False; } sal_Bool ScUnoAddInCollection::GetCalcName( const String& rExcelName, String& rRetCalcName ) { if (!bInitialized) Initialize(); String aUpperCmp = rExcelName; ScGlobal::pCharClass->toUpper(aUpperCmp); for (long i=0; i& rSequence = pFuncData->GetCompNames(); long nSeqLen = rSequence.getLength(); if ( nSeqLen ) { const sheet::LocalizedName* pArray = rSequence.getConstArray(); for ( long nName=0; nNameupper( pArray[nName].Name ) == aUpperCmp ) { //! store upper case for comparing? // use the first function that has this name for any language rRetCalcName = pFuncData->GetOriginalName(); return sal_True; } } } } return sal_False; } inline sal_Bool IsTypeName( const rtl::OUString& rName, const uno::Type& rType ) { return rName == rType.getTypeName(); } sal_Bool lcl_ValidReturnType( const uno::Reference& xClass ) { // this must match with ScUnoAddInCall::SetResult if ( !xClass.is() ) return sal_False; switch (xClass->getTypeClass()) { // case uno::TypeClass_VOID: // ??? case uno::TypeClass_ANY: // variable type case uno::TypeClass_ENUM: //! ??? case uno::TypeClass_BOOLEAN: case uno::TypeClass_CHAR: case uno::TypeClass_BYTE: case uno::TypeClass_SHORT: case uno::TypeClass_UNSIGNED_SHORT: case uno::TypeClass_LONG: case uno::TypeClass_UNSIGNED_LONG: case uno::TypeClass_FLOAT: case uno::TypeClass_DOUBLE: case uno::TypeClass_STRING: return sal_True; // values or string case uno::TypeClass_INTERFACE: { // return type XInterface may contain a XVolatileResult //! XIdlClass needs getType() method! rtl::OUString sName = xClass->getName(); return ( IsTypeName( sName, getCppuType((uno::Reference*)0) ) || IsTypeName( sName, getCppuType((uno::Reference*)0) ) ); } default: { // nested sequences for arrays //! XIdlClass needs getType() method! rtl::OUString sName = xClass->getName(); return ( IsTypeName( sName, getCppuType((uno::Sequence< uno::Sequence >*)0) ) || IsTypeName( sName, getCppuType((uno::Sequence< uno::Sequence >*)0) ) || IsTypeName( sName, getCppuType((uno::Sequence< uno::Sequence >*)0) ) || IsTypeName( sName, getCppuType((uno::Sequence< uno::Sequence >*)0) ) ); } } return sal_False; } ScAddInArgumentType lcl_GetArgType( const uno::Reference& xClass ) { if (!xClass.is()) return SC_ADDINARG_NONE; uno::TypeClass eType = xClass->getTypeClass(); if ( eType == uno::TypeClass_LONG ) //! other integer types? return SC_ADDINARG_INTEGER; if ( eType == uno::TypeClass_DOUBLE ) return SC_ADDINARG_DOUBLE; if ( eType == uno::TypeClass_STRING ) return SC_ADDINARG_STRING; //! XIdlClass needs getType() method! rtl::OUString sName = xClass->getName(); if (IsTypeName( sName, getCppuType((uno::Sequence< uno::Sequence >*)0) )) return SC_ADDINARG_INTEGER_ARRAY; if (IsTypeName( sName, getCppuType((uno::Sequence< uno::Sequence >*)0) )) return SC_ADDINARG_DOUBLE_ARRAY; if (IsTypeName( sName, getCppuType((uno::Sequence< uno::Sequence >*)0) )) return SC_ADDINARG_STRING_ARRAY; if (IsTypeName( sName, getCppuType((uno::Sequence< uno::Sequence >*)0) )) return SC_ADDINARG_MIXED_ARRAY; if (IsTypeName( sName, getCppuType((uno::Any*)0) )) return SC_ADDINARG_VALUE_OR_ARRAY; if (IsTypeName( sName, getCppuType((uno::Reference*)0) )) return SC_ADDINARG_CELLRANGE; if (IsTypeName( sName, getCppuType((uno::Reference*)0) )) return SC_ADDINARG_CALLER; if (IsTypeName( sName, getCppuType((uno::Sequence*)0) )) return SC_ADDINARG_VARARGS; return SC_ADDINARG_NONE; } void ScUnoAddInCollection::ReadFromAddIn( const uno::Reference& xInterface ) { uno::Reference xAddIn( xInterface, uno::UNO_QUERY ); uno::Reference xName( xInterface, uno::UNO_QUERY ); if ( xAddIn.is() && xName.is() ) { // AddIns must use the language for which the office is installed LanguageType eOfficeLang = Application::GetSettings().GetUILanguage(); lang::Locale aLocale( MsLangId::convertLanguageToLocale( eOfficeLang )); xAddIn->setLocale( aLocale ); String aServiceName = String( xName->getServiceName() ); ScUnoAddInHelpIdGenerator aHelpIdGenerator( xName->getServiceName() ); //! pass XIntrospection to ReadFromAddIn uno::Reference xManager = comphelper::getProcessServiceFactory(); if ( xManager.is() ) { uno::Reference xIntro( xManager->createInstance(rtl::OUString::createFromAscii( "com.sun.star.beans.Introspection" )), uno::UNO_QUERY ); if ( xIntro.is() ) { uno::Any aObject; aObject <<= xAddIn; uno::Reference xAcc = xIntro->inspect(aObject); if (xAcc.is()) { uno::Sequence< uno::Reference > aMethods = xAcc->getMethods( beans::MethodConcept::ALL ); long nNewCount = aMethods.getLength(); if ( nNewCount ) { long nOld = nFuncCount; nFuncCount = nNewCount+nOld; if ( nOld ) { ScUnoAddInFuncData** ppNew = new ScUnoAddInFuncData*[nFuncCount]; for (long i=0; i* pArray = aMethods.getConstArray(); for (long nFuncPos=0; nFuncPos xFunc = pArray[nFuncPos]; if (xFunc.is()) { // leave out internal functions uno::Reference xClass = xFunc->getDeclaringClass(); sal_Bool bSkip = sal_True; if ( xClass.is() ) { //! XIdlClass needs getType() method! rtl::OUString sName = xClass->getName(); bSkip = ( IsTypeName( sName, getCppuType((uno::Reference*)0) ) || IsTypeName( sName, getCppuType((uno::Reference*)0) ) || IsTypeName( sName, getCppuType((uno::Reference*)0) ) || IsTypeName( sName, getCppuType((uno::Reference*)0) ) || IsTypeName( sName, getCppuType((uno::Reference*)0) ) ); } if (!bSkip) { uno::Reference xReturn = xFunc->getReturnType(); if ( !lcl_ValidReturnType( xReturn ) ) bSkip = sal_True; } if (!bSkip) { rtl::OUString aFuncU = xFunc->getName(); // stored function name: (service name).(function) String aFuncName = aServiceName; aFuncName += '.'; aFuncName += String( aFuncU ); sal_Bool bValid = sal_True; long nVisibleCount = 0; long nCallerPos = SC_CALLERPOS_NONE; uno::Sequence aParams = xFunc->getParameterInfos(); long nParamCount = aParams.getLength(); const reflection::ParamInfo* pParArr = aParams.getConstArray(); long nParamPos; for (nParamPos=0; nParamPos xParClass = pParArr[nParamPos].aType; ScAddInArgumentType eArgType = lcl_GetArgType( xParClass ); if ( eArgType == SC_ADDINARG_NONE ) bValid = sal_False; else if ( eArgType == SC_ADDINARG_CALLER ) nCallerPos = nParamPos; else ++nVisibleCount; } if (bValid) { sal_uInt16 nCategory = lcl_GetCategory( String( xAddIn->getProgrammaticCategoryName( aFuncU ) ) ); rtl::OString sHelpId = aHelpIdGenerator.GetHelpId( aFuncU ); rtl::OUString aLocalU; try { aLocalU = xAddIn-> getDisplayFunctionName( aFuncU ); } catch(uno::Exception&) { aLocalU = rtl::OUString::createFromAscii( "###" ); } String aLocalName = String( aLocalU ); rtl::OUString aDescU; try { aDescU = xAddIn-> getFunctionDescription( aFuncU ); } catch(uno::Exception&) { aDescU = rtl::OUString::createFromAscii( "###" ); } String aDescription = String( aDescU ); ScAddInArgDesc* pVisibleArgs = NULL; if ( nVisibleCount > 0 ) { ScAddInArgDesc aDesc; pVisibleArgs = new ScAddInArgDesc[nVisibleCount]; long nDestPos = 0; for (nParamPos=0; nParamPos xParClass = pParArr[nParamPos].aType; ScAddInArgumentType eArgType = lcl_GetArgType( xParClass ); if ( eArgType != SC_ADDINARG_CALLER ) { rtl::OUString aArgName; try { aArgName = xAddIn-> getDisplayArgumentName( aFuncU, nParamPos ); } catch(uno::Exception&) { aArgName = rtl::OUString::createFromAscii( "###" ); } rtl::OUString aArgDesc; try { aArgDesc = xAddIn-> getArgumentDescription( aFuncU, nParamPos ); } catch(uno::Exception&) { aArgName = rtl::OUString::createFromAscii( "###" ); } sal_Bool bOptional = ( eArgType == SC_ADDINARG_VALUE_OR_ARRAY || eArgType == SC_ADDINARG_VARARGS ); aDesc.eType = eArgType; aDesc.aName = String( aArgName ); aDesc.aDescription = String( aArgDesc ); aDesc.bOptional = bOptional; //! initialize aInternalName only from config? aDesc.aInternalName = pParArr[nParamPos].aName; pVisibleArgs[nDestPos++] = aDesc; } } DBG_ASSERT( nDestPos==nVisibleCount, "wrong count" ); } ppFuncData[nFuncPos+nOld] = new ScUnoAddInFuncData( aFuncName, aLocalName, aDescription, nCategory, sHelpId, xFunc, aObject, nVisibleCount, pVisibleArgs, nCallerPos ); const ScUnoAddInFuncData* pData = ppFuncData[nFuncPos+nOld]; pExactHashMap->insert( ScAddInHashMap::value_type( pData->GetOriginalName(), pData ) ); pNameHashMap->insert( ScAddInHashMap::value_type( pData->GetUpperName(), pData ) ); pLocalHashMap->insert( ScAddInHashMap::value_type( pData->GetUpperLocal(), pData ) ); delete[] pVisibleArgs; } } } } } } } } } } void lcl_UpdateFunctionList( ScFunctionList& rFunctionList, const ScUnoAddInFuncData& rFuncData ) { String aCompare = rFuncData.GetUpperLocal(); // as used in FillFunctionDescFromData sal_uLong nCount = rFunctionList.GetCount(); for (sal_uLong nPos=0; nPospFuncName && *pDesc->pFuncName == aCompare ) { ScUnoAddInCollection::FillFunctionDescFromData( rFuncData, *const_cast(pDesc) ); break; } } } const ScAddInArgDesc* lcl_FindArgDesc( const ScUnoAddInFuncData& rFuncData, const String& rArgIntName ) { long nArgCount = rFuncData.GetArgumentCount(); const ScAddInArgDesc* pArguments = rFuncData.GetArguments(); for (long nPos=0; nPos& xInterface, const String& rServiceName ) { uno::Reference xLoc( xInterface, uno::UNO_QUERY ); if ( xLoc.is() ) // optional in new add-ins { LanguageType eOfficeLang = Application::GetSettings().GetUILanguage(); lang::Locale aLocale( MsLangId::convertLanguageToLocale( eOfficeLang )); xLoc->setLocale( aLocale ); } // if function list was already initialized, it must be updated ScFunctionList* pFunctionList = NULL; if ( ScGlobal::HasStarCalcFunctionList() ) pFunctionList = ScGlobal::GetStarCalcFunctionList(); // only get the function information from Introspection uno::Reference xManager = comphelper::getProcessServiceFactory(); if ( xManager.is() ) { uno::Reference xIntro( xManager->createInstance(rtl::OUString::createFromAscii( "com.sun.star.beans.Introspection" )), uno::UNO_QUERY ); if ( xIntro.is() ) { uno::Any aObject; aObject <<= xInterface; uno::Reference xAcc = xIntro->inspect(aObject); if (xAcc.is()) { uno::Sequence< uno::Reference > aMethods = xAcc->getMethods( beans::MethodConcept::ALL ); long nMethodCount = aMethods.getLength(); const uno::Reference* pArray = aMethods.getConstArray(); for (long nFuncPos=0; nFuncPos xFunc = pArray[nFuncPos]; if (xFunc.is()) { rtl::OUString aFuncU = xFunc->getName(); // stored function name: (service name).(function) String aFuncName = rServiceName; aFuncName += '.'; aFuncName += String( aFuncU ); // internal names are skipped because no FuncData exists ScUnoAddInFuncData* pOldData = const_cast( GetFuncData( aFuncName ) ); if ( pOldData ) { // Create new (complete) argument info. // As in ReadFromAddIn, the reflection information is authoritative. // Local names and descriptions from pOldData are looked up using the // internal argument name. sal_Bool bValid = sal_True; long nVisibleCount = 0; long nCallerPos = SC_CALLERPOS_NONE; uno::Sequence aParams = xFunc->getParameterInfos(); long nParamCount = aParams.getLength(); const reflection::ParamInfo* pParArr = aParams.getConstArray(); long nParamPos; for (nParamPos=0; nParamPos xParClass = pParArr[nParamPos].aType; ScAddInArgumentType eArgType = lcl_GetArgType( xParClass ); if ( eArgType == SC_ADDINARG_NONE ) bValid = sal_False; else if ( eArgType == SC_ADDINARG_CALLER ) nCallerPos = nParamPos; else ++nVisibleCount; } if (bValid) { ScAddInArgDesc* pVisibleArgs = NULL; if ( nVisibleCount > 0 ) { ScAddInArgDesc aDesc; pVisibleArgs = new ScAddInArgDesc[nVisibleCount]; long nDestPos = 0; for (nParamPos=0; nParamPos xParClass = pParArr[nParamPos].aType; ScAddInArgumentType eArgType = lcl_GetArgType( xParClass ); if ( eArgType != SC_ADDINARG_CALLER ) { const ScAddInArgDesc* pOldArgDesc = lcl_FindArgDesc( *pOldData, pParArr[nParamPos].aName ); if ( pOldArgDesc ) { aDesc.aName = pOldArgDesc->aName; aDesc.aDescription = pOldArgDesc->aDescription; } else aDesc.aName = aDesc.aDescription = String::CreateFromAscii( "###" ); sal_Bool bOptional = ( eArgType == SC_ADDINARG_VALUE_OR_ARRAY || eArgType == SC_ADDINARG_VARARGS ); aDesc.eType = eArgType; aDesc.bOptional = bOptional; //! initialize aInternalName only from config? aDesc.aInternalName = pParArr[nParamPos].aName; pVisibleArgs[nDestPos++] = aDesc; } } DBG_ASSERT( nDestPos==nVisibleCount, "wrong count" ); } pOldData->SetFunction( xFunc, aObject ); pOldData->SetArguments( nVisibleCount, pVisibleArgs ); pOldData->SetCallerPos( nCallerPos ); if ( pFunctionList ) lcl_UpdateFunctionList( *pFunctionList, *pOldData ); delete[] pVisibleArgs; } } } } } } } } String ScUnoAddInCollection::FindFunction( const String& rUpperName, sal_Bool bLocalFirst ) { if (!bInitialized) Initialize(); if (nFuncCount == 0) return EMPTY_STRING; if ( bLocalFirst ) { // first scan all local names (used for entering formulas) ScAddInHashMap::const_iterator iLook( pLocalHashMap->find( rUpperName ) ); if ( iLook != pLocalHashMap->end() ) return iLook->second->GetOriginalName(); #if 0 // after that, scan international names (really?) iLook = pNameHashMap->find( rUpperName ); if ( iLook != pNameHashMap->end() ) return iLook->second->GetOriginalName(); #endif } else { // first scan international names (used when calling a function) //! before that, check for exact match??? ScAddInHashMap::const_iterator iLook( pNameHashMap->find( rUpperName ) ); if ( iLook != pNameHashMap->end() ) return iLook->second->GetOriginalName(); // after that, scan all local names (to allow replacing old AddIns with Uno) iLook = pLocalHashMap->find( rUpperName ); if ( iLook != pLocalHashMap->end() ) return iLook->second->GetOriginalName(); } return EMPTY_STRING; } const ScUnoAddInFuncData* ScUnoAddInCollection::GetFuncData( const String& rName, bool bComplete ) { if (!bInitialized) Initialize(); // rName must be the exact internal name ScAddInHashMap::const_iterator iLook( pExactHashMap->find( rName ) ); if ( iLook != pExactHashMap->end() ) { const ScUnoAddInFuncData* pFuncData = iLook->second; if ( bComplete && !pFuncData->GetFunction().is() ) //! extra flag? LoadComponent( *pFuncData ); return pFuncData; } return NULL; } const ScUnoAddInFuncData* ScUnoAddInCollection::GetFuncData( long nIndex ) { if (!bInitialized) Initialize(); if (nIndex < nFuncCount) return ppFuncData[nIndex]; return NULL; } void ScUnoAddInCollection::LocalizeString( String& rName ) { if (!bInitialized) Initialize(); // modify rName - input: exact name ScAddInHashMap::const_iterator iLook( pExactHashMap->find( rName ) ); if ( iLook != pExactHashMap->end() ) rName = iLook->second->GetUpperLocal(); //! upper? } long ScUnoAddInCollection::GetFuncCount() { if (!bInitialized) Initialize(); return nFuncCount; } sal_Bool ScUnoAddInCollection::FillFunctionDesc( long nFunc, ScFuncDesc& rDesc ) { if (!bInitialized) Initialize(); if (nFunc >= nFuncCount || !ppFuncData[nFunc]) return sal_False; const ScUnoAddInFuncData& rFuncData = *ppFuncData[nFunc]; return FillFunctionDescFromData( rFuncData, rDesc ); } // static sal_Bool ScUnoAddInCollection::FillFunctionDescFromData( const ScUnoAddInFuncData& rFuncData, ScFuncDesc& rDesc ) { rDesc.Clear(); sal_Bool bIncomplete = !rFuncData.GetFunction().is(); //! extra flag? long nArgCount = rFuncData.GetArgumentCount(); if ( nArgCount > USHRT_MAX ) return sal_False; if ( bIncomplete ) nArgCount = 0; // if incomplete, fill without argument info (no wrong order) // nFIndex is set from outside rDesc.pFuncName = new String( rFuncData.GetUpperLocal() ); //! upper? rDesc.nCategory = rFuncData.GetCategory(); rDesc.sHelpId = rFuncData.GetHelpId(); String aDesc = rFuncData.GetDescription(); if (!aDesc.Len()) aDesc = rFuncData.GetLocalName(); // use name if no description is available rDesc.pFuncDesc = new String( aDesc ); // AddInArgumentType_CALLER is already left out in FuncData rDesc.nArgCount = (sal_uInt16)nArgCount; if ( nArgCount ) { sal_Bool bMultiple = sal_False; const ScAddInArgDesc* pArgs = rFuncData.GetArguments(); rDesc.ppDefArgNames = new String*[nArgCount]; rDesc.ppDefArgDescs = new String*[nArgCount]; rDesc.pDefArgFlags = new ScFuncDesc::ParameterFlags[nArgCount]; for ( long nArg=0; nArgLen() == 0 ) { String aDefName( RTL_CONSTASCII_USTRINGPARAM("arg") ); aDefName += String::CreateFromInt32( nArg+1 ); *rDesc.ppDefArgNames[nArg] = aDefName; } // last argument repeated? if ( nArg+1 == nArgCount && ( pArgs[nArg].eType == SC_ADDINARG_VARARGS ) ) bMultiple = sal_True; } if ( bMultiple ) rDesc.nArgCount += VAR_ARGS - 1; // VAR_ARGS means just one repeated arg } rDesc.bIncomplete = bIncomplete; return sal_True; } //------------------------------------------------------------------------ ScUnoAddInCall::ScUnoAddInCall( ScUnoAddInCollection& rColl, const String& rName, long nParamCount ) : bValidCount( sal_False ), nErrCode( errNoCode ), // before function was called bHasString( sal_True ), fValue( 0.0 ), xMatrix( NULL ) { pFuncData = rColl.GetFuncData( rName, true ); // need fully initialized data DBG_ASSERT( pFuncData, "Function Data missing" ); if ( pFuncData ) { long nDescCount = pFuncData->GetArgumentCount(); const ScAddInArgDesc* pArgs = pFuncData->GetArguments(); // is aVarArg sequence needed? if ( nParamCount >= nDescCount && nDescCount > 0 && pArgs[nDescCount-1].eType == SC_ADDINARG_VARARGS ) { long nVarCount = nParamCount - ( nDescCount - 1 ); // size of last argument aVarArg.realloc( nVarCount ); bValidCount = sal_True; } else if ( nParamCount <= nDescCount ) { // all args behind nParamCount must be optional bValidCount = sal_True; for (long i=nParamCount; iGetArgumentCount(); const ScAddInArgDesc* pArgs = pFuncData->GetArguments(); // if last arg is sequence, use "any" type if ( nCount > 0 && nPos >= nCount-1 && pArgs[nCount-1].eType == SC_ADDINARG_VARARGS ) return SC_ADDINARG_VALUE_OR_ARRAY; if ( nPos < nCount ) return pArgs[nPos].eType; } return SC_ADDINARG_VALUE_OR_ARRAY; //! error code !!!! } sal_Bool ScUnoAddInCall::NeedsCaller() const { return pFuncData && pFuncData->GetCallerPos() != SC_CALLERPOS_NONE; } void ScUnoAddInCall::SetCaller( const uno::Reference& rInterface ) { xCaller = rInterface; } void ScUnoAddInCall::SetCallerFromObjectShell( SfxObjectShell* pObjSh ) { if (pObjSh) { uno::Reference xInt( pObjSh->GetBaseModel(), uno::UNO_QUERY ); SetCaller( xInt ); } } void ScUnoAddInCall::SetParam( long nPos, const uno::Any& rValue ) { if ( pFuncData ) { long nCount = pFuncData->GetArgumentCount(); const ScAddInArgDesc* pArgs = pFuncData->GetArguments(); if ( nCount > 0 && nPos >= nCount-1 && pArgs[nCount-1].eType == SC_ADDINARG_VARARGS ) { long nVarPos = nPos-(nCount-1); if ( nVarPos < aVarArg.getLength() ) aVarArg.getArray()[nVarPos] = rValue; else { DBG_ERROR("wrong argument number"); } } else if ( nPos < aArgs.getLength() ) aArgs.getArray()[nPos] = rValue; else { DBG_ERROR("wrong argument number"); } } } void ScUnoAddInCall::ExecuteCall() { if ( !pFuncData ) return; long nCount = pFuncData->GetArgumentCount(); const ScAddInArgDesc* pArgs = pFuncData->GetArguments(); if ( nCount > 0 && pArgs[nCount-1].eType == SC_ADDINARG_VARARGS ) { // insert aVarArg as last argument //! after inserting caller (to prevent copying twice)? DBG_ASSERT( aArgs.getLength() == nCount, "wrong argument count" ); aArgs.getArray()[nCount-1] <<= aVarArg; } if ( pFuncData->GetCallerPos() != SC_CALLERPOS_NONE ) { uno::Any aCallerAny; aCallerAny <<= xCaller; long nUserLen = aArgs.getLength(); long nCallPos = pFuncData->GetCallerPos(); if (nCallPos>nUserLen) // should not happen { DBG_ERROR("wrong CallPos"); nCallPos = nUserLen; } long nDestLen = nUserLen + 1; uno::Sequence aRealArgs( nDestLen ); uno::Any* pDest = aRealArgs.getArray(); const uno::Any* pSource = aArgs.getConstArray(); long nSrcPos = 0; for ( long nDestPos = 0; nDestPos < nDestLen; nDestPos++ ) { if ( nDestPos == nCallPos ) pDest[nDestPos] = aCallerAny; else pDest[nDestPos] = pSource[nSrcPos++]; } ExecuteCallWithArgs( aRealArgs ); } else ExecuteCallWithArgs( aArgs ); } void ScUnoAddInCall::ExecuteCallWithArgs(uno::Sequence& rCallArgs) { // rCallArgs may not match argument descriptions (because of caller) uno::Reference xFunction; uno::Any aObject; if ( pFuncData ) { xFunction = pFuncData->GetFunction(); aObject = pFuncData->GetObject(); } if ( xFunction.is() ) { uno::Any aAny; nErrCode = 0; try { aAny = xFunction->invoke( aObject, rCallArgs ); } catch(lang::IllegalArgumentException&) { nErrCode = errIllegalArgument; } #if 0 catch(FloatingPointException&) { nErrCode = errIllegalFPOperation; } #endif catch(reflection::InvocationTargetException& rWrapped) { if ( rWrapped.TargetException.getValueType().equals( getCppuType( (lang::IllegalArgumentException*)0 ) ) ) nErrCode = errIllegalArgument; else if ( rWrapped.TargetException.getValueType().equals( getCppuType( (sheet::NoConvergenceException*)0 ) ) ) nErrCode = errNoConvergence; else nErrCode = errNoValue; } catch(uno::Exception&) { nErrCode = errNoValue; } if (!nErrCode) SetResult( aAny ); // convert result to Calc types } } void ScUnoAddInCall::SetResult( const uno::Any& rNewRes ) { nErrCode = 0; xVarRes = NULL; // Reflection* pRefl = rNewRes.getReflection(); uno::TypeClass eClass = rNewRes.getValueTypeClass(); uno::Type aType = rNewRes.getValueType(); switch (eClass) { case uno::TypeClass_VOID: nErrCode = NOTAVAILABLE; // #NA break; case uno::TypeClass_ENUM: case uno::TypeClass_BOOLEAN: case uno::TypeClass_CHAR: case uno::TypeClass_BYTE: case uno::TypeClass_SHORT: case uno::TypeClass_UNSIGNED_SHORT: case uno::TypeClass_LONG: case uno::TypeClass_UNSIGNED_LONG: case uno::TypeClass_FLOAT: case uno::TypeClass_DOUBLE: { uno::TypeClass eMyClass; ScApiTypeConversion::ConvertAnyToDouble( fValue, eMyClass, rNewRes); bHasString = sal_False; } break; case uno::TypeClass_STRING: { rtl::OUString aUStr; rNewRes >>= aUStr; aString = String( aUStr ); bHasString = sal_True; } break; case uno::TypeClass_INTERFACE: { //! directly extract XVolatileResult from any? uno::Reference xInterface; rNewRes >>= xInterface; if ( xInterface.is() ) xVarRes = uno::Reference( xInterface, uno::UNO_QUERY ); if (!xVarRes.is()) nErrCode = errNoValue; // unknown interface } break; default: if ( aType.equals( getCppuType( (uno::Sequence< uno::Sequence > *)0 ) ) ) { const uno::Sequence< uno::Sequence >* pRowSeq = NULL; //! use pointer from any! uno::Sequence< uno::Sequence > aSequence; if ( rNewRes >>= aSequence ) pRowSeq = &aSequence; if ( pRowSeq ) { long nRowCount = pRowSeq->getLength(); const uno::Sequence* pRowArr = pRowSeq->getConstArray(); long nMaxColCount = 0; long nCol, nRow; for (nRow=0; nRow nMaxColCount ) nMaxColCount = nTmp; } if ( nMaxColCount && nRowCount ) { xMatrix = new ScMatrix( static_cast(nMaxColCount), static_cast(nRowCount) ); ScMatrix* pMatrix = xMatrix; for (nRow=0; nRowPutDouble( pColArr[nCol], static_cast(nCol), static_cast(nRow) ); for (nCol=nColCount; nColPutDouble( 0.0, static_cast(nCol), static_cast(nRow) ); } } } } else if ( aType.equals( getCppuType( (uno::Sequence< uno::Sequence > *)0 ) ) ) { const uno::Sequence< uno::Sequence >* pRowSeq = NULL; //! use pointer from any! uno::Sequence< uno::Sequence > aSequence; if ( rNewRes >>= aSequence ) pRowSeq = &aSequence; if ( pRowSeq ) { long nRowCount = pRowSeq->getLength(); const uno::Sequence* pRowArr = pRowSeq->getConstArray(); long nMaxColCount = 0; long nCol, nRow; for (nRow=0; nRow nMaxColCount ) nMaxColCount = nTmp; } if ( nMaxColCount && nRowCount ) { xMatrix = new ScMatrix( static_cast(nMaxColCount), static_cast(nRowCount) ); ScMatrix* pMatrix = xMatrix; for (nRow=0; nRowPutDouble( pColArr[nCol], static_cast(nCol), static_cast(nRow) ); for (nCol=nColCount; nColPutDouble( 0.0, static_cast(nCol), static_cast(nRow) ); } } } } else if ( aType.equals( getCppuType( (uno::Sequence< uno::Sequence > *)0 ) ) ) { const uno::Sequence< uno::Sequence >* pRowSeq = NULL; //! use pointer from any! uno::Sequence< uno::Sequence > aSequence; if ( rNewRes >>= aSequence ) pRowSeq = &aSequence; if ( pRowSeq ) { long nRowCount = pRowSeq->getLength(); const uno::Sequence* pRowArr = pRowSeq->getConstArray(); long nMaxColCount = 0; long nCol, nRow; for (nRow=0; nRow nMaxColCount ) nMaxColCount = nTmp; } if ( nMaxColCount && nRowCount ) { xMatrix = new ScMatrix( static_cast(nMaxColCount), static_cast(nRowCount) ); ScMatrix* pMatrix = xMatrix; for (nRow=0; nRowPutString( String( pColArr[nCol] ), static_cast(nCol), static_cast(nRow) ); for (nCol=nColCount; nColPutString( EMPTY_STRING, static_cast(nCol), static_cast(nRow) ); } } } } else if ( aType.equals( getCppuType( (uno::Sequence< uno::Sequence > *)0 ) ) ) { xMatrix = ScSequenceToMatrix::CreateMixedMatrix( rNewRes ); } if (!xMatrix) // no array found nErrCode = errNoValue; //! code for error in return type??? } } //------------------------------------------------------------------------