    /* -*- 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/io/IOException.hpp>
    #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
    #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
    #include <osl/diagnose.h>
    #include <vcl/svapp.hxx>
    #include <svtools/unoevent.hxx>
    #include <svl/urihelper.hxx>
    #include <sfx2/event.hxx>
    #include <swtypes.hxx>
    #include <glosdoc.hxx>
    #include <shellio.hxx>
    #include <initui.hxx>
    #include <gloslst.hxx>
    #include <unoatxt.hxx>
    #include <unomap.hxx>
    #include <unomid.h>
    #include <unotextbodyhf.hxx>
    #include <unotextrange.hxx>
    #include <TextCursorHelper.hxx>
    #include <swevent.hxx>
    #include <doc.hxx>
    #include <unocrsr.hxx>
    #include <IMark.hxx>
    #include <IDocumentContentOperations.hxx>
    #include <IDocumentRedlineAccess.hxx>
    #include <IDocumentFieldsAccess.hxx>
    #include <IDocumentState.hxx>
    #include <unoprnms.hxx>
    #include <docsh.hxx>
    #include <swmodule.hxx>
    #include <swdll.hxx>
    #include <svl/hint.hxx>
    #include <svl/macitem.hxx>
    #include <editeng/acorrcfg.hxx>
    #include <comphelper/servicehelper.hxx>
    #include <cppuhelper/exc_hlp.hxx>
    #include <cppuhelper/supportsservice.hxx>

    #include <memory>

    using namespace ::com::sun::star;

    SwXAutoTextContainer::SwXAutoTextContainer()
    {
        pGlossaries = ::GetGlossaries();

    }

    SwXAutoTextContainer::~SwXAutoTextContainer()
    {

    }

    sal_Int32 SwXAutoTextContainer::getCount()
    {
        OSL_ENSURE(pGlossaries->GetGroupCnt() < static_cast<size_t>(SAL_MAX_INT32),
                   "SwXAutoTextContainer::getCount: too many items");
        return static_cast<sal_Int32>(pGlossaries->GetGroupCnt());
    }

    uno::Any SwXAutoTextContainer::getByIndex(sal_Int32 nIndex)
    {
        SolarMutexGuard aGuard;
        const size_t nCount = pGlossaries->GetGroupCnt();
        if ( nIndex < 0 || static_cast<size_t>(nIndex) >= nCount )
            throw lang::IndexOutOfBoundsException();
        return getByName(pGlossaries->GetGroupName( static_cast<size_t>(nIndex) ));
    }

    uno::Type SwXAutoTextContainer::getElementType()
    {
        return cppu::UnoType<text::XAutoTextGroup>::get();

    }

    sal_Bool SwXAutoTextContainer::hasElements()
    {
        // At least standard should always exists!
        return true;
    }

    uno::Any SwXAutoTextContainer::getByName(const OUString& GroupName)
    {
        SolarMutexGuard aGuard;

        uno::Reference< text::XAutoTextGroup > xGroup;
        if ( pGlossaries && hasByName( GroupName ) )    // group name already known?
            // true = create group if not already available
            xGroup = pGlossaries->GetAutoTextGroup( GroupName );

        if ( !xGroup.is() )
            throw container::NoSuchElementException();

        return makeAny( xGroup );
    }

    uno::Sequence< OUString > SwXAutoTextContainer::getElementNames()
    {
        SolarMutexGuard aGuard;
        const size_t nCount = pGlossaries->GetGroupCnt();
        OSL_ENSURE(nCount < static_cast<size_t>(SAL_MAX_INT32),
                   "SwXAutoTextContainer::getElementNames: too many groups");

        uno::Sequence< OUString > aGroupNames(static_cast<sal_Int32>(nCount));
        OUString *pArr = aGroupNames.getArray();

        for ( size_t i = 0; i < nCount; ++i )
        {
            // The names will be passed without a path extension.
            OUString sGroupName(pGlossaries->GetGroupName(i));
            pArr[i] = sGroupName.getToken(0, GLOS_DELIM);
        }
        return aGroupNames;
    }
    // Finds group names with or without path index.
    sal_Bool SwXAutoTextContainer::hasByName(const OUString& Name)
    {
        SolarMutexGuard aGuard;
        OUString sGroupName( pGlossaries->GetCompleteGroupName( Name ) );
        if(!sGroupName.isEmpty())
            return true;
        return false;
    }

    uno::Reference< text::XAutoTextGroup >  SwXAutoTextContainer::insertNewByName(
        const OUString& aGroupName)
    {
        SolarMutexGuard aGuard;
        if(hasByName(aGroupName))
            throw container::ElementExistException();
        //check for non-ASCII characters
        if(aGroupName.isEmpty())
        {
            lang::IllegalArgumentException aIllegal;
            aIllegal.Message = "group name must not be empty";
            throw aIllegal;
        }
        for(sal_Int32 nPos = 0; nPos < aGroupName.getLength(); nPos++)
        {
            sal_Unicode cChar = aGroupName[nPos];
            if (rtl::isAsciiAlphanumeric(cChar) ||
                (cChar == '_') ||
                (cChar == 0x20) ||
                (cChar == GLOS_DELIM) )
            {
                continue;
            }
            lang::IllegalArgumentException aIllegal;
            aIllegal.Message = "group name must contain a-z, A-z, '_', ' ' only";
            throw aIllegal;
        }
        OUString sGroup(aGroupName);
        if (sGroup.indexOf(GLOS_DELIM)<0)
        {
            sGroup += OUStringLiteral1(GLOS_DELIM) + "0";
        }
        pGlossaries->NewGroupDoc(sGroup, sGroup.getToken(0, GLOS_DELIM));

        uno::Reference< text::XAutoTextGroup > xGroup = pGlossaries->GetAutoTextGroup( sGroup );
        OSL_ENSURE( xGroup.is(), "SwXAutoTextContainer::insertNewByName: no UNO object created? How this?" );
            // We just inserted the group into the glossaries, so why doesn't it exist?

        return xGroup;
    }

    void SwXAutoTextContainer::removeByName(const OUString& aGroupName)
    {
        SolarMutexGuard aGuard;
        // At first find the name with path extension
        OUString sGroupName = pGlossaries->GetCompleteGroupName( aGroupName );
        if(sGroupName.isEmpty())
            throw container::NoSuchElementException();
        pGlossaries->DelGroupDoc(sGroupName);
    }

    OUString SwXAutoTextContainer::getImplementationName()
    {
        return OUString("SwXAutoTextContainer" );
    }

    sal_Bool SwXAutoTextContainer::supportsService(const OUString& rServiceName)
    {
        return cppu::supportsService(this, rServiceName);
    }

    uno::Sequence< OUString > SwXAutoTextContainer::getSupportedServiceNames()
    {
        OUString sService("com.sun.star.text.AutoTextContainer");
        const uno::Sequence< OUString > aSeq( &sService, 1 );
        return aSeq;
    }

    namespace
    {
        class theSwXAutoTextGroupUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXAutoTextGroupUnoTunnelId > {};
    }

    const uno::Sequence< sal_Int8 > & SwXAutoTextGroup::getUnoTunnelId()
    {
        return theSwXAutoTextGroupUnoTunnelId::get().getSeq();
    }

    sal_Int64 SAL_CALL SwXAutoTextGroup::getSomething( const uno::Sequence< sal_Int8 >& rId )
    {
        if( rId.getLength() == 16
            && 0 == memcmp( getUnoTunnelId().getConstArray(),
                                            rId.getConstArray(), 16 ) )
        {
                return sal::static_int_cast< sal_Int64 >( reinterpret_cast< sal_IntPtr >( this ));
        }
        return 0;
    }

    SwXAutoTextGroup::SwXAutoTextGroup(const OUString& rName,
                SwGlossaries*   pGlos) :
        pPropSet(aSwMapProvider.GetPropertySet(PROPERTY_MAP_AUTO_TEXT_GROUP)),
        pGlossaries(pGlos),
        sName(rName),
        m_sGroupName(rName)
    {
        OSL_ENSURE( -1 != rName.indexOf( GLOS_DELIM ),
            "SwXAutoTextGroup::SwXAutoTextGroup: to be constructed with a complete name only!" );
    }

    SwXAutoTextGroup::~SwXAutoTextGroup()
    {
    }

    uno::Sequence< OUString > SwXAutoTextGroup::getTitles()
    {
        SolarMutexGuard aGuard;
        std::unique_ptr<SwTextBlocks> pGlosGroup(pGlossaries ? pGlossaries->GetGroupDoc(m_sGroupName) : nullptr);
        if (!pGlosGroup || pGlosGroup->GetError())
            throw uno::RuntimeException();
        const sal_uInt16 nCount = pGlosGroup->GetCount();

        uno::Sequence< OUString > aEntryTitles(nCount);
        OUString *pArr = aEntryTitles.getArray();

        for ( sal_uInt16 i = 0; i < nCount; i++ )
            pArr[i] = pGlosGroup->GetLongName(i);
        return aEntryTitles;
    }

    void SwXAutoTextGroup::renameByName(const OUString& aElementName,
        const OUString& aNewElementName, const OUString& aNewElementTitle)
    {
        SolarMutexGuard aGuard;
        // throw exception only if the programmatic name is to be changed into an existing name
        if(aNewElementName != aElementName && hasByName(aNewElementName))
            throw container::ElementExistException();
        std::unique_ptr<SwTextBlocks> pGlosGroup(pGlossaries ? pGlossaries->GetGroupDoc(m_sGroupName) : nullptr);
        if(!pGlosGroup || pGlosGroup->GetError())
            throw uno::RuntimeException();

        sal_uInt16 nIdx = pGlosGroup->GetIndex( aElementName);
        if(USHRT_MAX == nIdx)
            throw lang::IllegalArgumentException();
        OUString aNewShort(aNewElementName);
        OUString aNewName(aNewElementTitle);
        sal_uInt16 nOldLongIdx = pGlosGroup->GetLongIndex( aNewShort );
        sal_uInt16 nOldIdx = pGlosGroup->GetIndex( aNewName );

        if( nIdx != USHRT_MAX &&
                (nOldLongIdx == USHRT_MAX || nOldLongIdx == nIdx )&&
                    (nOldIdx == USHRT_MAX || nOldIdx == nIdx ))
        {
            pGlosGroup->Rename( nIdx, &aNewShort, &aNewName );
            if(pGlosGroup->GetError() != ERRCODE_NONE)
                throw io::IOException();
        }

    }

    static bool lcl_CopySelToDoc( SwDoc* pInsDoc, OTextCursorHelper* pxCursor, SwXTextRange* pxRange)
    {
        OSL_ENSURE( pInsDoc, "no InsDoc");

        SwNodes& rNds = pInsDoc->GetNodes();

        SwNodeIndex aIdx( rNds.GetEndOfContent(), -1 );
        SwContentNode * pNd = aIdx.GetNode().GetContentNode();
        SwPosition aPos(aIdx, SwIndex(pNd, pNd ? pNd->Len() : 0));

        bool bRet = false;
        pInsDoc->getIDocumentFieldsAccess().LockExpFields();
        {
            SwDoc *const pDoc(pxCursor ? pxCursor->GetDoc() : &pxRange->GetDoc());
            SwPaM aPam(pDoc->GetNodes());
            SwPaM * pPam(nullptr);
            if(pxCursor)
            {
                pPam = pxCursor->GetPaM();
            }
            else
            {
                if (pxRange->GetPositions(aPam))
                {
                    pPam = & aPam;
                }
            }
            if (!pPam) { return false; }
            bRet = pDoc->getIDocumentContentOperations().CopyRange( *pPam, aPos, /*bCopyAll=*/false, /*bCheckPos=*/true ) || bRet;
        }

        pInsDoc->getIDocumentFieldsAccess().UnlockExpFields();
        if( !pInsDoc->getIDocumentFieldsAccess().IsExpFieldsLocked() )
            pInsDoc->getIDocumentFieldsAccess().UpdateExpFields(nullptr, true);

        return bRet;
    }

    uno::Reference< text::XAutoTextEntry >  SwXAutoTextGroup::insertNewByName(const OUString& aName,
            const OUString& aTitle, const uno::Reference< text::XTextRange > & xTextRange)
    {
        SolarMutexGuard aGuard;
        if(hasByName(aName))
            throw container::ElementExistException();
        if(!xTextRange.is())
            throw uno::RuntimeException();

        SwTextBlocks* pGlosGroup = pGlossaries ? pGlossaries->GetGroupDoc(m_sGroupName) : nullptr;
        const OUString& sShortName(aName);
        const OUString& sLongName(aTitle);
        if (pGlosGroup && !pGlosGroup->GetError())
        {
            uno::Reference<lang::XUnoTunnel> xRangeTunnel( xTextRange, uno::UNO_QUERY);
            SwXTextRange* pxRange = nullptr;
            OTextCursorHelper* pxCursor = nullptr;
            if(xRangeTunnel.is())
            {
                pxRange = reinterpret_cast<SwXTextRange*>(xRangeTunnel->getSomething(
                                        SwXTextRange::getUnoTunnelId()));
                pxCursor = reinterpret_cast<OTextCursorHelper*>(xRangeTunnel->getSomething(
                                        OTextCursorHelper::getUnoTunnelId()));
            }

            OUString sOnlyText;
            OUString* pOnlyText = nullptr;
            bool bNoAttr = !pxCursor && !pxRange;
            if(bNoAttr)
            {
                sOnlyText = xTextRange->getString();
                pOnlyText = &sOnlyText;
            }

            const SvxAutoCorrCfg& rCfg = SvxAutoCorrCfg::Get();

            SwDoc* pGDoc = pGlosGroup->GetDoc();

            // Until there is an option for that, delete base util::URL
            if(rCfg.IsSaveRelFile())
            {
                INetURLObject aTemp(pGlosGroup->GetFileName());
                pGlosGroup->SetBaseURL( aTemp.GetMainURL(INetURLObject::DecodeMechanism::NONE));
            }
            else
                pGlosGroup->SetBaseURL( OUString() );

            sal_uInt16 nRet = USHRT_MAX;
            if( pOnlyText )
                nRet = pGlosGroup->PutText( sShortName, sLongName, *pOnlyText );
            else
            {
                pGlosGroup->ClearDoc();
                if( pGlosGroup->BeginPutDoc( sShortName, sLongName ) )
                {
                    pGDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( RedlineFlags::DeleteRedlines );
                    lcl_CopySelToDoc( pGDoc, pxCursor, pxRange );
                    pGDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern(RedlineFlags::NONE);
                    nRet = pGlosGroup->PutDoc();
                }
            }

            if (nRet == USHRT_MAX)
            {
                throw uno::RuntimeException();
            }
        }
        delete pGlosGroup;

        uno::Reference< text::XAutoTextEntry > xEntry;

        try
        {
            xEntry = pGlossaries ?
                pGlossaries->GetAutoTextEntry( m_sGroupName, sName, sShortName ) :
                uno::Reference< text::XAutoTextEntry >();
            OSL_ENSURE( xEntry.is(), "SwXAutoTextGroup::insertNewByName: no UNO object created? How this?" );
                // we just inserted the entry into the group, so why doesn't it exist?
        }
        catch (const container::ElementExistException&)
        {
            throw;
        }
        catch (const uno::RuntimeException&)
        {
            throw;
        }
        catch (const uno::Exception&)
        {
            css::uno::Any anyEx = cppu::getCaughtException();
            throw css::lang::WrappedTargetRuntimeException(
                   "Error Getting AutoText!",
                   static_cast < OWeakObject * > ( this ),
                   anyEx );
        }

        return xEntry;
    }

    void SwXAutoTextGroup::removeByName(const OUString& aEntryName)
    {
        SolarMutexGuard aGuard;
        std::unique_ptr<SwTextBlocks> pGlosGroup(pGlossaries ? pGlossaries->GetGroupDoc(m_sGroupName) : nullptr);
        if(!pGlosGroup || pGlosGroup->GetError())
            throw container::NoSuchElementException();

        sal_uInt16 nIdx = pGlosGroup->GetIndex(aEntryName);
        if ( nIdx != USHRT_MAX )
            pGlosGroup->Delete(nIdx);
    }

    OUString SwXAutoTextGroup::getName()
    {
        SolarMutexGuard aGuard;
        return sName;
    }

    void SwXAutoTextGroup::setName(const OUString& rName)
    {
        SolarMutexGuard aGuard;
        if( !pGlossaries )
            throw uno::RuntimeException();

        sal_Int32 nNewDelimPos = rName.lastIndexOf( GLOS_DELIM );
        sal_Int32 nOldDelimPos = sName.lastIndexOf( GLOS_DELIM );

        OUString aNewSuffix;
        if (nNewDelimPos > -1)
            aNewSuffix = rName.copy( nNewDelimPos + 1 );
        OUString aOldSuffix;
        if (nOldDelimPos > -1)
            aOldSuffix = sName.copy( nOldDelimPos + 1 );

        sal_Int32 nNewNumeric = aNewSuffix.toInt32();
        sal_Int32 nOldNumeric = aOldSuffix.toInt32();

        OUString aNewPrefix( (nNewDelimPos > 1) ? rName.copy( 0, nNewDelimPos ) : rName );
        OUString aOldPrefix( (nOldDelimPos > 1) ? sName.copy( 0, nOldDelimPos ) : sName );

        if ( sName == rName ||
           ( nNewNumeric == nOldNumeric && aNewPrefix == aOldPrefix ) )
            return;
        OUString sNewGroup(rName);
        if (sNewGroup.indexOf(GLOS_DELIM)<0)
        {
            sNewGroup += OUStringLiteral1(GLOS_DELIM) + "0";
        }

        //the name must be saved, the group may be invalidated while in RenameGroupDoc()
        SwGlossaries* pTempGlossaries = pGlossaries;

        OUString sPreserveTitle( pGlossaries->GetGroupTitle( sName ) );
        if ( !pGlossaries->RenameGroupDoc( sName, sNewGroup, sPreserveTitle ) )
            throw uno::RuntimeException();
        sName = rName;
        m_sGroupName = sNewGroup;
        pGlossaries = pTempGlossaries;
    }

    sal_Int32 SwXAutoTextGroup::getCount()
    {
        SolarMutexGuard aGuard;
        std::unique_ptr<SwTextBlocks> pGlosGroup(pGlossaries ? pGlossaries->GetGroupDoc(m_sGroupName) : nullptr);
        if (!pGlosGroup || pGlosGroup->GetError())
            throw uno::RuntimeException();
        return static_cast<sal_Int32>(pGlosGroup->GetCount());
    }

    uno::Any SwXAutoTextGroup::getByIndex(sal_Int32 nIndex)
    {
        SolarMutexGuard aGuard;
        std::unique_ptr<SwTextBlocks> pGlosGroup(pGlossaries ? pGlossaries->GetGroupDoc(m_sGroupName) : nullptr);
        if (!pGlosGroup || pGlosGroup->GetError())
            throw uno::RuntimeException();
        const sal_uInt16 nCount = pGlosGroup->GetCount();
        if (nIndex < 0 || nIndex >= static_cast<sal_Int32>(nCount))
            throw lang::IndexOutOfBoundsException();
        return getByName(pGlosGroup->GetShortName(static_cast<sal_uInt16>(nIndex)));
    }

    uno::Type SwXAutoTextGroup::getElementType()
    {
        return cppu::UnoType<text::XAutoTextEntry>::get();

    }

    sal_Bool SwXAutoTextGroup::hasElements()
    {
        SolarMutexGuard aGuard;
        std::unique_ptr<SwTextBlocks> pGlosGroup(pGlossaries ? pGlossaries->GetGroupDoc(m_sGroupName) : nullptr);
        if (!pGlosGroup || pGlosGroup->GetError())
            throw uno::RuntimeException();
        return pGlosGroup->GetCount() > 0;

    }

    uno::Any SwXAutoTextGroup::getByName(const OUString& _rName)
    {
        SolarMutexGuard aGuard;
        uno::Reference< text::XAutoTextEntry > xEntry = pGlossaries->GetAutoTextEntry( m_sGroupName, sName, _rName );
        OSL_ENSURE( xEntry.is(), "SwXAutoTextGroup::getByName: GetAutoTextEntry is fractious!" );
            // we told it to create the object, so why didn't it?
        return makeAny( xEntry );
    }

    uno::Sequence< OUString > SwXAutoTextGroup::getElementNames()
    {
        SolarMutexGuard aGuard;
        std::unique_ptr<SwTextBlocks> pGlosGroup(pGlossaries ? pGlossaries->GetGroupDoc(m_sGroupName) : nullptr);
        if (!pGlosGroup || pGlosGroup->GetError())
            throw uno::RuntimeException();

        const sal_uInt16 nCount = pGlosGroup->GetCount();
        uno::Sequence< OUString > aEntryNames(nCount);
        OUString *pArr = aEntryNames.getArray();

        for ( sal_uInt16 i = 0; i < nCount; i++ )
            pArr[i] = pGlosGroup->GetShortName(i);
        return aEntryNames;
    }

    sal_Bool SwXAutoTextGroup::hasByName(const OUString& rName)
    {
        SolarMutexGuard aGuard;
        bool bRet = false;
        std::unique_ptr<SwTextBlocks> pGlosGroup(pGlossaries ? pGlossaries->GetGroupDoc(m_sGroupName) : nullptr);
        if (!pGlosGroup || pGlosGroup->GetError())
            throw uno::RuntimeException();

        const sal_uInt16 nCount = pGlosGroup->GetCount();
        for( sal_uInt16 i = 0; i < nCount; ++i )
        {
            OUString sCompare(pGlosGroup->GetShortName(i));
            if(sCompare.equalsIgnoreAsciiCase(rName))
            {
                bRet = true;
                break;
            }
        }
        return bRet;
    }

    uno::Reference< beans::XPropertySetInfo >  SwXAutoTextGroup::getPropertySetInfo()
    {
        static uno::Reference< beans::XPropertySetInfo >  xRet = pPropSet->getPropertySetInfo();
        return xRet;
    }

    void SwXAutoTextGroup::setPropertyValue(
        const OUString& rPropertyName, const uno::Any& aValue)
    {
        SolarMutexGuard aGuard;
        const SfxItemPropertySimpleEntry*   pEntry = pPropSet->getPropertyMap().getByName( rPropertyName );

        if(!pEntry)
            throw beans::UnknownPropertyException();

        std::unique_ptr<SwTextBlocks> pGlosGroup(pGlossaries ? pGlossaries->GetGroupDoc(m_sGroupName) : nullptr);
        if(!pGlosGroup || pGlosGroup->GetError())
            throw uno::RuntimeException();
        switch(pEntry->nWID)
        {
            case  WID_GROUP_TITLE:
            {
                OUString sNewTitle;
                aValue >>= sNewTitle;
                if(sNewTitle.isEmpty())
                    throw lang::IllegalArgumentException();
                bool bChanged = sNewTitle != pGlosGroup->GetName();
                pGlosGroup->SetName(sNewTitle);
                if(bChanged && HasGlossaryList())
                    GetGlossaryList()->ClearGroups();
            }
            break;
        }
    }

    uno::Any SwXAutoTextGroup::getPropertyValue(const OUString& rPropertyName)
    {
        SolarMutexGuard aGuard;
        const SfxItemPropertySimpleEntry*   pEntry = pPropSet->getPropertyMap().getByName( rPropertyName);

        if(!pEntry)
            throw beans::UnknownPropertyException();
        std::unique_ptr<SwTextBlocks> pGlosGroup(pGlossaries ? pGlossaries->GetGroupDoc(m_sGroupName) : nullptr);
        if(!pGlosGroup  || pGlosGroup->GetError())
            throw uno::RuntimeException();

        uno::Any aAny;
        switch(pEntry->nWID)
        {
            case  WID_GROUP_PATH:
                aAny <<= pGlosGroup->GetFileName();
            break;
            case  WID_GROUP_TITLE:
                aAny <<= pGlosGroup->GetName();
            break;
        }
        return aAny;
    }

    void SwXAutoTextGroup::addPropertyChangeListener(
        const OUString& /*PropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*aListener*/)
    {
    }

    void SwXAutoTextGroup::removePropertyChangeListener(
        const OUString& /*PropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*aListener*/)
    {
    }

    void SwXAutoTextGroup::addVetoableChangeListener(
        const OUString& /*PropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*aListener*/)
    {
    }

    void SwXAutoTextGroup::removeVetoableChangeListener(
        const OUString& /*PropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*aListener*/)
    {
    }

    void SwXAutoTextGroup::Invalidate()
    {
        pGlossaries = nullptr;
        sName.clear();
        m_sGroupName.clear();
    }

    OUString SwXAutoTextGroup::getImplementationName()
    {
        return OUString("SwXAutoTextGroup");
    }

    sal_Bool SwXAutoTextGroup::supportsService(const OUString& rServiceName)
    {
        return cppu::supportsService(this, rServiceName);
    }

    uno::Sequence< OUString > SwXAutoTextGroup::getSupportedServiceNames()
    {
        uno::Sequence<OUString> aRet { "com.sun.star.text.AutoTextGroup" };
        return aRet;
    }

    namespace
    {
        class theSwXAutoTextEntryUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXAutoTextEntryUnoTunnelId > {};
    }

    const uno::Sequence< sal_Int8 > & SwXAutoTextEntry::getUnoTunnelId()
    {
        return theSwXAutoTextEntryUnoTunnelId::get().getSeq();
    }

    sal_Int64 SAL_CALL SwXAutoTextEntry::getSomething( const uno::Sequence< sal_Int8 >& rId )
    {
        if( rId.getLength() == 16
            && 0 == memcmp( getUnoTunnelId().getConstArray(),
                                            rId.getConstArray(), 16 ) )
        {
                return sal::static_int_cast< sal_Int64 >( reinterpret_cast< sal_IntPtr >( this ));
        }
        return 0;
    }

    SwXAutoTextEntry::SwXAutoTextEntry(SwGlossaries* pGlss, const OUString& rGroupName,
                                                const OUString& rEntryName) :
        WeakComponentImplHelper(m_aMutex),
        pGlossaries(pGlss),
        sGroupName(rGroupName),
        sEntryName(rEntryName)
    {
    }

    SwXAutoTextEntry::~SwXAutoTextEntry()
    {
        SolarMutexGuard aGuard;

        // ensure that any pending modifications are written
        implFlushDocument( true );
    }

    void SwXAutoTextEntry::implFlushDocument( bool _bCloseDoc )
    {
        if ( xDocSh.is() )
        {
            if ( xDocSh->GetDoc()->getIDocumentState().IsModified () )
                xDocSh->Save();

            if ( _bCloseDoc )
            {
                // stop listening at the document
                EndListening( *xDocSh );

                xDocSh->DoClose();
                xDocSh.clear();
            }
        }
    }

    void SwXAutoTextEntry::Notify( SfxBroadcaster& _rBC, const SfxHint& _rHint )
    {
        if ( &_rBC == xDocSh.get() )
        {   // it's our document
            if (const SfxEventHint* pEventHint = dynamic_cast<const SfxEventHint*>(&_rHint))
            {
                if (SfxEventHintId::PrepareCloseDoc == pEventHint->GetEventId())
                {
                    implFlushDocument();
                    mxBodyText.clear();
                    EndListening( *xDocSh );
                    xDocSh.clear();
                }
            }
            else
            {
                if ( SfxHintId::Deinitializing == _rHint.GetId() )
                {
                    // our document is dying (possibly because we're shuting down, and the document was notified
                    // earlier than we are?)
                    // stop listening at the docu
                    EndListening( *xDocSh );
                    // and release our reference
                    xDocSh.clear();
                }
            }
        }
    }

    void SwXAutoTextEntry::GetBodyText ()
    {
        SolarMutexGuard aGuard;

        xDocSh = pGlossaries->EditGroupDoc ( sGroupName, sEntryName, false );
        OSL_ENSURE( xDocSh.is(), "SwXAutoTextEntry::GetBodyText: unexpected: no doc returned by EditGroupDoc!" );

        // start listening at the document
        StartListening( *xDocSh );

        mxBodyText = new SwXBodyText ( xDocSh->GetDoc() );
    }

    void SwXAutoTextEntry::disposing()
    {
        SolarMutexGuard g;
        implFlushDocument(true);
    }

    uno::Reference< text::XTextCursor >  SwXAutoTextEntry::createTextCursor()
    {
        SolarMutexGuard aGuard;
        EnsureBodyText();
        return mxBodyText->createTextCursor();
    }

    uno::Reference< text::XTextCursor >  SwXAutoTextEntry::createTextCursorByRange(
        const uno::Reference< text::XTextRange > & aTextPosition)
    {
        SolarMutexGuard aGuard;
        EnsureBodyText();
        return mxBodyText->createTextCursorByRange ( aTextPosition );
    }

    void SwXAutoTextEntry::insertString(const uno::Reference< text::XTextRange > & xRange, const OUString& aString, sal_Bool bAbsorb)
    {
        SolarMutexGuard aGuard;
        EnsureBodyText();
        mxBodyText->insertString ( xRange, aString, bAbsorb );
    }

    void SwXAutoTextEntry::insertControlCharacter(const uno::Reference< text::XTextRange > & xRange,
        sal_Int16 nControlCharacter, sal_Bool bAbsorb)
    {
        SolarMutexGuard aGuard;
        EnsureBodyText();
        mxBodyText->insertControlCharacter ( xRange, nControlCharacter, bAbsorb );
    }

    void SwXAutoTextEntry::insertTextContent(
        const uno::Reference< text::XTextRange > & xRange,
        const uno::Reference< text::XTextContent > & xContent, sal_Bool bAbsorb)
    {
        SolarMutexGuard aGuard;
        EnsureBodyText();
        mxBodyText->insertTextContent ( xRange, xContent, bAbsorb );
    }

    void SwXAutoTextEntry::removeTextContent(
        const uno::Reference< text::XTextContent > & xContent)
    {
        SolarMutexGuard aGuard;
        EnsureBodyText();
        mxBodyText->removeTextContent ( xContent );
    }

    uno::Reference< text::XText >  SwXAutoTextEntry::getText()
    {
        SolarMutexGuard aGuard;
        uno::Reference< text::XText >  xRet =  static_cast<text::XText*>(this);
        return xRet;
    }

    uno::Reference< text::XTextRange >  SwXAutoTextEntry::getStart()
    {
        SolarMutexGuard aGuard;
        EnsureBodyText();
        return mxBodyText->getStart();
    }

    uno::Reference< text::XTextRange >  SwXAutoTextEntry::getEnd()
    {
        SolarMutexGuard aGuard;
        EnsureBodyText();
        return mxBodyText->getEnd();
    }

    OUString SwXAutoTextEntry::getString()
    {
        SolarMutexGuard aGuard;
        EnsureBodyText();
        return mxBodyText->getString();
    }

    void SwXAutoTextEntry::setString(const OUString& aString)
    {
        SolarMutexGuard aGuard;
        EnsureBodyText();
        mxBodyText->setString( aString );
    }

    void SwXAutoTextEntry::applyTo(const uno::Reference< text::XTextRange > & xTextRange)
    {
        SolarMutexGuard aGuard;

        // ensure that any pending modifications are written
        // reason is that we're holding the _copy_ of the auto text, while the real auto text
        // is stored somewhere. And below, we're not working with our copy, but only tell the target
        // TextRange to work with the stored version.
        // #96380# - 2003-03-03 - fs@openoffice.org
        implFlushDocument();
            // TODO: think about if we should pass "true" here
            // The difference would be that when the next modification is made to this instance here, then
            // we would be forced to open the document again, instead of working on our current copy.
            // This means that we would reflect any changes which were done to the AutoText by foreign instances
            // in the meantime

        uno::Reference<lang::XUnoTunnel> xTunnel( xTextRange, uno::UNO_QUERY);
        SwXTextRange* pRange = nullptr;
        OTextCursorHelper* pCursor = nullptr;
        SwXText *pText = nullptr;

        if(xTunnel.is())
        {
            pRange = reinterpret_cast < SwXTextRange* >
                    ( xTunnel->getSomething( SwXTextRange::getUnoTunnelId() ) );
            pCursor = reinterpret_cast < OTextCursorHelper*>
                    ( xTunnel->getSomething( OTextCursorHelper::getUnoTunnelId() ) );
            pText = reinterpret_cast < SwXText* >
                    ( xTunnel->getSomething( SwXText::getUnoTunnelId() ) );
        }

        SwDoc* pDoc = nullptr;
        if (pRange)
            pDoc = &pRange->GetDoc();
        else if ( pCursor )
            pDoc = pCursor->GetDoc();
        else if ( pText && pText->GetDoc() )
        {
            xTunnel.set(pText->getStart(), uno::UNO_QUERY);
            if (xTunnel.is())
            {
                pCursor = reinterpret_cast < OTextCursorHelper* >
                    ( xTunnel->getSomething( OTextCursorHelper::getUnoTunnelId() ) );
                if (pCursor)
                    pDoc = pText->GetDoc();
            }
        }

        if(!pDoc)
            throw uno::RuntimeException();

        SwPaM InsertPaM(pDoc->GetNodes());
        if (pRange)
        {
            if (!pRange->GetPositions(InsertPaM))
            {
                throw uno::RuntimeException();
            }
        }
        else
        {
            InsertPaM = *pCursor->GetPaM();
        }

        std::unique_ptr<SwTextBlocks> pBlock(pGlossaries->GetGroupDoc(sGroupName));
        const bool bResult = pBlock.get() && !pBlock->GetError()
                        && pDoc->InsertGlossary( *pBlock, sEntryName, InsertPaM);

        if(!bResult)
            throw uno::RuntimeException();
    }

    OUString SwXAutoTextEntry::getImplementationName()
    {
        return OUString("SwXAutoTextEntry");
    }

    sal_Bool SwXAutoTextEntry::supportsService(const OUString& rServiceName)
    {
        return cppu::supportsService(this, rServiceName);
    }

    uno::Sequence< OUString > SwXAutoTextEntry::getSupportedServiceNames()
    {
        uno::Sequence<OUString> aRet { "com.sun.star.text.AutoTextEntry" };
        return aRet;
    }

    uno::Reference< container::XNameReplace > SwXAutoTextEntry::getEvents()
    {
        return new SwAutoTextEventDescriptor( *this );
    }

    const struct SvEventDescription aAutotextEvents[] =
    {
        { SvMacroItemId::SwStartInsGlossary,  "OnInsertStart" },
        { SvMacroItemId::SwEndInsGlossary,    "OnInsertDone" },
        { SvMacroItemId::NONE, nullptr }
    };

    SwAutoTextEventDescriptor::SwAutoTextEventDescriptor(
        SwXAutoTextEntry& rAutoText ) :
            SvBaseEventDescriptor(aAutotextEvents),
            sSwAutoTextEventDescriptor(
                "SwAutoTextEventDescriptor"),
            rAutoTextEntry(rAutoText)
    {
    }

    SwAutoTextEventDescriptor::~SwAutoTextEventDescriptor()
    {
    }

    OUString SwAutoTextEventDescriptor::getImplementationName()
    {
        return sSwAutoTextEventDescriptor;
    }

    void SwAutoTextEventDescriptor::replaceByName(
        const SvMacroItemId nEvent,
        const SvxMacro& rMacro)
    {
        OSL_ENSURE( nullptr != rAutoTextEntry.GetGlossaries(),
                    "Strangely enough, the AutoText vanished!" );
        OSL_ENSURE( (nEvent == SvMacroItemId::SwEndInsGlossary) ||
                    (nEvent == SvMacroItemId::SwStartInsGlossary) ,
                    "Unknown event ID" );

        SwGlossaries *const pGlossaries =
            const_cast<SwGlossaries*>(rAutoTextEntry.GetGlossaries());
        std::unique_ptr<SwTextBlocks> pBlocks(
            pGlossaries->GetGroupDoc( rAutoTextEntry.GetGroupName() ));
        OSL_ENSURE( pBlocks,
                    "can't get autotext group; SwAutoTextEntry has illegal name?");

        if( pBlocks && !pBlocks->GetError())
        {
            sal_uInt16 nIndex = pBlocks->GetIndex( rAutoTextEntry.GetEntryName() );
            if( nIndex != USHRT_MAX )
            {
                SvxMacroTableDtor aMacroTable;
                if( pBlocks->GetMacroTable( nIndex, aMacroTable ) )
                {
                    aMacroTable.Insert( nEvent, rMacro );
                    pBlocks->SetMacroTable( nIndex, aMacroTable );
                }
            }
        }
        // else: ignore
    }

    void SwAutoTextEventDescriptor::getByName(
        SvxMacro& rMacro,
        const SvMacroItemId nEvent )
    {
        OSL_ENSURE( nullptr != rAutoTextEntry.GetGlossaries(), "no AutoText" );
        OSL_ENSURE( (nEvent == SvMacroItemId::SwEndInsGlossary) ||
                    (nEvent == SvMacroItemId::SwStartInsGlossary) ,
                    "Unknown event ID" );

        SwGlossaries *const pGlossaries =
            const_cast<SwGlossaries*>(rAutoTextEntry.GetGlossaries());
        std::unique_ptr<SwTextBlocks> pBlocks(
            pGlossaries->GetGroupDoc( rAutoTextEntry.GetGroupName() ));
        OSL_ENSURE( pBlocks,
                    "can't get autotext group; SwAutoTextEntry has illegal name?");

        // return empty macro, unless macro is found
        OUString sEmptyStr;
        SvxMacro aEmptyMacro(sEmptyStr, sEmptyStr);
        rMacro = aEmptyMacro;

        if ( pBlocks &&  !pBlocks->GetError())
        {
            sal_uInt16 nIndex = pBlocks->GetIndex( rAutoTextEntry.GetEntryName() );
            if( nIndex != USHRT_MAX )
            {
                SvxMacroTableDtor aMacroTable;
                if( pBlocks->GetMacroTable( nIndex, aMacroTable ) )
                {
                    SvxMacro *pMacro = aMacroTable.Get( nEvent );
                    if( pMacro )
                        rMacro = *pMacro;
                }
            }
        }
    }

    extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
    SwXAutoTextContainer_get_implementation(css::uno::XComponentContext*,
            css::uno::Sequence<css::uno::Any> const &)
    {
        //the module may not be loaded
        SolarMutexGuard aGuard;
        SwGlobals::ensure();
        return cppu::acquire(new SwXAutoTextContainer());
    }

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

↑ V560 A part of conditional expression is always true: nIdx != 0xffff.