/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 * 
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 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 <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( 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 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( 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 = 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(RTL_CONSTASCII_USTRINGPARAM("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(RTL_CONSTASCII_USTRINGPARAM(SCADDINSUPPLIER_SERVICE)) );
        if ( xEnum.is() )
        {
            //  loop through all AddIns
            while ( xEnum->hasMoreElements() )
            {
                uno::Any aAddInAny = xEnum->nextElement();

                {
                    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(RTL_CONSTASCII_USTRINGPARAM( 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(RTL_CONSTASCII_USTRINGPARAM( 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(RTL_CONSTASCII_USTRINGPARAM( CFGSTR_DISPLAYNAME) );
                pNameArray[CFG_FUNCPROP_DESCRIPTION] = aFuncPropPath;
                pNameArray[CFG_FUNCPROP_DESCRIPTION] += rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( CFGSTR_DESCRIPTION ));
                pNameArray[CFG_FUNCPROP_CATEGORY] = aFuncPropPath;
                pNameArray[CFG_FUNCPROP_CATEGORY] += rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( 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(RTL_CONSTASCII_USTRINGPARAM( 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(RTL_CONSTASCII_USTRINGPARAM( 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(RTL_CONSTASCII_USTRINGPARAM( CFGSTR_DISPLAYNAME ));
                        pPropNameArray[nIndex] = aOneArgPath;
                        pPropNameArray[nIndex++] += rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( 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 = 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 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 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 false;

    switch (xClass->getTypeClass())
    {
        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 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(RTL_CONSTASCII_USTRINGPARAM(
                                        "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 = false;
                                        uno::Reference<reflection::XIdlClass> xParClass =
                                                    pParArr[nParamPos].aType;
                                        ScAddInArgumentType eArgType = lcl_GetArgType( xParClass );
                                        if ( eArgType == SC_ADDINARG_NONE )
                                            bValid = false;
                                        else if ( eArgType == SC_ADDINARG_CALLER )
                                            nCallerPos = nParamPos;
                                        else
                                            ++nVisibleCount;
                                    }
                                    if (bValid)
                                    {
                                        sal_uInt16 nCategory = lcl_GetCategory(
                                            String(
                                            xAddIn->getProgrammaticCategoryName(
                                            aFuncU ) ) );

                                        rtl::OString sHelpId = aHelpIdGenerator.GetHelpId( aFuncU );

                                        rtl::OUString aLocalU;
                                        try
                                        {
                                            aLocalU = xAddIn->
                                                getDisplayFunctionName( aFuncU );
                                        }
                                        catch(uno::Exception&)
                                        {
                                            aLocalU = rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( "###" ));
                                        }
                                        String aLocalName = String( aLocalU );

                                        rtl::OUString aDescU;
                                        try
                                        {
                                            aDescU = xAddIn->
                                                getFunctionDescription( aFuncU );
                                        }
                                        catch(uno::Exception&)
                                        {
                                            aDescU = rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( "###" ));
                                        }
                                        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(RTL_CONSTASCII_USTRINGPARAM( "###" ));
                                                    }
                                                    rtl::OUString aArgDesc;
                                                    try
                                                    {
                                                        aArgDesc = xAddIn->
                                                            getArgumentDescription( aFuncU, nParamPos );
                                                    }
                                                    catch(uno::Exception&)
                                                    {
                                                        aArgName = rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( "###" ));
                                                    }

                                                    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 )
{
    ::rtl::OUString 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(RTL_CONSTASCII_USTRINGPARAM(
                                    "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 = false;
                                uno::Reference<reflection::XIdlClass> xParClass =
                                            pParArr[nParamPos].aType;
                                ScAddInArgumentType eArgType = lcl_GetArgType( xParClass );
                                if ( eArgType == SC_ADDINARG_NONE )
                                    bValid = false;
                                else if ( eArgType == SC_ADDINARG_CALLER )
                                    nCallerPos = nParamPos;
                                else
                                    ++nVisibleCount;
                            }
                            if (bValid)
                            {
                                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();
    }
    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 false;

    const ScUnoAddInFuncData& rFuncData = *ppFuncData[nFunc];

    return FillFunctionDescFromData( rFuncData, rDesc );
}

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 false;

    if ( bIncomplete )
        nArgCount = 0;      // if incomplete, fill without argument info (no wrong order)

    // nFIndex is set from outside

    rDesc.pFuncName = new ::rtl::OUString( 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 ::rtl::OUString( aDesc );

    // AddInArgumentType_CALLER is already left out in FuncData

    rDesc.nArgCount = (sal_uInt16)nArgCount;
    if ( nArgCount )
    {
        sal_Bool bMultiple = false;
        const ScAddInArgDesc* pArgs = rFuncData.GetArguments();

        rDesc.ppDefArgNames = new ::rtl::OUString*[nArgCount];
        rDesc.ppDefArgDescs = new ::rtl::OUString*[nArgCount];
        rDesc.pDefArgFlags   = new ScFuncDesc::ParameterFlags[nArgCount];
        for ( long nArg=0; nArg<nArgCount; nArg++ )
        {
            rDesc.ppDefArgNames[nArg] = new ::rtl::OUString( pArgs[nArg].aName );
            rDesc.ppDefArgDescs[nArg] = new ::rtl::OUString( pArgs[nArg].aDescription );
            rDesc.pDefArgFlags[nArg].bOptional = pArgs[nArg].bOptional;
            rDesc.pDefArgFlags[nArg].bSuppress = false;

            // no empty names...
            if ( rDesc.ppDefArgNames[nArg]->getLength() == 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( 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 = 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
            {
                OSL_FAIL("wrong argument number");
            }
        }
        else if ( nPos < aArgs.getLength() )
            aArgs.getArray()[nPos] = rValue;
        else
        {
            OSL_FAIL("wrong argument number");
        }
    }
}

void ScUnoAddInCall::ExecuteCall()
{
    if ( !pFuncData )
        return;

    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
        {
            OSL_FAIL("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;
        }

        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 = 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) );
                        for (nRow=0; nRow<nRowCount; nRow++)
                        {
                            long nColCount = pRowArr[nRow].getLength();
                            const sal_Int32* pColArr = pRowArr[nRow].getConstArray();
                            for (nCol=0; nCol<nColCount; nCol++)
                                xMatrix->PutDouble( pColArr[nCol],
                                        static_cast<SCSIZE>(nCol),
                                        static_cast<SCSIZE>(nRow) );
                            for (nCol=nColCount; nCol<nMaxColCount; nCol++)
                                xMatrix->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) );
                        for (nRow=0; nRow<nRowCount; nRow++)
                        {
                            long nColCount = pRowArr[nRow].getLength();
                            const double* pColArr = pRowArr[nRow].getConstArray();
                            for (nCol=0; nCol<nColCount; nCol++)
                                xMatrix->PutDouble( pColArr[nCol],
                                        static_cast<SCSIZE>(nCol),
                                        static_cast<SCSIZE>(nRow) );
                            for (nCol=nColCount; nCol<nMaxColCount; nCol++)
                                xMatrix->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) );
                        for (nRow=0; nRow<nRowCount; nRow++)
                        {
                            long nColCount = pRowArr[nRow].getLength();
                            const rtl::OUString* pColArr = pRowArr[nRow].getConstArray();
                            for (nCol=0; nCol<nColCount; nCol++)
                                xMatrix->PutString( String( pColArr[nCol] ),
                                    static_cast<SCSIZE>(nCol),
                                    static_cast<SCSIZE>(nRow) );
                            for (nCol=nColCount; nCol<nMaxColCount; nCol++)
                                xMatrix->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???
    }
}



//------------------------------------------------------------------------


/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
