| /************************************************************** |
| * |
| * 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 <comphelper/processfactory.hxx> |
| #include <tools/debug.hxx> |
| #include <i18npool/mslangid.hxx> |
| #include <vcl/svapp.hxx> |
| #include <vos/xception.hxx> |
| #include <sfx2/objsh.hxx> |
| #include <unotools/charclass.hxx> |
| |
| #include <com/sun/star/container/XContentEnumerationAccess.hpp> |
| #include <com/sun/star/lang/XServiceName.hpp> |
| #include <com/sun/star/lang/XSingleServiceFactory.hpp> |
| #include <com/sun/star/lang/XSingleComponentFactory.hpp> |
| #include <com/sun/star/reflection/XIdlClass.hpp> |
| #include <com/sun/star/reflection/XIdlClassProvider.hpp> |
| #include <com/sun/star/beans/XIntrospectionAccess.hpp> |
| #include <com/sun/star/beans/XIntrospection.hpp> |
| #include <com/sun/star/beans/MethodConcept.hpp> |
| #include <com/sun/star/beans/XPropertySet.hpp> |
| #include <com/sun/star/table/XCellRange.hpp> |
| #include <com/sun/star/lang/Locale.hpp> |
| #include <com/sun/star/sheet/XCompatibilityNames.hpp> |
| #include <com/sun/star/sheet/NoConvergenceException.hpp> |
| |
| #include "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<reflection::XIdlMethod>& 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; i<nArgCount; i++) |
| pArgDescs[i] = pAD[i]; |
| } |
| else |
| pArgDescs = NULL; |
| |
| ScGlobal::pCharClass->toUpper(aUpperName); |
| ScGlobal::pCharClass->toUpper(aUpperLocal); |
| } |
| |
| ScUnoAddInFuncData::~ScUnoAddInFuncData() |
| { |
| delete[] pArgDescs; |
| } |
| |
| const uno::Sequence<sheet::LocalizedName>& ScUnoAddInFuncData::GetCompNames() const |
| { |
| if ( !bCompInitialized ) |
| { |
| // read sequence of compatibility names on demand |
| |
| uno::Reference<sheet::XAddIn> xAddIn; |
| if ( aObject >>= xAddIn ) |
| { |
| uno::Reference<sheet::XCompatibilityNames> xComp( xAddIn, uno::UNO_QUERY ); |
| if ( xComp.is() && xFunction.is() ) |
| { |
| 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<nSeqLen; i++) |
| { |
| lang::Locale& rLocale = pArray[i].Locale; |
| rLocale.Language = rLocale.Language.toAsciiLowerCase(); |
| rLocale.Country = rLocale.Country.toAsciiUpperCase(); |
| } |
| } |
| } |
| } |
| |
| bCompInitialized = sal_True; // also if not successful |
| } |
| return aCompNames; |
| } |
| |
| void ScUnoAddInFuncData::SetCompNames( const uno::Sequence< sheet::LocalizedName>& 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<nSeqLen; i++) |
| { |
| lang::Locale& rLocale = pArray[i].Locale; |
| rLocale.Language = rLocale.Language.toAsciiLowerCase(); |
| rLocale.Country = rLocale.Country.toAsciiUpperCase(); |
| } |
| } |
| |
| bCompInitialized = sal_True; |
| } |
| |
| sal_Bool ScUnoAddInFuncData::GetExcelName( LanguageType eDestLang, String& rRetExcelName ) const |
| { |
| const uno::Sequence<sheet::LocalizedName>& 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<nSeqLen; i++) |
| if ( pArray[i].Locale.Language == aUserLang && |
| pArray[i].Locale.Country == aUserCountry ) |
| { |
| rRetExcelName = pArray[i].Name; |
| return sal_True; |
| } |
| |
| // second: check only language |
| |
| for ( i=0; i<nSeqLen; i++) |
| if ( pArray[i].Locale.Language == aUserLang ) |
| { |
| rRetExcelName = pArray[i].Name; |
| return sal_True; |
| } |
| |
| // third: #i57772# fall-back to en-US |
| |
| if ( eDestLang != LANGUAGE_ENGLISH_US ) |
| return GetExcelName( LANGUAGE_ENGLISH_US, rRetExcelName ); |
| |
| // forth: use first (default) entry |
| |
| rRetExcelName = pArray[0].Name; |
| return sal_True; |
| } |
| return sal_False; |
| } |
| |
| void ScUnoAddInFuncData::SetFunction( const uno::Reference< reflection::XIdlMethod>& 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<nArgCount; i++) |
| pArgDescs[i] = pNewDescs[i]; |
| } |
| else |
| pArgDescs = NULL; |
| } |
| |
| void ScUnoAddInFuncData::SetCallerPos( long nNewPos ) |
| { |
| nCallerPos = nNewPos; |
| } |
| |
| //------------------------------------------------------------------------ |
| |
| ScUnoAddInCollection::ScUnoAddInCollection() : |
| nFuncCount( 0 ), |
| ppFuncData( NULL ), |
| pExactHashMap( NULL ), |
| pNameHashMap( NULL ), |
| pLocalHashMap( NULL ), |
| bInitialized( sal_False ) |
| { |
| } |
| |
| ScUnoAddInCollection::~ScUnoAddInCollection() |
| { |
| Clear(); |
| } |
| |
| void ScUnoAddInCollection::Clear() |
| { |
| DELETEZ( pExactHashMap ); |
| DELETEZ( pNameHashMap ); |
| DELETEZ( pLocalHashMap ); |
| if ( ppFuncData ) |
| { |
| for ( long i=0; i<nFuncCount; i++ ) |
| delete ppFuncData[i]; |
| delete[] ppFuncData; |
| } |
| ppFuncData = NULL; |
| nFuncCount = 0; |
| |
| bInitialized = sal_False; |
| } |
| |
| uno::Reference<uno::XComponentContext> getContext(uno::Reference<lang::XMultiServiceFactory> xMSF) |
| { |
| uno::Reference<uno::XComponentContext> xCtx; |
| try { |
| uno::Reference<beans::XPropertySet> 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<lang::XMultiServiceFactory> xManager = comphelper::getProcessServiceFactory(); |
| uno::Reference<container::XContentEnumerationAccess> xEnAc( xManager, uno::UNO_QUERY ); |
| if ( xEnAc.is() ) |
| { |
| uno::Reference<container::XEnumeration> 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<uno::XInterface> xIntFac; |
| aAddInAny >>= xIntFac; |
| if ( xIntFac.is() ) |
| { |
| // #i59984# try XSingleComponentFactory in addition to (old) XSingleServiceFactory, |
| // passing the context to the component |
| |
| uno::Reference<uno::XInterface> xInterface; |
| uno::Reference<uno::XComponentContext> xCtx = getContext(xManager); |
| uno::Reference<lang::XSingleComponentFactory> xCFac( xIntFac, uno::UNO_QUERY ); |
| if (xCtx.is() && xCFac.is()) |
| { |
| xInterface = xCFac->createInstanceWithContext(xCtx); |
| if (xInterface.is()) |
| ReadFromAddIn( xInterface ); |
| } |
| |
| if (!xInterface.is()) |
| { |
| uno::Reference<lang::XSingleServiceFactory> xFac( xIntFac, uno::UNO_QUERY ); |
| if ( xFac.is() ) |
| { |
| xInterface = xFac->createInstance(); |
| if (xInterface.is()) |
| ReadFromAddIn( xInterface ); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| // 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; i<SC_FUNCGROUP_COUNT; i++) |
| if ( rName.EqualsAscii( aFuncNames[i] ) ) |
| return i+1; // IDs start at 1 |
| |
| return ID_FUNCTION_GRP_ADDINS; // if not found, use Add-In group |
| } |
| |
| |
| #define CFGPATH_ADDINS "Office.CalcAddIns/AddInInfo" |
| #define CFGSTR_ADDINFUNCTIONS "AddInFunctions" |
| |
| #define CFG_FUNCPROP_DISPLAYNAME 0 |
| #define CFG_FUNCPROP_DESCRIPTION 1 |
| #define CFG_FUNCPROP_CATEGORY 2 |
| #define CFG_FUNCPROP_COUNT 3 |
| #define CFGSTR_DISPLAYNAME "DisplayName" |
| #define CFGSTR_DESCRIPTION "Description" |
| #define CFGSTR_CATEGORY "Category" |
| // CategoryDisplayName is ignored for now |
| |
| #define CFGSTR_COMPATIBILITYNAME "CompatibilityName" |
| #define CFGSTR_PARAMETERS "Parameters" |
| |
| |
| void ScUnoAddInCollection::ReadConfiguration() |
| { |
| // called only from Initialize |
| |
| ScAddInCfg& rAddInConfig = SC_MOD()->GetAddInCfg(); |
| |
| // 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<rtl::OUString> 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<rtl::OUString> 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; i<nOld; i++) |
| ppNew[i] = ppFuncData[i]; |
| delete[] ppFuncData; |
| ppFuncData = ppNew; |
| } |
| else |
| ppFuncData = new ScUnoAddInFuncData*[nFuncCount]; |
| |
| //! TODO: adjust bucket count? |
| if ( !pExactHashMap ) |
| pExactHashMap = new ScAddInHashMap; |
| if ( !pNameHashMap ) |
| pNameHashMap = new ScAddInHashMap; |
| if ( !pLocalHashMap ) |
| pLocalHashMap = new ScAddInHashMap; |
| |
| //! get the function information in a single call for all functions? |
| |
| const rtl::OUString* pFuncNameArray = aFunctionNames.getConstArray(); |
| for ( sal_Int32 nFuncPos = 0; nFuncPos < nNewCount; nFuncPos++ ) |
| { |
| ppFuncData[nFuncPos+nOld] = NULL; |
| |
| // stored function name: (service name).(function) |
| String aFuncName( aServiceName ); |
| aFuncName += '.'; |
| aFuncName += String( pFuncNameArray[nFuncPos] ); |
| |
| // skip the function if already known (read from old AddIn service) |
| |
| if ( pExactHashMap->find( 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<rtl::OUString> 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<uno::Any> 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<sheet::LocalizedName> aCompNames; |
| |
| rtl::OUString aCompPath = aFuncPropPath; |
| aCompPath += rtl::OUString::createFromAscii( CFGSTR_COMPATIBILITYNAME ); |
| uno::Sequence<rtl::OUString> aCompPropNames( &aCompPath, 1 ); |
| |
| uno::Sequence<uno::Any> aCompProperties = aAllLocalesConfig.GetProperties( aCompPropNames ); |
| if ( aCompProperties.getLength() == 1 ) |
| { |
| uno::Sequence<beans::PropertyValue> aLocalEntries; |
| if ( aCompProperties[0] >>= aLocalEntries ) |
| { |
| 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<rtl::OUString> aArgumentNames = rAddInConfig.GetNodeNames( aArgumentsPath ); |
| sal_Int32 nArgumentCount = aArgumentNames.getLength(); |
| if ( nArgumentCount ) |
| { |
| // get DisplayName and Description for each argument |
| uno::Sequence<rtl::OUString> 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<uno::Any> 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<reflection::XIdlMethod> xFunc; // remains empty |
| uno::Any aObject; // also empty |
| |
| // create and insert into the array |
| |
| ScUnoAddInFuncData* pData = new ScUnoAddInFuncData( |
| aFuncName, aLocalName, aDescription, |
| nCategory, sHelpId, |
| xFunc, aObject, |
| nVisibleCount, pVisibleArgs, 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<lang::XMultiServiceFactory> xServiceFactory = comphelper::getProcessServiceFactory(); |
| uno::Reference<uno::XInterface> 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<nFuncCount; i++) |
| { |
| ScUnoAddInFuncData* pFuncData = ppFuncData[i]; |
| if ( pFuncData ) |
| { |
| const uno::Sequence<sheet::LocalizedName>& rSequence = pFuncData->GetCompNames(); |
| long nSeqLen = rSequence.getLength(); |
| if ( nSeqLen ) |
| { |
| const sheet::LocalizedName* pArray = rSequence.getConstArray(); |
| for ( long nName=0; nName<nSeqLen; nName++) |
| if ( ScGlobal::pCharClass->upper( 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<reflection::XIdlClass>& 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<sheet::XVolatileResult>*)0) ) || |
| IsTypeName( sName, getCppuType((uno::Reference<uno::XInterface>*)0) ) ); |
| } |
| |
| default: |
| { |
| // nested sequences for arrays |
| //! XIdlClass needs getType() method! |
| |
| rtl::OUString sName = xClass->getName(); |
| return ( |
| IsTypeName( sName, getCppuType((uno::Sequence< uno::Sequence<sal_Int32> >*)0) ) || |
| IsTypeName( sName, getCppuType((uno::Sequence< uno::Sequence<double> >*)0) ) || |
| IsTypeName( sName, getCppuType((uno::Sequence< uno::Sequence<rtl::OUString> >*)0) ) || |
| IsTypeName( sName, getCppuType((uno::Sequence< uno::Sequence<uno::Any> >*)0) ) ); |
| } |
| } |
| return sal_False; |
| } |
| |
| ScAddInArgumentType lcl_GetArgType( const uno::Reference<reflection::XIdlClass>& xClass ) |
| { |
| if (!xClass.is()) |
| return SC_ADDINARG_NONE; |
| |
| uno::TypeClass eType = xClass->getTypeClass(); |
| |
| if ( eType == uno::TypeClass_LONG ) //! 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<sal_Int32> >*)0) )) |
| return SC_ADDINARG_INTEGER_ARRAY; |
| |
| if (IsTypeName( sName, getCppuType((uno::Sequence< uno::Sequence<double> >*)0) )) |
| return SC_ADDINARG_DOUBLE_ARRAY; |
| |
| if (IsTypeName( sName, getCppuType((uno::Sequence< uno::Sequence<rtl::OUString> >*)0) )) |
| return SC_ADDINARG_STRING_ARRAY; |
| |
| if (IsTypeName( sName, getCppuType((uno::Sequence< uno::Sequence<uno::Any> >*)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<table::XCellRange>*)0) )) |
| return SC_ADDINARG_CELLRANGE; |
| |
| if (IsTypeName( sName, getCppuType((uno::Reference<beans::XPropertySet>*)0) )) |
| return SC_ADDINARG_CALLER; |
| |
| if (IsTypeName( sName, getCppuType((uno::Sequence<uno::Any>*)0) )) |
| return SC_ADDINARG_VARARGS; |
| |
| return SC_ADDINARG_NONE; |
| } |
| |
| void ScUnoAddInCollection::ReadFromAddIn( const uno::Reference<uno::XInterface>& xInterface ) |
| { |
| uno::Reference<sheet::XAddIn> xAddIn( xInterface, uno::UNO_QUERY ); |
| uno::Reference<lang::XServiceName> xName( xInterface, uno::UNO_QUERY ); |
| if ( xAddIn.is() && xName.is() ) |
| { |
| // 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<lang::XMultiServiceFactory> xManager = comphelper::getProcessServiceFactory(); |
| if ( xManager.is() ) |
| { |
| uno::Reference<beans::XIntrospection> xIntro( |
| xManager->createInstance(rtl::OUString::createFromAscii( |
| "com.sun.star.beans.Introspection" )), |
| uno::UNO_QUERY ); |
| if ( xIntro.is() ) |
| { |
| uno::Any aObject; |
| aObject <<= xAddIn; |
| uno::Reference<beans::XIntrospectionAccess> xAcc = xIntro->inspect(aObject); |
| if (xAcc.is()) |
| { |
| uno::Sequence< uno::Reference<reflection::XIdlMethod> > 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<nOld; i++) |
| ppNew[i] = ppFuncData[i]; |
| delete[] ppFuncData; |
| ppFuncData = ppNew; |
| } |
| else |
| ppFuncData = new ScUnoAddInFuncData*[nFuncCount]; |
| |
| //! TODO: adjust bucket count? |
| if ( !pExactHashMap ) |
| pExactHashMap = new ScAddInHashMap; |
| if ( !pNameHashMap ) |
| pNameHashMap = new ScAddInHashMap; |
| if ( !pLocalHashMap ) |
| pLocalHashMap = new ScAddInHashMap; |
| |
| const uno::Reference<reflection::XIdlMethod>* pArray = aMethods.getConstArray(); |
| for (long nFuncPos=0; nFuncPos<nNewCount; nFuncPos++) |
| { |
| ppFuncData[nFuncPos+nOld] = NULL; |
| |
| uno::Reference<reflection::XIdlMethod> xFunc = pArray[nFuncPos]; |
| if (xFunc.is()) |
| { |
| // leave out internal functions |
| uno::Reference<reflection::XIdlClass> 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<uno::XInterface>*)0) ) || |
| IsTypeName( sName, |
| getCppuType((uno::Reference<reflection::XIdlClassProvider>*)0) ) || |
| IsTypeName( sName, |
| getCppuType((uno::Reference<lang::XServiceName>*)0) ) || |
| IsTypeName( sName, |
| getCppuType((uno::Reference<lang::XServiceInfo>*)0) ) || |
| IsTypeName( sName, |
| getCppuType((uno::Reference<sheet::XAddIn>*)0) ) ); |
| } |
| if (!bSkip) |
| { |
| uno::Reference<reflection::XIdlClass> 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<reflection::ParamInfo> aParams = |
| xFunc->getParameterInfos(); |
| long nParamCount = aParams.getLength(); |
| const reflection::ParamInfo* pParArr = aParams.getConstArray(); |
| long nParamPos; |
| for (nParamPos=0; nParamPos<nParamCount; nParamPos++) |
| { |
| if ( pParArr[nParamPos].aMode != reflection::ParamMode_IN ) |
| bValid = sal_False; |
| uno::Reference<reflection::XIdlClass> 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<nParamCount; nParamPos++) |
| { |
| uno::Reference<reflection::XIdlClass> 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; nPos<nCount; nPos++) |
| { |
| const ScFuncDesc* pDesc = rFunctionList.GetFunction( nPos ); |
| if ( pDesc && pDesc->pFuncName && *pDesc->pFuncName == aCompare ) |
| { |
| ScUnoAddInCollection::FillFunctionDescFromData( rFuncData, *const_cast<ScFuncDesc*>(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<nArgCount; nPos++) |
| { |
| if ( pArguments[nPos].aInternalName == rArgIntName ) |
| return &pArguments[nPos]; |
| } |
| return NULL; |
| } |
| |
| void ScUnoAddInCollection::UpdateFromAddIn( const uno::Reference<uno::XInterface>& xInterface, |
| const String& rServiceName ) |
| { |
| uno::Reference<lang::XLocalizable> 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<lang::XMultiServiceFactory> xManager = comphelper::getProcessServiceFactory(); |
| if ( xManager.is() ) |
| { |
| uno::Reference<beans::XIntrospection> xIntro( |
| xManager->createInstance(rtl::OUString::createFromAscii( |
| "com.sun.star.beans.Introspection" )), |
| uno::UNO_QUERY ); |
| if ( xIntro.is() ) |
| { |
| uno::Any aObject; |
| aObject <<= xInterface; |
| uno::Reference<beans::XIntrospectionAccess> xAcc = xIntro->inspect(aObject); |
| if (xAcc.is()) |
| { |
| uno::Sequence< uno::Reference<reflection::XIdlMethod> > aMethods = |
| xAcc->getMethods( beans::MethodConcept::ALL ); |
| long nMethodCount = aMethods.getLength(); |
| const uno::Reference<reflection::XIdlMethod>* pArray = aMethods.getConstArray(); |
| for (long nFuncPos=0; nFuncPos<nMethodCount; nFuncPos++) |
| { |
| uno::Reference<reflection::XIdlMethod> 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<ScUnoAddInFuncData*>( GetFuncData( aFuncName ) ); |
| if ( pOldData ) |
| { |
| // Create new (complete) argument info. |
| // As in ReadFromAddIn, the reflection information is authoritative. |
| // Local names and descriptions from pOldData are looked up using the |
| // internal argument name. |
| |
| sal_Bool bValid = sal_True; |
| long nVisibleCount = 0; |
| long nCallerPos = SC_CALLERPOS_NONE; |
| |
| uno::Sequence<reflection::ParamInfo> aParams = |
| xFunc->getParameterInfos(); |
| long nParamCount = aParams.getLength(); |
| const reflection::ParamInfo* pParArr = aParams.getConstArray(); |
| long nParamPos; |
| for (nParamPos=0; nParamPos<nParamCount; nParamPos++) |
| { |
| if ( pParArr[nParamPos].aMode != reflection::ParamMode_IN ) |
| bValid = sal_False; |
| uno::Reference<reflection::XIdlClass> 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<nParamCount; nParamPos++) |
| { |
| uno::Reference<reflection::XIdlClass> 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; nArg<nArgCount; nArg++ ) |
| { |
| rDesc.ppDefArgNames[nArg] = new String( pArgs[nArg].aName ); |
| rDesc.ppDefArgDescs[nArg] = new String( pArgs[nArg].aDescription ); |
| rDesc.pDefArgFlags[nArg].bOptional = pArgs[nArg].bOptional; |
| rDesc.pDefArgFlags[nArg].bSuppress = false; |
| |
| // no empty names... |
| if ( rDesc.ppDefArgNames[nArg]->Len() == 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; i<nDescCount; i++) |
| if ( !pArgs[i].bOptional ) |
| bValidCount = sal_False; |
| } |
| // else invalid (too many arguments) |
| |
| if ( bValidCount ) |
| aArgs.realloc( nDescCount ); // sequence must always match function signature |
| } |
| } |
| |
| ScUnoAddInCall::~ScUnoAddInCall() |
| { |
| // pFuncData is deleted with ScUnoAddInCollection |
| } |
| |
| sal_Bool ScUnoAddInCall::ValidParamCount() |
| { |
| return bValidCount; |
| } |
| |
| ScAddInArgumentType ScUnoAddInCall::GetArgType( long nPos ) |
| { |
| if ( pFuncData ) |
| { |
| long nCount = pFuncData->GetArgumentCount(); |
| const ScAddInArgDesc* pArgs = pFuncData->GetArguments(); |
| |
| // if last arg is sequence, use "any" type |
| if ( nCount > 0 && nPos >= nCount-1 && pArgs[nCount-1].eType == SC_ADDINARG_VARARGS ) |
| return SC_ADDINARG_VALUE_OR_ARRAY; |
| |
| if ( nPos < nCount ) |
| return pArgs[nPos].eType; |
| } |
| return SC_ADDINARG_VALUE_OR_ARRAY; //! error code !!!! |
| } |
| |
| sal_Bool ScUnoAddInCall::NeedsCaller() const |
| { |
| return pFuncData && pFuncData->GetCallerPos() != SC_CALLERPOS_NONE; |
| } |
| |
| void ScUnoAddInCall::SetCaller( const uno::Reference<uno::XInterface>& rInterface ) |
| { |
| xCaller = rInterface; |
| } |
| |
| void ScUnoAddInCall::SetCallerFromObjectShell( SfxObjectShell* pObjSh ) |
| { |
| if (pObjSh) |
| { |
| uno::Reference<uno::XInterface> 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<uno::Any> 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<uno::Any>& rCallArgs) |
| { |
| // rCallArgs may not match argument descriptions (because of caller) |
| |
| uno::Reference<reflection::XIdlMethod> xFunction; |
| uno::Any aObject; |
| if ( pFuncData ) |
| { |
| xFunction = pFuncData->GetFunction(); |
| aObject = pFuncData->GetObject(); |
| } |
| |
| if ( xFunction.is() ) |
| { |
| 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<uno::XInterface> xInterface; |
| rNewRes >>= xInterface; |
| if ( xInterface.is() ) |
| xVarRes = uno::Reference<sheet::XVolatileResult>( xInterface, uno::UNO_QUERY ); |
| |
| if (!xVarRes.is()) |
| nErrCode = errNoValue; // unknown interface |
| } |
| break; |
| |
| default: |
| if ( aType.equals( getCppuType( (uno::Sequence< uno::Sequence<sal_Int32> > *)0 ) ) ) |
| { |
| const uno::Sequence< uno::Sequence<sal_Int32> >* pRowSeq = NULL; |
| |
| //! use pointer from any! |
| uno::Sequence< uno::Sequence<sal_Int32> > aSequence; |
| if ( rNewRes >>= aSequence ) |
| pRowSeq = &aSequence; |
| |
| if ( pRowSeq ) |
| { |
| long nRowCount = pRowSeq->getLength(); |
| const uno::Sequence<sal_Int32>* pRowArr = pRowSeq->getConstArray(); |
| long nMaxColCount = 0; |
| long nCol, nRow; |
| for (nRow=0; nRow<nRowCount; nRow++) |
| { |
| long nTmp = pRowArr[nRow].getLength(); |
| if ( nTmp > nMaxColCount ) |
| nMaxColCount = nTmp; |
| } |
| if ( nMaxColCount && nRowCount ) |
| { |
| xMatrix = new ScMatrix( |
| static_cast<SCSIZE>(nMaxColCount), |
| static_cast<SCSIZE>(nRowCount) ); |
| ScMatrix* pMatrix = xMatrix; |
| for (nRow=0; nRow<nRowCount; nRow++) |
| { |
| long nColCount = pRowArr[nRow].getLength(); |
| const sal_Int32* pColArr = pRowArr[nRow].getConstArray(); |
| for (nCol=0; nCol<nColCount; nCol++) |
| pMatrix->PutDouble( pColArr[nCol], |
| static_cast<SCSIZE>(nCol), |
| static_cast<SCSIZE>(nRow) ); |
| for (nCol=nColCount; nCol<nMaxColCount; nCol++) |
| pMatrix->PutDouble( 0.0, |
| static_cast<SCSIZE>(nCol), |
| static_cast<SCSIZE>(nRow) ); |
| } |
| } |
| } |
| } |
| else if ( aType.equals( getCppuType( (uno::Sequence< uno::Sequence<double> > *)0 ) ) ) |
| { |
| const uno::Sequence< uno::Sequence<double> >* pRowSeq = NULL; |
| |
| //! use pointer from any! |
| uno::Sequence< uno::Sequence<double> > aSequence; |
| if ( rNewRes >>= aSequence ) |
| pRowSeq = &aSequence; |
| |
| if ( pRowSeq ) |
| { |
| long nRowCount = pRowSeq->getLength(); |
| const uno::Sequence<double>* pRowArr = pRowSeq->getConstArray(); |
| long nMaxColCount = 0; |
| long nCol, nRow; |
| for (nRow=0; nRow<nRowCount; nRow++) |
| { |
| long nTmp = pRowArr[nRow].getLength(); |
| if ( nTmp > nMaxColCount ) |
| nMaxColCount = nTmp; |
| } |
| if ( nMaxColCount && nRowCount ) |
| { |
| xMatrix = new ScMatrix( |
| static_cast<SCSIZE>(nMaxColCount), |
| static_cast<SCSIZE>(nRowCount) ); |
| ScMatrix* pMatrix = xMatrix; |
| for (nRow=0; nRow<nRowCount; nRow++) |
| { |
| long nColCount = pRowArr[nRow].getLength(); |
| const double* pColArr = pRowArr[nRow].getConstArray(); |
| for (nCol=0; nCol<nColCount; nCol++) |
| pMatrix->PutDouble( pColArr[nCol], |
| static_cast<SCSIZE>(nCol), |
| static_cast<SCSIZE>(nRow) ); |
| for (nCol=nColCount; nCol<nMaxColCount; nCol++) |
| pMatrix->PutDouble( 0.0, |
| static_cast<SCSIZE>(nCol), |
| static_cast<SCSIZE>(nRow) ); |
| } |
| } |
| } |
| } |
| else if ( aType.equals( getCppuType( (uno::Sequence< uno::Sequence<rtl::OUString> > *)0 ) ) ) |
| { |
| const uno::Sequence< uno::Sequence<rtl::OUString> >* pRowSeq = NULL; |
| |
| //! use pointer from any! |
| uno::Sequence< uno::Sequence<rtl::OUString> > aSequence; |
| if ( rNewRes >>= aSequence ) |
| pRowSeq = &aSequence; |
| |
| if ( pRowSeq ) |
| { |
| long nRowCount = pRowSeq->getLength(); |
| const uno::Sequence<rtl::OUString>* pRowArr = pRowSeq->getConstArray(); |
| long nMaxColCount = 0; |
| long nCol, nRow; |
| for (nRow=0; nRow<nRowCount; nRow++) |
| { |
| long nTmp = pRowArr[nRow].getLength(); |
| if ( nTmp > nMaxColCount ) |
| nMaxColCount = nTmp; |
| } |
| if ( nMaxColCount && nRowCount ) |
| { |
| xMatrix = new ScMatrix( |
| static_cast<SCSIZE>(nMaxColCount), |
| static_cast<SCSIZE>(nRowCount) ); |
| ScMatrix* pMatrix = xMatrix; |
| for (nRow=0; nRow<nRowCount; nRow++) |
| { |
| long nColCount = pRowArr[nRow].getLength(); |
| const rtl::OUString* pColArr = pRowArr[nRow].getConstArray(); |
| for (nCol=0; nCol<nColCount; nCol++) |
| pMatrix->PutString( String( pColArr[nCol] ), |
| static_cast<SCSIZE>(nCol), |
| static_cast<SCSIZE>(nRow) ); |
| for (nCol=nColCount; nCol<nMaxColCount; nCol++) |
| pMatrix->PutString( EMPTY_STRING, |
| static_cast<SCSIZE>(nCol), |
| static_cast<SCSIZE>(nRow) ); |
| } |
| } |
| } |
| } |
| else if ( aType.equals( getCppuType( (uno::Sequence< uno::Sequence<uno::Any> > *)0 ) ) ) |
| { |
| xMatrix = ScSequenceToMatrix::CreateMixedMatrix( rNewRes ); |
| } |
| |
| if (!xMatrix) // no array found |
| nErrCode = errNoValue; //! code for error in return type??? |
| } |
| } |
| |
| |
| |
| //------------------------------------------------------------------------ |
| |
| |
| |