    /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
    /*
     * This file is part of the LibreOffice project.
     *
     * This Source Code Form is subject to the terms of the Mozilla Public
     * License, v. 2.0. If a copy of the MPL was not distributed with this
     * file, You can obtain one at http://mozilla.org/MPL/2.0/.
     *
     * This file incorporates work covered by the following license notice:
     *
     *   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 .
     */

    #include <sal/config.h>

    #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
    #include <svl/hint.hxx>
    #include <sfx2/linkmgr.hxx>
    #include <vcl/svapp.hxx>
    #include <svl/sharedstringpool.hxx>

    #include <linkuno.hxx>
    #include <miscuno.hxx>
    #include <convuno.hxx>
    #include <docsh.hxx>
    #include <docfunc.hxx>
    #include <tablink.hxx>
    #include <arealink.hxx>
    #include <hints.hxx>
    #include <unonames.hxx>
    #include <rangeseq.hxx>
    #include <token.hxx>
    #include <scmatrix.hxx>
    #include <documentlinkmgr.hxx>

    #include <vector>
    #include <climits>

    using namespace com::sun::star;
    using namespace formula;
    using ::com::sun::star::uno::Any;
    using ::com::sun::star::uno::Sequence;
    using ::com::sun::star::lang::IllegalArgumentException;
    using ::com::sun::star::uno::RuntimeException;
    using ::std::vector;

    //  used for sheet- and area link:
    static const SfxItemPropertyMapEntry* lcl_GetSheetLinkMap()
    {
        static const SfxItemPropertyMapEntry aSheetLinkMap_Impl[] =
        {
            {OUString(SC_UNONAME_FILTER),   0,  cppu::UnoType<OUString>::get(),    0, 0 },
            {OUString(SC_UNONAME_FILTOPT),  0,  cppu::UnoType<OUString>::get(),    0, 0 },
            {OUString(SC_UNONAME_LINKURL),  0,  cppu::UnoType<OUString>::get(),    0, 0 },
            {OUString(SC_UNONAME_REFDELAY), 0,  cppu::UnoType<sal_Int32>::get(),        0, 0 },
            {OUString(SC_UNONAME_REFPERIOD),    0,  cppu::UnoType<sal_Int32>::get(),        0, 0 },
            { OUString(), 0, css::uno::Type(), 0, 0 }
        };
        return aSheetLinkMap_Impl;
    }

    SC_SIMPLE_SERVICE_INFO( ScAreaLinkObj, "ScAreaLinkObj", "com.sun.star.sheet.CellAreaLink" )
    SC_SIMPLE_SERVICE_INFO( ScAreaLinksObj, "ScAreaLinksObj", "com.sun.star.sheet.CellAreaLinks" )
    SC_SIMPLE_SERVICE_INFO( ScDDELinkObj, "ScDDELinkObj", "com.sun.star.sheet.DDELink" )
    SC_SIMPLE_SERVICE_INFO( ScDDELinksObj, "ScDDELinksObj", "com.sun.star.sheet.DDELinks" )
    SC_SIMPLE_SERVICE_INFO( ScSheetLinkObj, "ScSheetLinkObj", "com.sun.star.sheet.SheetLink" )
    SC_SIMPLE_SERVICE_INFO( ScSheetLinksObj, "ScSheetLinksObj", "com.sun.star.sheet.SheetLinks" )

    ScSheetLinkObj::ScSheetLinkObj(ScDocShell* pDocSh, const OUString& rName) :
        aPropSet( lcl_GetSheetLinkMap() ),
        pDocShell( pDocSh ),
        aFileName( rName )
    {
        pDocShell->GetDocument().AddUnoObject(*this);
    }

    ScSheetLinkObj::~ScSheetLinkObj()
    {
        SolarMutexGuard g;

        if (pDocShell)
            pDocShell->GetDocument().RemoveUnoObject(*this);
    }

    void ScSheetLinkObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
    {
        //! notify if links in document are changed
        //  UpdateRef is not needed here

        if ( dynamic_cast<const ScLinkRefreshedHint*>(&rHint) )
        {
            const ScLinkRefreshedHint& rLH = static_cast<const ScLinkRefreshedHint&>(rHint);
            if ( rLH.GetLinkType() == ScLinkRefType::SHEET && rLH.GetUrl() == aFileName )
                Refreshed_Impl();
        }
        else
        {
            if ( rHint.GetId() == SfxHintId::Dying )
                pDocShell = nullptr;       // pointer is invalid
        }
    }

    ScTableLink* ScSheetLinkObj::GetLink_Impl() const
    {
        if (pDocShell)
        {
            sfx2::LinkManager* pLinkManager = pDocShell->GetDocument().GetLinkManager();
            size_t nCount = pLinkManager->GetLinks().size();
            for (size_t i=0; i<nCount; i++)
            {
                ::sfx2::SvBaseLink* pBase = pLinkManager->GetLinks()[i].get();
                if (dynamic_cast<const ScTableLink*>( pBase) !=  nullptr)
                {
                    ScTableLink* pTabLink = static_cast<ScTableLink*>(pBase);
                    if ( pTabLink->GetFileName() == aFileName )
                        return pTabLink;
                }
            }
        }
        return nullptr;    // not found
    }

    // XNamed

    OUString SAL_CALL ScSheetLinkObj::getName()
    {
        SolarMutexGuard aGuard;
        return getFileName();   // Name is the same as filename (URL)
    }

    void SAL_CALL ScSheetLinkObj::setName( const OUString& aName )
    {
        SolarMutexGuard aGuard;
        setFileName(aName);     // Name is the same as filename (URL)
    }

    // XRefreshable

    void SAL_CALL ScSheetLinkObj::refresh()
    {
        SolarMutexGuard aGuard;
        ScTableLink* pLink = GetLink_Impl();
        if (pLink)
            pLink->Refresh( pLink->GetFileName(), pLink->GetFilterName(), nullptr, pLink->GetRefreshDelay() );
    }

    void SAL_CALL ScSheetLinkObj::addRefreshListener(
        const uno::Reference<util::XRefreshListener >& xListener )
    {
        SolarMutexGuard aGuard;
        aRefreshListeners.push_back( xListener );

        //  hold one additional ref to keep this object alive as long as there are listeners
        if ( aRefreshListeners.size() == 1 )
            acquire();
    }

    void SAL_CALL ScSheetLinkObj::removeRefreshListener(
                                    const uno::Reference<util::XRefreshListener >& xListener )
    {
        SolarMutexGuard aGuard;
        size_t nCount = aRefreshListeners.size();
        for ( size_t n=nCount; n--; )
        {
            uno::Reference<util::XRefreshListener>& rObj = aRefreshListeners[n];
            if ( rObj == xListener )
            {
                aRefreshListeners.erase( aRefreshListeners.begin() + n );
                if ( aRefreshListeners.empty() )
                    release();                          // release ref for listeners
                break;
            }
        }
    }

    void ScSheetLinkObj::Refreshed_Impl()
    {
        lang::EventObject aEvent;
        aEvent.Source.set(static_cast<cppu::OWeakObject*>(this));
        for (uno::Reference<util::XRefreshListener> & xRefreshListener : aRefreshListeners)
            xRefreshListener->refreshed( aEvent );
    }

    void ScSheetLinkObj::ModifyRefreshDelay_Impl( sal_Int32 nRefresh )
    {
        ScTableLink* pLink = GetLink_Impl();
        if( pLink )
            pLink->SetRefreshDelay( static_cast<sal_uLong>(nRefresh) );
    }

    // XPropertySet

    uno::Reference<beans::XPropertySetInfo> SAL_CALL ScSheetLinkObj::getPropertySetInfo()
    {
        SolarMutexGuard aGuard;
        static uno::Reference<beans::XPropertySetInfo> aRef(
            new SfxItemPropertySetInfo( aPropSet.getPropertyMap() ));
        return aRef;
    }

    void SAL_CALL ScSheetLinkObj::setPropertyValue(
                            const OUString& aPropertyName, const uno::Any& aValue )
    {
        SolarMutexGuard aGuard;
        OUString aValStr;
        if ( aPropertyName == SC_UNONAME_LINKURL )
        {
            if ( aValue >>= aValStr )
                setFileName( aValStr );
        }
        else if ( aPropertyName == SC_UNONAME_FILTER )
        {
            if ( aValue >>= aValStr )
                setFilter( aValStr );
        }
        else if ( aPropertyName == SC_UNONAME_FILTOPT )
        {
            if ( aValue >>= aValStr )
                setFilterOptions( aValStr );
        }
        else if ( aPropertyName == SC_UNONAME_REFPERIOD )
        {
            sal_Int32 nRefresh = 0;
            if ( aValue >>= nRefresh )
                setRefreshDelay( nRefresh );
        }
        else if ( aPropertyName == SC_UNONAME_REFDELAY )
        {
            sal_Int32 nRefresh = 0;
            if ( aValue >>= nRefresh )
                setRefreshDelay( nRefresh );
        }
    }

    uno::Any SAL_CALL ScSheetLinkObj::getPropertyValue( const OUString& aPropertyName )
    {
        SolarMutexGuard aGuard;
        uno::Any aRet;
        if ( aPropertyName == SC_UNONAME_LINKURL )
            aRet <<= getFileName();
        else if ( aPropertyName == SC_UNONAME_FILTER )
            aRet <<= getFilter();
        else if ( aPropertyName == SC_UNONAME_FILTOPT )
            aRet <<= getFilterOptions();
        else if ( aPropertyName == SC_UNONAME_REFPERIOD )
            aRet <<= getRefreshDelay();
        else if ( aPropertyName == SC_UNONAME_REFDELAY )
            aRet <<= getRefreshDelay();
        return aRet;
    }

    SC_IMPL_DUMMY_PROPERTY_LISTENER( ScSheetLinkObj )

    // internal:

    OUString ScSheetLinkObj::getFileName() const
    {
        SolarMutexGuard aGuard;
        return aFileName;
    }

    void ScSheetLinkObj::setFileName(const OUString& rNewName)
    {
        SolarMutexGuard aGuard;
        ScTableLink* pLink = GetLink_Impl();
        if (pLink)
        {
            //  pLink->Refresh with a new file name confuses sfx2::LinkManager
            //  therefore we transplant the sheets manually and create new links with UpdateLinks

            OUString aNewStr(ScGlobal::GetAbsDocName( rNewName, pDocShell ));

            //  first transplant the sheets

            ScDocument& rDoc = pDocShell->GetDocument();
            SCTAB nTabCount = rDoc.GetTableCount();
            for (SCTAB nTab=0; nTab<nTabCount; nTab++)
                if ( rDoc.IsLinked(nTab) && rDoc.GetLinkDoc(nTab) == aFileName )  // old file
                    rDoc.SetLink( nTab, rDoc.GetLinkMode(nTab), aNewStr,
                                    rDoc.GetLinkFlt(nTab), rDoc.GetLinkOpt(nTab),
                                    rDoc.GetLinkTab(nTab),
                                    rDoc.GetLinkRefreshDelay(nTab) );  // only change the file

            //  update links
            //! Undo !!!

            pDocShell->UpdateLinks();   // remove old links, possibly set up new ones

            //  copy data

            aFileName = aNewStr;
            pLink = GetLink_Impl();     // new link with new name
            if (pLink)
                pLink->Update();        // incl. paint & undo for data
        }
    }

    OUString ScSheetLinkObj::getFilter() const
    {
        SolarMutexGuard aGuard;
        OUString aRet;
        ScTableLink* pLink = GetLink_Impl();
        if (pLink)
            aRet = pLink->GetFilterName();
        return aRet;
    }

    void ScSheetLinkObj::setFilter(const OUString& rFilter)
    {
        SolarMutexGuard aGuard;
        ScTableLink* pLink = GetLink_Impl();
        if (pLink)
        {
            pLink->Refresh( aFileName, rFilter, nullptr, pLink->GetRefreshDelay() );
        }
    }

    OUString ScSheetLinkObj::getFilterOptions() const
    {
        SolarMutexGuard aGuard;
        OUString aRet;
        ScTableLink* pLink = GetLink_Impl();
        if (pLink)
            aRet = pLink->GetOptions();
        return aRet;
    }

    void ScSheetLinkObj::setFilterOptions(const OUString& FilterOptions)
    {
        SolarMutexGuard aGuard;
        ScTableLink* pLink = GetLink_Impl();
        if (pLink)
        {
            OUString aOptStr(FilterOptions);
            pLink->Refresh( aFileName, pLink->GetFilterName(), &aOptStr, pLink->GetRefreshDelay() );
        }
    }

    sal_Int32 ScSheetLinkObj::getRefreshDelay() const
    {
        SolarMutexGuard aGuard;
        sal_Int32 nRet = 0;
        ScTableLink* pLink = GetLink_Impl();
        if (pLink)
            nRet = static_cast<sal_Int32>(pLink->GetRefreshDelay());
        return nRet;
    }

    void ScSheetLinkObj::setRefreshDelay(sal_Int32 nRefreshDelay)
    {
        SolarMutexGuard aGuard;
        ModifyRefreshDelay_Impl( nRefreshDelay );
    }

    ScSheetLinksObj::ScSheetLinksObj(ScDocShell* pDocSh) :
        pDocShell( pDocSh )
    {
        pDocShell->GetDocument().AddUnoObject(*this);
    }

    ScSheetLinksObj::~ScSheetLinksObj()
    {
        SolarMutexGuard g;

        if (pDocShell)
            pDocShell->GetDocument().RemoveUnoObject(*this);
    }

    void ScSheetLinksObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
    {
        // we don't care about update of references here

        if ( rHint.GetId() == SfxHintId::Dying )
        {
            pDocShell = nullptr;       // became invalid
        }
    }

    // XSheetLinks

    ScSheetLinkObj* ScSheetLinksObj::GetObjectByIndex_Impl(sal_Int32 nIndex)
    {
        if (!pDocShell)
            return nullptr;

        typedef std::unordered_set<OUString> StrSetType;
        StrSetType aNames;
        ScDocument& rDoc = pDocShell->GetDocument();
        SCTAB nTabCount = rDoc.GetTableCount();
        sal_Int32 nCount = 0;
        for (SCTAB nTab = 0; nTab < nTabCount; ++nTab)
        {
            if (!rDoc.IsLinked(nTab))
                continue;

            OUString aLinkDoc = rDoc.GetLinkDoc(nTab);
            if (aNames.insert(aLinkDoc).second)
            {
                // unique document name.
                if (nCount == nIndex)
                    return new ScSheetLinkObj( pDocShell, aLinkDoc );
                ++nCount;
            }
        }

        return nullptr;    // no document or index too large
    }

    ScSheetLinkObj* ScSheetLinksObj::GetObjectByName_Impl(const OUString& aName)
    {
        //  Name is the same as file name

        if (pDocShell)
        {
            ScDocument& rDoc = pDocShell->GetDocument();
            SCTAB nTabCount = rDoc.GetTableCount();
            for (SCTAB nTab=0; nTab<nTabCount; nTab++)
                if (rDoc.IsLinked(nTab))
                {
                    //! case-insensitive ???
                    OUString aLinkDoc = rDoc.GetLinkDoc( nTab );
                    if ( aLinkDoc == aName )
                        return new ScSheetLinkObj( pDocShell, aName );
                }
        }

        return nullptr;
    }

    // XEnumerationAccess
    uno::Reference<container::XEnumeration> SAL_CALL ScSheetLinksObj::createEnumeration()
    {
        SolarMutexGuard aGuard;
        return new ScIndexEnumeration(this, "com.sun.star.sheet.SheetLinksEnumeration");
    }

    // XIndexAccess
    sal_Int32 SAL_CALL ScSheetLinksObj::getCount()
    {
        typedef std::unordered_set<OUString> StrSetType;

        SolarMutexGuard aGuard;
        if (!pDocShell)
            return 0;

        sal_Int32 nCount = 0;

        StrSetType aNames;
        ScDocument& rDoc = pDocShell->GetDocument();
        SCTAB nTabCount = rDoc.GetTableCount();
        for (SCTAB nTab = 0; nTab < nTabCount; ++nTab)
        {
            if (!rDoc.IsLinked(nTab))
                continue;

            OUString aLinkDoc = rDoc.GetLinkDoc(nTab);
            if (aNames.insert(aLinkDoc).second)
                ++nCount;
        }
        return nCount;
    }

    uno::Any SAL_CALL ScSheetLinksObj::getByIndex( sal_Int32 nIndex )
    {
        SolarMutexGuard aGuard;
        uno::Reference<beans::XPropertySet> xLink(GetObjectByIndex_Impl(nIndex));
        if (!xLink.is())
            throw lang::IndexOutOfBoundsException();

        return uno::makeAny(xLink);
    }

    uno::Type SAL_CALL ScSheetLinksObj::getElementType()
    {
        SolarMutexGuard aGuard;
        return cppu::UnoType<beans::XPropertySet>::get();
    }

    sal_Bool SAL_CALL ScSheetLinksObj::hasElements()
    {
        SolarMutexGuard aGuard;
        return ( getCount() != 0 );
    }

    uno::Any SAL_CALL ScSheetLinksObj::getByName( const OUString& aName )
    {
        SolarMutexGuard aGuard;
        uno::Reference<beans::XPropertySet> xLink(GetObjectByName_Impl(aName));
        if (!xLink.is())
            throw container::NoSuchElementException();

        return uno::makeAny(xLink);
    }

    sal_Bool SAL_CALL ScSheetLinksObj::hasByName( const OUString& aName )
    {
        SolarMutexGuard aGuard;
        //  Name is the same as file name

        if (pDocShell)
        {
            ScDocument& rDoc = pDocShell->GetDocument();
            SCTAB nTabCount = rDoc.GetTableCount();
            for (SCTAB nTab=0; nTab<nTabCount; nTab++)
                if (rDoc.IsLinked(nTab))
                {
                    //! case-insensitive ???
                    OUString aLinkDoc(rDoc.GetLinkDoc( nTab ));
                    if ( aLinkDoc == aName )
                        return true;
                }
        }
        return false;
    }

    uno::Sequence<OUString> SAL_CALL ScSheetLinksObj::getElementNames()
    {
        typedef std::unordered_set<OUString> StrSetType;

        SolarMutexGuard aGuard;
        //  Name is the same as file name

        if (!pDocShell)
            return uno::Sequence<OUString>();

        StrSetType aNames;
        ScDocument& rDoc = pDocShell->GetDocument();
        SCTAB nTabCount = rDoc.GetTableCount();

        sal_Int32 nLinkCount = getCount();
        uno::Sequence<OUString> aSeq(nLinkCount);
        OUString* pAry = aSeq.getArray();
        size_t nPos = 0;
        for (SCTAB nTab = 0; nTab < nTabCount; ++nTab)
        {
            if (!rDoc.IsLinked(nTab))
                continue;

            OUString aLinkDoc = rDoc.GetLinkDoc(nTab);
            if (aNames.insert(aLinkDoc).second)
                pAry[nPos++] = aLinkDoc;
        }
        OSL_ENSURE( nPos==static_cast<size_t>(nLinkCount), "verzaehlt" );
        return aSeq;
    }

    static ScAreaLink* lcl_GetAreaLink( ScDocShell* pDocShell, size_t nPos )
    {
        if (pDocShell)
        {
            sfx2::LinkManager* pLinkManager = pDocShell->GetDocument().GetLinkManager();
            size_t nTotalCount = pLinkManager->GetLinks().size();
            size_t nAreaCount = 0;
            for (size_t i=0; i<nTotalCount; i++)
            {
                ::sfx2::SvBaseLink* pBase = pLinkManager->GetLinks()[i].get();
                if (dynamic_cast<const ScAreaLink*>( pBase) !=  nullptr)
                {
                    if ( nAreaCount == nPos )
                        return static_cast<ScAreaLink*>(pBase);
                    ++nAreaCount;
                }
            }
        }
        return nullptr;    // not found
    }

    ScAreaLinkObj::ScAreaLinkObj(ScDocShell* pDocSh, size_t nP) :
        aPropSet( lcl_GetSheetLinkMap() ),
        pDocShell( pDocSh ),
        nPos( nP )
    {
        pDocShell->GetDocument().AddUnoObject(*this);
    }

    ScAreaLinkObj::~ScAreaLinkObj()
    {
        SolarMutexGuard g;

        if (pDocShell)
            pDocShell->GetDocument().RemoveUnoObject(*this);
    }

    void ScAreaLinkObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
    {
        //! notify if links in document are changed
        //  UpdateRef is not needed here

        if ( dynamic_cast<const ScLinkRefreshedHint*>(&rHint) )
        {
            const ScLinkRefreshedHint& rLH = static_cast<const ScLinkRefreshedHint&>(rHint);
            if ( rLH.GetLinkType() == ScLinkRefType::AREA )
            {
                //  get this link to compare dest position
                ScAreaLink* pLink = lcl_GetAreaLink(pDocShell, nPos);
                if ( pLink && pLink->GetDestArea().aStart == rLH.GetDestPos() )
                    Refreshed_Impl();
            }
        }
        else
        {
            if ( rHint.GetId() == SfxHintId::Dying )
                pDocShell = nullptr;       // pointer is invalid
        }
    }

    // XFileLink

    void ScAreaLinkObj::Modify_Impl( const OUString* pNewFile, const OUString* pNewFilter,
                                     const OUString* pNewOptions, const OUString* pNewSource,
                                     const table::CellRangeAddress* pNewDest )
    {
        ScAreaLink* pLink = lcl_GetAreaLink(pDocShell, nPos);
        if (pLink)
        {
            OUString aFile    (pLink->GetFile());
            OUString aFilter  (pLink->GetFilter());
            OUString aOptions (pLink->GetOptions());
            OUString aSource  (pLink->GetSource());
            ScRange aDest   (pLink->GetDestArea());
            sal_uLong nRefresh  = pLink->GetRefreshDelay();

            //! Undo delete
            //! Undo merge

            sfx2::LinkManager* pLinkManager = pDocShell->GetDocument().GetLinkManager();
            pLinkManager->Remove( pLink );
            pLink = nullptr;   // deleted along with remove

            bool bFitBlock = true;          // move, if the size changes with update
            if (pNewFile)
            {
                aFile = *pNewFile;
                aFile = ScGlobal::GetAbsDocName( aFile, pDocShell );    //! in InsertAreaLink?
            }
            if (pNewFilter)
                aFilter = *pNewFilter;
            if (pNewOptions)
                aOptions = *pNewOptions;
            if (pNewSource)
                aSource = *pNewSource;
            if (pNewDest)
            {
                ScUnoConversion::FillScRange( aDest, *pNewDest );
                bFitBlock = false;  // new range was specified -> do not move the content
            }
            pDocShell->GetDocFunc().InsertAreaLink( aFile, aFilter, aOptions, aSource,
                                                    aDest, nRefresh, bFitBlock, true );
        }
    }

    void ScAreaLinkObj::ModifyRefreshDelay_Impl( sal_Int32 nRefresh )
    {
        ScAreaLink* pLink = lcl_GetAreaLink( pDocShell, nPos );
        if( pLink )
            pLink->SetRefreshDelay( static_cast<sal_uLong>(nRefresh) );
    }

    // XRefreshable

    void SAL_CALL ScAreaLinkObj::refresh()
    {
        SolarMutexGuard aGuard;
        ScAreaLink* pLink = lcl_GetAreaLink(pDocShell, nPos);
        if (pLink)
            pLink->Refresh( pLink->GetFile(), pLink->GetFilter(), pLink->GetSource(), pLink->GetRefreshDelay() );
    }

    void SAL_CALL ScAreaLinkObj::addRefreshListener(
        const uno::Reference<util::XRefreshListener >& xListener )
    {
        SolarMutexGuard aGuard;
        aRefreshListeners.push_back( xListener );

        //  hold one additional ref to keep this object alive as long as there are listeners
        if ( aRefreshListeners.size() == 1 )
            acquire();
    }

    void SAL_CALL ScAreaLinkObj::removeRefreshListener(
                                    const uno::Reference<util::XRefreshListener >& xListener )
    {
        SolarMutexGuard aGuard;
        size_t nCount = aRefreshListeners.size();
        for ( size_t n=nCount; n--; )
        {
            uno::Reference<util::XRefreshListener>& rObj = aRefreshListeners[n];
            if ( rObj == xListener )
            {
                aRefreshListeners.erase( aRefreshListeners.begin() + n );
                if ( aRefreshListeners.empty() )
                    release();                          // release ref for listeners
                break;
            }

            if(n == 0)
                break;
        }
    }

    void ScAreaLinkObj::Refreshed_Impl()
    {
        lang::EventObject aEvent;
        aEvent.Source.set(static_cast<cppu::OWeakObject*>(this));
        for (uno::Reference<util::XRefreshListener> & xRefreshListener : aRefreshListeners)
            xRefreshListener->refreshed( aEvent );
    }

    // XPropertySet

    uno::Reference<beans::XPropertySetInfo> SAL_CALL ScAreaLinkObj::getPropertySetInfo()
    {
        SolarMutexGuard aGuard;
        static uno::Reference<beans::XPropertySetInfo> aRef(
            new SfxItemPropertySetInfo( aPropSet.getPropertyMap() ));
        return aRef;
    }

    void SAL_CALL ScAreaLinkObj::setPropertyValue(
                            const OUString& aPropertyName, const uno::Any& aValue )
    {
        SolarMutexGuard aGuard;
        OUString aValStr;
        if ( aPropertyName == SC_UNONAME_LINKURL )
        {
            if ( aValue >>= aValStr )
                setFileName( aValStr );
        }
        else if ( aPropertyName == SC_UNONAME_FILTER )
        {
            if ( aValue >>= aValStr )
                setFilter( aValStr );
        }
        else if ( aPropertyName == SC_UNONAME_FILTOPT )
        {
            if ( aValue >>= aValStr )
                setFilterOptions( aValStr );
        }
        else if ( aPropertyName == SC_UNONAME_REFPERIOD )
        {
            sal_Int32 nRefresh = 0;
            if ( aValue >>= nRefresh )
                setRefreshDelay( nRefresh );
        }
        else if ( aPropertyName == SC_UNONAME_REFDELAY )
        {
            sal_Int32 nRefresh = 0;
            if ( aValue >>= nRefresh )
                setRefreshDelay( nRefresh );
        }
    }

    uno::Any SAL_CALL ScAreaLinkObj::getPropertyValue( const OUString& aPropertyName )
    {
        SolarMutexGuard aGuard;
        uno::Any aRet;
        if ( aPropertyName == SC_UNONAME_LINKURL )
            aRet <<= getFileName();
        else if ( aPropertyName == SC_UNONAME_FILTER )
            aRet <<= getFilter();
        else if ( aPropertyName == SC_UNONAME_FILTOPT )
            aRet <<= getFilterOptions();
        else if ( aPropertyName == SC_UNONAME_REFPERIOD )
            aRet <<= getRefreshDelay();
        else if ( aPropertyName == SC_UNONAME_REFDELAY )
            aRet <<= getRefreshDelay();
        return aRet;
    }

    SC_IMPL_DUMMY_PROPERTY_LISTENER( ScAreaLinkObj )

    //  internal:

    OUString ScAreaLinkObj::getFileName() const
    {
        SolarMutexGuard aGuard;
        OUString aRet;
        ScAreaLink* pLink = lcl_GetAreaLink(pDocShell, nPos);
        if (pLink)
            aRet = pLink->GetFile();
        return aRet;
    }

    void ScAreaLinkObj::setFileName(const OUString& rNewName)
    {
        SolarMutexGuard aGuard;
        Modify_Impl( &rNewName, nullptr, nullptr, nullptr, nullptr );
    }

    OUString ScAreaLinkObj::getFilter() const
    {
        SolarMutexGuard aGuard;
        OUString aRet;
        ScAreaLink* pLink = lcl_GetAreaLink(pDocShell, nPos);
        if (pLink)
            aRet = pLink->GetFilter();
        return aRet;
    }

    void ScAreaLinkObj::setFilter(const OUString& Filter)
    {
        SolarMutexGuard aGuard;
        Modify_Impl( nullptr, &Filter, nullptr, nullptr, nullptr );
    }

    OUString ScAreaLinkObj::getFilterOptions() const
    {
        SolarMutexGuard aGuard;
        OUString aRet;
        ScAreaLink* pLink = lcl_GetAreaLink(pDocShell, nPos);
        if (pLink)
            aRet = pLink->GetOptions();
        return aRet;
    }

    void ScAreaLinkObj::setFilterOptions(const OUString& FilterOptions)
    {
        SolarMutexGuard aGuard;
        Modify_Impl( nullptr, nullptr, &FilterOptions, nullptr, nullptr );
    }

    sal_Int32 ScAreaLinkObj::getRefreshDelay() const
    {
        SolarMutexGuard aGuard;
        sal_Int32 nRet = 0;
        ScAreaLink* pLink = lcl_GetAreaLink(pDocShell, nPos);
        if (pLink)
            nRet = static_cast<sal_Int32>(pLink->GetRefreshDelay());
        return nRet;
    }

    void ScAreaLinkObj::setRefreshDelay(sal_Int32 nRefreshDelay)
    {
        SolarMutexGuard aGuard;
        ModifyRefreshDelay_Impl( nRefreshDelay );
    }

    // XAreaLink

    OUString SAL_CALL ScAreaLinkObj::getSourceArea()
    {
        SolarMutexGuard aGuard;
        OUString aRet;
        ScAreaLink* pLink = lcl_GetAreaLink(pDocShell, nPos);
        if (pLink)
            aRet = pLink->GetSource();
        return aRet;
    }

    void SAL_CALL ScAreaLinkObj::setSourceArea( const OUString& aSourceArea )
    {
        SolarMutexGuard aGuard;
        Modify_Impl( nullptr, nullptr, nullptr, &aSourceArea, nullptr );
    }

    table::CellRangeAddress SAL_CALL ScAreaLinkObj::getDestArea()
    {
        SolarMutexGuard aGuard;
        table::CellRangeAddress aRet;
        ScAreaLink* pLink = lcl_GetAreaLink(pDocShell, nPos);
        if (pLink)
            ScUnoConversion::FillApiRange( aRet, pLink->GetDestArea() );
        return aRet;
    }

    void SAL_CALL ScAreaLinkObj::setDestArea( const table::CellRangeAddress& aDestArea )
    {
        SolarMutexGuard aGuard;
        Modify_Impl( nullptr, nullptr, nullptr, nullptr, &aDestArea );
    }

    ScAreaLinksObj::ScAreaLinksObj(ScDocShell* pDocSh) :
        pDocShell( pDocSh )
    {
        pDocShell->GetDocument().AddUnoObject(*this);
    }

    ScAreaLinksObj::~ScAreaLinksObj()
    {
        SolarMutexGuard g;

        if (pDocShell)
            pDocShell->GetDocument().RemoveUnoObject(*this);
    }

    void ScAreaLinksObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
    {
        //  we don't care about update of references here

        if ( rHint.GetId() == SfxHintId::Dying )
        {
            pDocShell = nullptr;       // became invalid
        }
    }

    // XAreaLinks

    ScAreaLinkObj* ScAreaLinksObj::GetObjectByIndex_Impl(sal_Int32 nIndex)
    {
        if ( pDocShell && nIndex >= 0 && nIndex < getCount() )
            return new ScAreaLinkObj( pDocShell, static_cast<size_t>(nIndex) );

        return nullptr;    // not found
    }

    void SAL_CALL ScAreaLinksObj::insertAtPosition( const table::CellAddress& aDestPos,
                                                    const OUString& aFileName,
                                                    const OUString& aSourceArea,
                                                    const OUString& aFilter,
                                                    const OUString& aFilterOptions )
    {
        SolarMutexGuard aGuard;
        if (pDocShell)
        {
            OUString aFileStr   (aFileName);
            ScAddress aDestAddr( static_cast<SCCOL>(aDestPos.Column), static_cast<SCROW>(aDestPos.Row), aDestPos.Sheet );

            aFileStr = ScGlobal::GetAbsDocName( aFileStr, pDocShell );  //! in InsertAreaLink ???
            pDocShell->GetDocFunc().InsertAreaLink( aFileStr, aFilter, aFilterOptions,
                                                    aSourceArea, ScRange(aDestAddr),
                                                    0, false, true ); // don't move contents
        }
    }

    void SAL_CALL ScAreaLinksObj::removeByIndex( sal_Int32 nIndex )
    {
        SolarMutexGuard aGuard;
        ScAreaLink* pLink = lcl_GetAreaLink(pDocShell, static_cast<size_t>(nIndex));
        if (pLink)
        {
            //! SetAddUndo or what

            sfx2::LinkManager* pLinkManager = pDocShell->GetDocument().GetLinkManager();
            pLinkManager->Remove( pLink );
        }
    }

    // XEnumerationAccess

    uno::Reference<container::XEnumeration> SAL_CALL ScAreaLinksObj::createEnumeration()
    {
        SolarMutexGuard aGuard;
        return new ScIndexEnumeration(this, "com.sun.star.sheet.CellAreaLinksEnumeration");
    }

    // XIndexAccess

    sal_Int32 SAL_CALL ScAreaLinksObj::getCount()
    {
        SolarMutexGuard aGuard;
        sal_Int32 nAreaCount = 0;
        if (pDocShell)
        {
            sfx2::LinkManager* pLinkManager = pDocShell->GetDocument().GetLinkManager();
            size_t nTotalCount = pLinkManager->GetLinks().size();
            for (size_t i=0; i<nTotalCount; i++)
            {
                ::sfx2::SvBaseLink* pBase = pLinkManager->GetLinks()[i].get();
                if (dynamic_cast<const ScAreaLink*>( pBase) !=  nullptr)
                    ++nAreaCount;
            }
        }
        return nAreaCount;
    }

    uno::Any SAL_CALL ScAreaLinksObj::getByIndex( sal_Int32 nIndex )
    {
        SolarMutexGuard aGuard;
        uno::Reference<sheet::XAreaLink> xLink(GetObjectByIndex_Impl(nIndex));
        if (!xLink.is())
            throw lang::IndexOutOfBoundsException();

        return uno::makeAny(xLink);

    }

    uno::Type SAL_CALL ScAreaLinksObj::getElementType()
    {
        SolarMutexGuard aGuard;
        return cppu::UnoType<sheet::XAreaLink>::get();
    }

    sal_Bool SAL_CALL ScAreaLinksObj::hasElements()
    {
        SolarMutexGuard aGuard;
        return ( getCount() != 0 );
    }

    ScDDELinkObj::ScDDELinkObj(ScDocShell* pDocSh, const OUString& rA,
                                const OUString& rT, const OUString& rI) :
        pDocShell( pDocSh ),
        aAppl( rA ),
        aTopic( rT ),
        aItem( rI )
    {
        pDocShell->GetDocument().AddUnoObject(*this);
    }

    ScDDELinkObj::~ScDDELinkObj()
    {
        SolarMutexGuard g;

        if (pDocShell)
            pDocShell->GetDocument().RemoveUnoObject(*this);
    }

    void ScDDELinkObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
    {
        //! notify if links in document are changed
        //  UpdateRef is not needed here

        if ( dynamic_cast<const ScLinkRefreshedHint*>(&rHint) )
        {
            const ScLinkRefreshedHint& rLH = static_cast<const ScLinkRefreshedHint&>(rHint);
            if ( rLH.GetLinkType() == ScLinkRefType::DDE &&
                 rLH.GetDdeAppl()  == aAppl &&
                 rLH.GetDdeTopic() == aTopic &&
                 rLH.GetDdeItem()  == aItem )       //! mode is ignored
                Refreshed_Impl();
        }
        else
        {
            if ( rHint.GetId() == SfxHintId::Dying )
                pDocShell = nullptr;       // pointer is invalid
        }
    }

    // XNamed

    static OUString lcl_BuildDDEName( const OUString& rAppl, const OUString& rTopic, const OUString& rItem )
    {
        //  Appl|Topic!Item (like Excel)
        OUString aRet = rAppl + "|" + rTopic + "!" + rItem;
        return aRet;
    }

    OUString SAL_CALL ScDDELinkObj::getName()
    {
        SolarMutexGuard aGuard;
        return lcl_BuildDDEName( aAppl, aTopic, aItem );
    }

    void SAL_CALL ScDDELinkObj::setName( const OUString& /* aName */ )
    {
        //  name can't be changed (formulas wouldn't find the link)
        throw uno::RuntimeException();
    }

    // XDDELink

    OUString SAL_CALL ScDDELinkObj::getApplication()
    {
        SolarMutexGuard aGuard;
        //! Test if the link is still in the document?

        return aAppl;
    }

    OUString SAL_CALL ScDDELinkObj::getTopic()
    {
        SolarMutexGuard aGuard;
        //! Test if the link is still in the document?

        return aTopic;
    }

    OUString SAL_CALL ScDDELinkObj::getItem()
    {
        SolarMutexGuard aGuard;
        //! Test if the link is still in the document?

        return aItem;
    }

    // XRefreshable

    void SAL_CALL ScDDELinkObj::refresh()
    {
        SolarMutexGuard aGuard;
        if (pDocShell)
        {
            sc::DocumentLinkManager& rMgr = pDocShell->GetDocument().GetDocLinkManager();
            rMgr.updateDdeLink(aAppl, aTopic, aItem);
        }
    }

    void SAL_CALL ScDDELinkObj::addRefreshListener(
        const uno::Reference<util::XRefreshListener >& xListener )
    {
        SolarMutexGuard aGuard;
        aRefreshListeners.push_back( xListener );

        //  hold one additional ref to keep this object alive as long as there are listeners
        if ( aRefreshListeners.size() == 1 )
            acquire();
    }

    void SAL_CALL ScDDELinkObj::removeRefreshListener(
                                    const uno::Reference<util::XRefreshListener >& xListener )
    {
        SolarMutexGuard aGuard;
        size_t nCount = aRefreshListeners.size();
        for ( size_t n=nCount; n--; )
        {
            uno::Reference<util::XRefreshListener>& rObj = aRefreshListeners[n];
            if ( rObj == xListener )
            {
                aRefreshListeners.erase( aRefreshListeners.begin() + n );
                if ( aRefreshListeners.empty() )
                    release();                          // release ref for listeners
                break;
            }
        }
    }

    // XDDELinkResults

    uno::Sequence< uno::Sequence< uno::Any > > ScDDELinkObj::getResults(  )
    {
        SolarMutexGuard aGuard;
        uno::Sequence< uno::Sequence< uno::Any > > aReturn;
        bool bSuccess = false;

        if ( pDocShell )
        {
            ScDocument& rDoc = pDocShell->GetDocument();
            size_t nPos = 0;
            if ( rDoc.FindDdeLink( aAppl, aTopic, aItem, SC_DDE_IGNOREMODE, nPos ) )
            {
                const ScMatrix* pMatrix = rDoc.GetDdeLinkResultMatrix( nPos );
                if ( pMatrix )
                {
                    uno::Any aAny;
                    if ( ScRangeToSequence::FillMixedArray( aAny, pMatrix, true ) )
                    {
                        aAny >>= aReturn;
                    }
                }
                bSuccess = true;
            }
        }

        if ( !bSuccess )
        {
            throw uno::RuntimeException(
                "ScDDELinkObj::getResults: failed to get results!" );
        }

        return aReturn;
    }

    void ScDDELinkObj::setResults( const uno::Sequence< uno::Sequence< uno::Any > >& aResults )
    {
        SolarMutexGuard aGuard;
        bool bSuccess = false;

        if ( pDocShell )
        {
            ScDocument& rDoc = pDocShell->GetDocument();
            size_t nPos = 0;
            if ( rDoc.FindDdeLink( aAppl, aTopic, aItem, SC_DDE_IGNOREMODE, nPos ) )
            {
                ScMatrixRef xMatrix = ScSequenceToMatrix::CreateMixedMatrix( Any(aResults) );
                bSuccess = rDoc.SetDdeLinkResultMatrix( nPos, xMatrix );
            }
        }

        if ( !bSuccess )
        {
            throw uno::RuntimeException(
                "ScDDELinkObj::setResults: failed to set results!" );
        }
    }

    void ScDDELinkObj::Refreshed_Impl()
    {
        lang::EventObject aEvent;
        aEvent.Source.set(static_cast<cppu::OWeakObject*>(this));
        for (uno::Reference<util::XRefreshListener> & xRefreshListener : aRefreshListeners)
            xRefreshListener->refreshed( aEvent );
    }

    ScDDELinksObj::ScDDELinksObj(ScDocShell* pDocSh) :
        pDocShell( pDocSh )
    {
        pDocShell->GetDocument().AddUnoObject(*this);
    }

    ScDDELinksObj::~ScDDELinksObj()
    {
        SolarMutexGuard g;

        if (pDocShell)
            pDocShell->GetDocument().RemoveUnoObject(*this);
    }

    void ScDDELinksObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
    {
        //  we don't care about update of references here

        if ( rHint.GetId() == SfxHintId::Dying )
        {
            pDocShell = nullptr;       // became invalid
        }
    }

    // XDDELinks

    ScDDELinkObj* ScDDELinksObj::GetObjectByIndex_Impl(sal_Int32 nIndex)
    {
        if (pDocShell)
        {
            OUString aAppl, aTopic, aItem;
            if ( pDocShell->GetDocument().GetDdeLinkData( static_cast<size_t>(nIndex), aAppl, aTopic, aItem ) )
                return new ScDDELinkObj( pDocShell, aAppl, aTopic, aItem );
        }
        return nullptr;
    }

    ScDDELinkObj* ScDDELinksObj::GetObjectByName_Impl(const OUString& aName)
    {
        if (pDocShell)
        {
            OUString aAppl, aTopic, aItem;

            ScDocument& rDoc = pDocShell->GetDocument();
            size_t nCount = rDoc.GetDocLinkManager().getDdeLinkCount();
            for (size_t i=0; i<nCount; i++)
            {
                rDoc.GetDdeLinkData( i, aAppl, aTopic, aItem );
                if ( lcl_BuildDDEName(aAppl, aTopic, aItem) == aName )
                    return new ScDDELinkObj( pDocShell, aAppl, aTopic, aItem );
            }
        }
        return nullptr;
    }

    // XEnumerationAccess

    uno::Reference<container::XEnumeration> SAL_CALL ScDDELinksObj::createEnumeration()
    {
        SolarMutexGuard aGuard;
        return new ScIndexEnumeration(this, "com.sun.star.sheet.DDELinksEnumeration");
    }

    // XIndexAccess

    sal_Int32 SAL_CALL ScDDELinksObj::getCount()
    {
        SolarMutexGuard aGuard;
        sal_Int32 nAreaCount = 0;
        if (pDocShell)
            nAreaCount = pDocShell->GetDocument().GetDocLinkManager().getDdeLinkCount();
        return nAreaCount;
    }

    uno::Any SAL_CALL ScDDELinksObj::getByIndex( sal_Int32 nIndex )
    {
        SolarMutexGuard aGuard;
        uno::Reference<sheet::XDDELink> xLink(GetObjectByIndex_Impl(nIndex));
        if (!xLink.is())
            throw lang::IndexOutOfBoundsException();

        return uno::makeAny(xLink);
    }

    uno::Type SAL_CALL ScDDELinksObj::getElementType()
    {
        SolarMutexGuard aGuard;
        return cppu::UnoType<sheet::XDDELink>::get();
    }

    sal_Bool SAL_CALL ScDDELinksObj::hasElements()
    {
        SolarMutexGuard aGuard;
        return ( getCount() != 0 );
    }

    uno::Any SAL_CALL ScDDELinksObj::getByName( const OUString& aName )
    {
        SolarMutexGuard aGuard;
        uno::Reference<sheet::XDDELink> xLink(GetObjectByName_Impl(aName));
        if (!xLink.is())
            throw container::NoSuchElementException();

        return uno::makeAny(xLink);
    }

    uno::Sequence<OUString> SAL_CALL ScDDELinksObj::getElementNames()
    {
        SolarMutexGuard aGuard;
        if (pDocShell)
        {
            OUString aAppl, aTopic, aItem;

            ScDocument& rDoc = pDocShell->GetDocument();
            size_t nCount = pDocShell->GetDocument().GetDocLinkManager().getDdeLinkCount();
            uno::Sequence<OUString> aSeq(nCount);
            OUString* pAry = aSeq.getArray();

            for (size_t i=0; i<nCount; i++)
            {
                rDoc.GetDdeLinkData( i, aAppl, aTopic, aItem );
                pAry[i] = lcl_BuildDDEName(aAppl, aTopic, aItem);
            }
            return aSeq;
        }
        return uno::Sequence<OUString>();
    }

    sal_Bool SAL_CALL ScDDELinksObj::hasByName( const OUString& aName )
    {
        SolarMutexGuard aGuard;
        if (pDocShell)
        {
            OUString aAppl, aTopic, aItem;

            ScDocument& rDoc = pDocShell->GetDocument();
            size_t nCount = pDocShell->GetDocument().GetDocLinkManager().getDdeLinkCount();
            for (size_t i=0; i<nCount; i++)
            {
                rDoc.GetDdeLinkData( i, aAppl, aTopic, aItem );
                if ( lcl_BuildDDEName(aAppl, aTopic, aItem) == aName )
                    return true;
            }
        }
        return false;
    }

    // XDDELinks

    uno::Reference< sheet::XDDELink > ScDDELinksObj::addDDELink(
        const OUString& aApplication, const OUString& aTopic,
        const OUString& aItem, css::sheet::DDELinkMode nMode )
    {
        SolarMutexGuard aGuard;
        uno::Reference< sheet::XDDELink > xLink;

        if ( pDocShell )
        {
            ScDocument& rDoc = pDocShell->GetDocument();
            sal_uInt8 nMod = SC_DDE_DEFAULT;
            switch ( nMode )
            {
                case sheet::DDELinkMode_DEFAULT:
                    {
                        nMod = SC_DDE_DEFAULT;
                    }
                    break;
                case sheet::DDELinkMode_ENGLISH:
                    {
                        nMod = SC_DDE_ENGLISH;
                    }
                    break;
                case sheet::DDELinkMode_TEXT:
                    {
                        nMod = SC_DDE_TEXT;
                    }
                    break;
                default:
                    {
                    }
                    break;
            }

            if ( rDoc.CreateDdeLink( aApplication, aTopic, aItem, nMod, ScMatrixRef() ) )
            {
                const OUString aName( lcl_BuildDDEName( aApplication, aTopic, aItem ) );
                xLink.set( GetObjectByName_Impl( aName ) );
            }
        }

        if ( !xLink.is() )
        {
            throw uno::RuntimeException(
                "ScDDELinksObj::addDDELink: cannot add DDE link!" );
        }

        return xLink;
    }

    ScExternalSheetCacheObj::ScExternalSheetCacheObj(ScDocShell* pDocShell, ScExternalRefCache::TableTypeRef const & pTable, size_t nIndex) :
        mpDocShell(pDocShell),
        mpTable(pTable),
        mnIndex(nIndex)
    {
    }

    ScExternalSheetCacheObj::~ScExternalSheetCacheObj()
    {
    }

    void SAL_CALL ScExternalSheetCacheObj::setCellValue(sal_Int32 nCol, sal_Int32 nRow, const Any& rValue)
    {
        SolarMutexGuard aGuard;
        if (nRow < 0 || nCol < 0)
            throw IllegalArgumentException();

        ScExternalRefCache::TokenRef pToken;
        double fVal = 0.0;
        OUString aVal;
        if (rValue >>= fVal)
            pToken.reset(new FormulaDoubleToken(fVal));
        else if (rValue >>= aVal)
        {
            svl::SharedStringPool& rPool = mpDocShell->GetDocument().GetSharedStringPool();
            svl::SharedString aSS = rPool.intern(aVal);
            pToken.reset(new FormulaStringToken(aSS));
        }
        else
            // unidentified value type.
            return;

        mpTable->setCell(static_cast<SCCOL>(nCol), static_cast<SCROW>(nRow), pToken);
    }

    Any SAL_CALL ScExternalSheetCacheObj::getCellValue(sal_Int32 nCol, sal_Int32 nRow)
    {
        SolarMutexGuard aGuard;
        if (nRow < 0 || nCol < 0)
            throw IllegalArgumentException();

        FormulaToken* pToken = mpTable->getCell(static_cast<SCCOL>(nCol), static_cast<SCROW>(nRow)).get();
        if (!pToken)
            throw IllegalArgumentException();

        Any aValue;
        switch (pToken->GetType())
        {
            case svDouble:
            {
                double fVal = pToken->GetDouble();
                aValue <<= fVal;
            }
            break;
            case svString:
            {
                OUString aVal = pToken->GetString().getString();
                aValue <<= aVal;
            }
            break;
            default:
                throw IllegalArgumentException();
        }
        return aValue;
    }

    Sequence< sal_Int32 > SAL_CALL ScExternalSheetCacheObj::getAllRows()
    {
        SolarMutexGuard aGuard;
        vector<SCROW> aRows;
        mpTable->getAllRows(aRows);
        size_t nSize = aRows.size();
        Sequence<sal_Int32> aRowsSeq(nSize);
        for (size_t i = 0; i < nSize; ++i)
            aRowsSeq[i] = aRows[i];

        return aRowsSeq;
    }

    Sequence< sal_Int32 > SAL_CALL ScExternalSheetCacheObj::getAllColumns(sal_Int32 nRow)
    {
        SolarMutexGuard aGuard;
        if (nRow < 0)
            throw IllegalArgumentException();

        vector<SCCOL> aCols;
        mpTable->getAllCols(static_cast<SCROW>(nRow), aCols);
        size_t nSize = aCols.size();
        Sequence<sal_Int32> aColsSeq(nSize);
        for (size_t i = 0; i < nSize; ++i)
            aColsSeq[i] = aCols[i];

        return aColsSeq;
    }

    sal_Int32 SAL_CALL ScExternalSheetCacheObj::getTokenIndex()
    {
        return static_cast< sal_Int32 >( mnIndex );
    }

    ScExternalDocLinkObj::ScExternalDocLinkObj(ScDocShell* pDocShell, ScExternalRefManager* pRefMgr, sal_uInt16 nFileId) :
        mpDocShell(pDocShell), mpRefMgr(pRefMgr), mnFileId(nFileId)
    {
    }

    ScExternalDocLinkObj::~ScExternalDocLinkObj()
    {
    }

    uno::Reference< sheet::XExternalSheetCache > SAL_CALL ScExternalDocLinkObj::addSheetCache(
        const OUString& aSheetName, sal_Bool bDynamicCache )
    {
        SolarMutexGuard aGuard;
        size_t nIndex = 0;
        ScExternalRefCache::TableTypeRef pTable = mpRefMgr->getCacheTable(mnFileId, aSheetName, true, &nIndex);
        if (!bDynamicCache)
            // Set the whole table cached to prevent access to the source document.
            pTable->setWholeTableCached();

        uno::Reference< sheet::XExternalSheetCache > aSheetCache(new ScExternalSheetCacheObj(mpDocShell, pTable, nIndex));
        return aSheetCache;
    }

    Any SAL_CALL ScExternalDocLinkObj::getByName(const OUString &aName)
    {
        SolarMutexGuard aGuard;
        size_t nIndex = 0;
        ScExternalRefCache::TableTypeRef pTable = mpRefMgr->getCacheTable(mnFileId, aName, false, &nIndex);
        if (!pTable)
            throw container::NoSuchElementException();

        uno::Reference< sheet::XExternalSheetCache > aSheetCache(new ScExternalSheetCacheObj(mpDocShell, pTable, nIndex));

        return Any(aSheetCache);
    }

    Sequence< OUString > SAL_CALL ScExternalDocLinkObj::getElementNames()
    {
        SolarMutexGuard aGuard;
        vector<OUString> aTabNames;
        mpRefMgr->getAllCachedTableNames(mnFileId, aTabNames);

        // #i116940# be consistent with getByName: include only table names which have a cache already
        vector<OUString> aValidNames;
        for (vector<OUString>::iterator aIter = aTabNames.begin(); aIter != aTabNames.end(); ++aIter)
            if (mpRefMgr->getCacheTable(mnFileId, *aIter, false))
                aValidNames.push_back(*aIter);

        size_t n = aValidNames.size();
        Sequence<OUString> aSeq(n);
        for (size_t i = 0; i < n; ++i)
            aSeq[i] = aValidNames[i];
        return aSeq;
    }

    sal_Bool SAL_CALL ScExternalDocLinkObj::hasByName(const OUString &aName)
    {
        SolarMutexGuard aGuard;

        // #i116940# be consistent with getByName: allow only table names which have a cache already
        ScExternalRefCache::TableTypeRef pTable = mpRefMgr->getCacheTable(mnFileId, aName, false);
        return (pTable.get() != nullptr);
    }

    sal_Int32 SAL_CALL ScExternalDocLinkObj::getCount()
    {
        SolarMutexGuard aGuard;

        // #i116940# be consistent with getByName: count only table names which have a cache already
        return getElementNames().getLength();
    }

    Any SAL_CALL ScExternalDocLinkObj::getByIndex(sal_Int32 nApiIndex)
    {
        SolarMutexGuard aGuard;

        // #i116940# Can't use nApiIndex as index for the ref manager, because the API counts only
        // the entries which have a cache already. Quick solution: Use getElementNames.
        Sequence< OUString > aNames( getElementNames() );
        if (nApiIndex < 0 || nApiIndex >= aNames.getLength())
            throw lang::IndexOutOfBoundsException();

        size_t nIndex = 0;
        ScExternalRefCache::TableTypeRef pTable = mpRefMgr->getCacheTable(mnFileId, aNames[nApiIndex], false, &nIndex);
        if (!pTable)
            throw lang::IndexOutOfBoundsException();

        uno::Reference< sheet::XExternalSheetCache > aSheetCache(new ScExternalSheetCacheObj(mpDocShell, pTable, nIndex));

        return Any(aSheetCache);
    }

    uno::Reference< container::XEnumeration > SAL_CALL ScExternalDocLinkObj::createEnumeration()
    {
        SolarMutexGuard aGuard;
        uno::Reference< container::XEnumeration > aRef(
            new ScIndexEnumeration(this, "com.sun.star.sheet.ExternalDocLink"));
        return aRef;
    }

    uno::Type SAL_CALL ScExternalDocLinkObj::getElementType()
    {
        SolarMutexGuard aGuard;
        return cppu::UnoType<sheet::XExternalDocLink>::get();
    }

    sal_Bool SAL_CALL ScExternalDocLinkObj::hasElements()
    {
        SolarMutexGuard aGuard;

        // #i116940# be consistent with getByName: count only table names which have a cache already
        return ( getElementNames().getLength() > 0 );
    }

    sal_Int32 SAL_CALL ScExternalDocLinkObj::getTokenIndex()
    {
        return static_cast<sal_Int32>(mnFileId);
    }

    ScExternalDocLinksObj::ScExternalDocLinksObj(ScDocShell* pDocShell) :
        mpDocShell(pDocShell),
        mpRefMgr(pDocShell->GetDocument().GetExternalRefManager())
    {
    }

    ScExternalDocLinksObj::~ScExternalDocLinksObj()
    {
    }

    uno::Reference< sheet::XExternalDocLink > SAL_CALL ScExternalDocLinksObj::addDocLink(
        const OUString& aDocName )
    {
        SolarMutexGuard aGuard;
        OUString aDocUrl( ScGlobal::GetAbsDocName( aDocName, mpDocShell));
        sal_uInt16 nFileId = mpRefMgr->getExternalFileId(aDocUrl);
        uno::Reference< sheet::XExternalDocLink > aDocLink(new ScExternalDocLinkObj(mpDocShell, mpRefMgr, nFileId));
        return aDocLink;
    }

    Any SAL_CALL ScExternalDocLinksObj::getByName(const OUString &aName)
    {
        SolarMutexGuard aGuard;
        OUString aDocUrl( ScGlobal::GetAbsDocName( aName, mpDocShell));
        if (!mpRefMgr->hasExternalFile(aDocUrl))
            throw container::NoSuchElementException();

        sal_uInt16 nFileId = mpRefMgr->getExternalFileId(aDocUrl);
        uno::Reference< sheet::XExternalDocLink > aDocLink(new ScExternalDocLinkObj(mpDocShell, mpRefMgr, nFileId));

        return Any(aDocLink);
    }

    Sequence< OUString > SAL_CALL ScExternalDocLinksObj::getElementNames()
    {
        SolarMutexGuard aGuard;
        sal_uInt16 n = mpRefMgr->getExternalFileCount();
        Sequence<OUString> aSeq(n);
        for (sal_uInt16 i = 0; i < n; ++i)
        {
            const OUString* pName = mpRefMgr->getExternalFileName(i);
            aSeq[i] = pName ? *pName : OUString();
        }

        return aSeq;
    }

    sal_Bool SAL_CALL ScExternalDocLinksObj::hasByName(const OUString &aName)
    {
        SolarMutexGuard aGuard;
        return mpRefMgr->hasExternalFile(aName);
    }

    sal_Int32 SAL_CALL ScExternalDocLinksObj::getCount()
    {
        SolarMutexGuard aGuard;
        return mpRefMgr->getExternalFileCount();
    }

    Any SAL_CALL ScExternalDocLinksObj::getByIndex(sal_Int32 nIndex)
    {
        SolarMutexGuard aGuard;
        if (nIndex > ::std::numeric_limits<sal_uInt16>::max() || nIndex < ::std::numeric_limits<sal_uInt16>::min())
            throw lang::IndexOutOfBoundsException();

        sal_uInt16 nFileId = static_cast<sal_uInt16>(nIndex);

        if (!mpRefMgr->hasExternalFile(nFileId))
            throw lang::IndexOutOfBoundsException();

        uno::Reference< sheet::XExternalDocLink > aDocLink(new ScExternalDocLinkObj(mpDocShell, mpRefMgr, nFileId));
        return Any(aDocLink);
    }

    uno::Reference< container::XEnumeration > SAL_CALL ScExternalDocLinksObj::createEnumeration()
    {
        SolarMutexGuard aGuard;
        uno::Reference< container::XEnumeration > aRef(
            new ScIndexEnumeration(this, "com.sun.star.sheet.ExternalDocLinks"));
        return aRef;
    }

    uno::Type SAL_CALL ScExternalDocLinksObj::getElementType()
    {
        SolarMutexGuard aGuard;
        return cppu::UnoType<sheet::XExternalDocLinks>::get();
    }

    sal_Bool SAL_CALL ScExternalDocLinksObj::hasElements()
    {
        SolarMutexGuard aGuard;
        return mpRefMgr->getExternalFileCount() > 0;
    }

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

↑ V524 It is odd that the body of 'removeVetoableChangeListener' function is fully equivalent to the body of 'addVetoableChangeListener' function.

↑ V524 It is odd that the body of 'removePropertyChangeListener' function is fully equivalent to the body of 'addPropertyChangeListener' function.

↑ V524 It is odd that the body of 'removeVetoableChangeListener' function is fully equivalent to the body of 'addVetoableChangeListener' function.

↑ V524 It is odd that the body of 'removePropertyChangeListener' function is fully equivalent to the body of 'addPropertyChangeListener' function.