Resolves: tdf#150203 Gather AddIn English names and provide for FunctionAccess

 This is a combination of 4 commits.

Use nNumOfLoc instead of sizeof(pLang)

... which is count*sizeof(char*), but luckily this private
function was never used with out-of-bounds values.

xChange-Id: Ief3b3de614ca0df00c424f7caabf70e029ea7266
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/137703
Reviewed-by: Eike Rathke <erack@redhat.com>
Tested-by: Jenkins
(cherry picked from commit dac843c4dc0407430d5a7441fd80940a99a4d8f8)

Related: tdf#150203 Pricing, there is only one compatibility name, en-US

... but that was associated with de-DE instead.

xChange-Id: I9024666b5d33bbabbdb514075fe0598d072dacd5
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/137706
Reviewed-by: Eike Rathke <erack@redhat.com>
Tested-by: Jenkins
(cherry picked from commit 8690d8878248320b0b706e9d6f7b8fa89ed903c4)

Resolves: tdf#150203 Gather AddIn English names and provide for FunctionAccess

This works at least for the bundled AddIns from scaddins/. It may
for others if en-US compatibility function names are provided, if
not the local name will be continued to be used.

xChange-Id: I09e23f731c0f3d9753ef355ab59c2005bc567464
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/137708
Reviewed-by: Eike Rathke <erack@redhat.com>
Tested-by: Jenkins
(cherry picked from commit 8dc76b13458fac1a30787f8bbab117a4a9508ab5)

Avoid multiple conversions to LanguageTag, specifically in loops

xChange-Id: I47a969d7476df32e0c9d525d416467c59358d9ce
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/137712
Reviewed-by: Eike Rathke <erack@redhat.com>
Tested-by: Jenkins
(cherry picked from commit 86b2bfd34a4f07c54f03c8c8dfe48e0810834628)

Change-Id: I09e23f731c0f3d9753ef355ab59c2005bc567464
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/137713
Tested-by: Jenkins
Reviewed-by: Caolán McNamara <caolanm@redhat.com>
diff --git a/sc/inc/addincol.hxx b/sc/inc/addincol.hxx
index 9366e31..14af120 100644
--- a/sc/inc/addincol.hxx
+++ b/sc/inc/addincol.hxx
@@ -42,6 +42,7 @@ namespace com::sun::star::uno { class XInterface; }
class SfxObjectShell;
class ScUnoAddInFuncData;
class ScFuncDesc;
class LanguageTag;

typedef std::unordered_map< OUString, const ScUnoAddInFuncData* > ScAddInHashMap;

@@ -123,7 +124,8 @@ public:
    const OString&          GetHelpId() const           { return sHelpId; }

    const ::std::vector< LocalizedName >&  GetCompNames() const;
    bool                    GetExcelName( LanguageType eDestLang, OUString& rRetExcelName ) const;
    bool                    GetExcelName( const LanguageTag& rDestLang, OUString& rRetExcelName,
                                          bool bFallbackToAny = true ) const;

    void    SetFunction( const css::uno::Reference< css::reflection::XIdlMethod>& rNewFunc,
                         const css::uno::Any& rNewObj );
@@ -140,6 +142,7 @@ private:
    std::unique_ptr<ScAddInHashMap>       pExactHashMap;      ///< exact internal name
    std::unique_ptr<ScAddInHashMap>       pNameHashMap;       ///< internal name upper
    std::unique_ptr<ScAddInHashMap>       pLocalHashMap;      ///< localized name upper
    std::unique_ptr<ScAddInHashMap>       pEnglishHashMap;    ///< English name upper
    bool                    bInitialized;

    void        Initialize();
diff --git a/sc/source/core/tool/addincol.cxx b/sc/source/core/tool/addincol.cxx
index addb151..ea21842 100644
--- a/sc/source/core/tool/addincol.cxx
+++ b/sc/source/core/tool/addincol.cxx
@@ -138,13 +138,12 @@ void ScUnoAddInFuncData::SetCompNames( ::std::vector< ScUnoAddInFuncData::Locali
    bCompInitialized = true;
}

bool ScUnoAddInFuncData::GetExcelName( LanguageType eDestLang, OUString& rRetExcelName ) const
bool ScUnoAddInFuncData::GetExcelName( const LanguageTag& rDestLang, OUString& rRetExcelName, bool bFallbackToAny ) const
{
    const ::std::vector<LocalizedName>& rCompNames = GetCompNames();
    if ( !rCompNames.empty() )
    {
        LanguageTag aLanguageTag( eDestLang);
        const OUString& aSearch( aLanguageTag.getBcp47());
        const OUString& aSearch( rDestLang.getBcp47());

        // First, check exact match without fallback overhead.
        ::std::vector<LocalizedName>::const_iterator itNames = std::find_if(rCompNames.begin(), rCompNames.end(),
@@ -157,7 +156,7 @@ bool ScUnoAddInFuncData::GetExcelName( LanguageType eDestLang, OUString& rRetExc

        // Second, try match of fallback search with fallback locales,
        // appending also 'en-US' and 'en' to search if not queried.
        ::std::vector< OUString > aFallbackSearch( aLanguageTag.getFallbackStrings( true));
        ::std::vector< OUString > aFallbackSearch( rDestLang.getFallbackStrings( true));
        if (aSearch != "en-US")
        {
            aFallbackSearch.emplace_back("en-US");
@@ -180,9 +179,12 @@ bool ScUnoAddInFuncData::GetExcelName( LanguageType eDestLang, OUString& rRetExc
            }
        }

        // Third, last resort, use first (default) entry.
        rRetExcelName = rCompNames[0].maName;
        return true;
        if (bFallbackToAny)
        {
            // Third, last resort, use first (default) entry.
            rRetExcelName = rCompNames[0].maName;
            return true;
        }
    }
    return false;
}
@@ -226,6 +228,7 @@ void ScUnoAddInCollection::Clear()
    pExactHashMap.reset();
    pNameHashMap.reset();
    pLocalHashMap.reset();
    pEnglishHashMap.reset();
    ppFuncData.reset();
    nFuncCount = 0;

@@ -380,6 +383,8 @@ void ScUnoAddInCollection::ReadConfiguration()
            pNameHashMap.reset( new ScAddInHashMap );
        if ( !pLocalHashMap )
            pLocalHashMap.reset( new ScAddInHashMap );
        if ( !pEnglishHashMap )
            pEnglishHashMap.reset( new ScAddInHashMap );

        //TODO: get the function information in a single call for all functions?

@@ -395,6 +400,7 @@ void ScUnoAddInCollection::ReadConfiguration()

            if ( pExactHashMap->find( aFuncName ) == pExactHashMap->end() )
            {
                OUString aEnglishName;
                OUString aLocalName;
                OUString aDescription;
                sal_uInt16 nCategory = ID_FUNCTION_GRP_ADDINS;
@@ -441,6 +447,11 @@ void ScUnoAddInCollection::ReadConfiguration()
                            OUString aName;
                            rConfig.Value >>= aName;
                            aCompNames.emplace_back( aLocale, aName);
                            // Accept 'en' and 'en-...' but prefer 'en-US'.
                            if (aLocale == "en-US")
                                aEnglishName = aName;
                            else if (aEnglishName.isEmpty() && (aLocale == "en" || aLocale.startsWith("en-")))
                                aEnglishName = aName;
                        }
                    }
                }
@@ -527,6 +538,15 @@ void ScUnoAddInCollection::ReadConfiguration()
                pLocalHashMap->emplace(
                            pData->GetUpperLocal(),
                            pData );

                if (aEnglishName.isEmpty())
                    SAL_WARN("sc.core", "no English name for " << aLocalName << " " << aFuncName);
                else
                {
                    pEnglishHashMap->emplace(
                            aEnglishName.toAsciiUpperCase(),
                            pData );
                }
            }
        }
    }
@@ -561,7 +581,7 @@ bool ScUnoAddInCollection::GetExcelName( const OUString& rCalcName,
{
    const ScUnoAddInFuncData* pFuncData = GetFuncData( rCalcName );
    if ( pFuncData )
        return pFuncData->GetExcelName( eDestLang, rRetExcelName);
        return pFuncData->GetExcelName( LanguageTag( eDestLang), rRetExcelName);
    return false;
}

@@ -746,7 +766,10 @@ void ScUnoAddInCollection::ReadFromAddIn( const uno::Reference<uno::XInterface>&
        pNameHashMap.reset( new ScAddInHashMap );
    if ( !pLocalHashMap )
        pLocalHashMap.reset( new ScAddInHashMap );
    if ( !pEnglishHashMap )
        pEnglishHashMap.reset( new ScAddInHashMap );

    const LanguageTag aEnglishLanguageTag(LANGUAGE_ENGLISH_US);
    const uno::Reference<reflection::XIdlMethod>* pArray = aMethods.getConstArray();
    for (tools::Long nFuncPos=0; nFuncPos<nNewCount; nFuncPos++)
    {
@@ -907,6 +930,16 @@ void ScUnoAddInCollection::ReadFromAddIn( const uno::Reference<uno::XInterface>&
                    pLocalHashMap->emplace(
                                pData->GetUpperLocal(),
                                pData );

                    OUString aEnglishName;
                    if (!pData->GetExcelName( aEnglishLanguageTag, aEnglishName, false /*bFallbackToAny*/))
                        SAL_WARN("sc.core", "no English name for " << aLocalName << " " << aFuncName);
                    else
                    {
                        pEnglishHashMap->emplace(
                                aEnglishName.toAsciiUpperCase(),
                                pData );
                    }
                }
            }
        }
@@ -1076,7 +1109,7 @@ OUString ScUnoAddInCollection::FindFunction( const OUString& rUpperName, bool bL

    if ( bLocalFirst )
    {
        //  first scan all local names (used for entering formulas)
        // Only scan local names (used for entering formulas).

        ScAddInHashMap::const_iterator iLook( pLocalHashMap->find( rUpperName ) );
        if ( iLook != pLocalHashMap->end() )
@@ -1084,14 +1117,22 @@ OUString ScUnoAddInCollection::FindFunction( const OUString& rUpperName, bool bL
    }
    else
    {
        //  first scan international names (used when calling a function)
        //TODO: before that, check for exact match???
        // First scan international programmatic names (used when calling a
        // function).

        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)
        // Then scan English names (as FunctionAccess API could expect).

        iLook = pEnglishHashMap->find( rUpperName );
        if ( iLook != pEnglishHashMap->end() )
            return iLook->second->GetOriginalName();

        // After that, scan all local names; either to allow replacing old
        // AddIns with Uno, or for functions where the AddIn did not provide an
        // English name.

        iLook = pLocalHashMap->find( rUpperName );
        if ( iLook != pLocalHashMap->end() )
diff --git a/sc/source/core/tool/compiler.cxx b/sc/source/core/tool/compiler.cxx
index 86499cc..d7b6286 100644
--- a/sc/source/core/tool/compiler.cxx
+++ b/sc/source/core/tool/compiler.cxx
@@ -166,6 +166,7 @@ void ScCompiler::fillFromAddInCollectionUpperName( const NonConstOpCodeMapPtr& x

void ScCompiler::fillFromAddInCollectionEnglishName( const NonConstOpCodeMapPtr& xMap ) const
{
    const LanguageTag aEnglishLanguageTag(LANGUAGE_ENGLISH_US);
    ScUnoAddInCollection* pColl = ScGlobal::GetAddInCollection();
    tools::Long nCount = pColl->GetFuncCount();
    for (tools::Long i=0; i < nCount; ++i)
@@ -174,7 +175,7 @@ void ScCompiler::fillFromAddInCollectionEnglishName( const NonConstOpCodeMapPtr&
        if (pFuncData)
        {
            OUString aName;
            if (pFuncData->GetExcelName( LANGUAGE_ENGLISH_US, aName))
            if (pFuncData->GetExcelName( aEnglishLanguageTag, aName))
                xMap->putExternalSoftly( aName, pFuncData->GetOriginalName());
            else
                xMap->putExternalSoftly( pFuncData->GetUpperName(),
@@ -5523,6 +5524,7 @@ void ScCompiler::fillAddInToken(::std::vector< css::sheet::FormulaOpCodeMapEntry
    sheet::FormulaOpCodeMapEntry aEntry;
    aEntry.Token.OpCode = ocExternal;

    const LanguageTag aEnglishLanguageTag(LANGUAGE_ENGLISH_US);
    ScUnoAddInCollection* pColl = ScGlobal::GetAddInCollection();
    const tools::Long nCount = pColl->GetFuncCount();
    for (tools::Long i=0; i < nCount; ++i)
@@ -5533,7 +5535,7 @@ void ScCompiler::fillAddInToken(::std::vector< css::sheet::FormulaOpCodeMapEntry
            if ( _bIsEnglish )
            {
                OUString aName;
                if (pFuncData->GetExcelName( LANGUAGE_ENGLISH_US, aName))
                if (pFuncData->GetExcelName( aEnglishLanguageTag, aName))
                    aEntry.Name = aName;
                else
                    aEntry.Name = pFuncData->GetUpperName();
diff --git a/scaddins/source/analysis/analysis.cxx b/scaddins/source/analysis/analysis.cxx
index 9ad63bc..9c9fc97 100644
--- a/scaddins/source/analysis/analysis.cxx
+++ b/scaddins/source/analysis/analysis.cxx
@@ -298,7 +298,7 @@ inline const lang::Locale& AnalysisAddIn::GetLocale( sal_uInt32 nInd )
    if( !pDefLocales )
        InitDefLocales();

    if( nInd < sizeof( pLang ) )
    if( nInd < nNumOfLoc )
        return pDefLocales[ nInd ];
    else
        return aFuncLoc;
diff --git a/scaddins/source/datefunc/datefunc.cxx b/scaddins/source/datefunc/datefunc.cxx
index 3ca23c5..92492b2 100644
--- a/scaddins/source/datefunc/datefunc.cxx
+++ b/scaddins/source/datefunc/datefunc.cxx
@@ -120,7 +120,7 @@ const lang::Locale& ScaDateAddIn::GetLocale( sal_uInt32 nIndex )
    if( !pDefLocales )
        InitDefLocales();

    return (nIndex < sizeof( pLang )) ? pDefLocales[ nIndex ] : aFuncLoc;
    return (nIndex < nNumOfLoc) ? pDefLocales[ nIndex ] : aFuncLoc;
}

void ScaDateAddIn::InitData()
diff --git a/scaddins/source/pricing/pricing.cxx b/scaddins/source/pricing/pricing.cxx
index 3961603..f4e9e53 100644
--- a/scaddins/source/pricing/pricing.cxx
+++ b/scaddins/source/pricing/pricing.cxx
@@ -105,8 +105,8 @@ ScaPricingAddIn::~ScaPricingAddIn()
{
}

static const char*  pLang[] = { "de", "en" };
static const char*  pCoun[] = { "DE", "US" };
static const char*  pLang[] = { "en" };
static const char*  pCoun[] = { "US" };
const sal_uInt32 nNumOfLoc = SAL_N_ELEMENTS( pLang );

void ScaPricingAddIn::InitDefLocales()
@@ -125,7 +125,7 @@ const lang::Locale& ScaPricingAddIn::GetLocale( sal_uInt32 nIndex )
    if( !pDefLocales )
        InitDefLocales();

    return (nIndex < sizeof( pLang )) ? pDefLocales[ nIndex ] : aFuncLoc;
    return (nIndex < nNumOfLoc) ? pDefLocales[ nIndex ] : aFuncLoc;
}

void ScaPricingAddIn::InitData()
diff --git a/scaddins/source/pricing/pricing.hxx b/scaddins/source/pricing/pricing.hxx
index ee362a3..6c6ad7d 100644
--- a/scaddins/source/pricing/pricing.hxx
+++ b/scaddins/source/pricing/pricing.hxx
@@ -61,8 +61,8 @@ struct ScaFuncDataBase
    const TranslateId*          pDescrID;           // resource ID to description, parameter names and ~ description
    // pCompName was originally meant to be able to load Excel documents that for
    // some time were stored with localized function names.
    // This is not relevant to this add-in, so we only supply the same
    // (English) function names again.
    // This is not relevant to this add-in, so we only supply the
    // English function name.
    // see also: GetExcelName() or GetCompNames() or getCompatibilityNames()
    const char*                 pCompName;
    sal_uInt16                  nParamCount;        // number of named / described parameters