/* -*- 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 <memory>
#include <unosection.hxx>
#include <unotext.hxx>

#include <com/sun/star/beans/PropertyAttribute.hpp>
#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
#include <com/sun/star/text/SectionFileLink.hpp>

#include <comphelper/interfacecontainer4.hxx>
#include <cppuhelper/exc_hlp.hxx>
#include <cppuhelper/supportsservice.hxx>

#include <cmdid.h>
#include <hintids.hxx>
#include <svl/urihelper.hxx>
#include <svl/listener.hxx>
#include <editeng/brushitem.hxx>
#include <editeng/xmlcnitm.hxx>
#include <sfx2/linkmgr.hxx>
#include <sfx2/lnkbase.hxx>
#include <osl/diagnose.h>
#include <vcl/svapp.hxx>
#include <fmtclds.hxx>
#include <unotextrange.hxx>
#include <TextCursorHelper.hxx>
#include <unoport.hxx>
#include <redline.hxx>
#include <unomap.hxx>
#include <section.hxx>
#include <doc.hxx>
#include <IDocumentRedlineAccess.hxx>
#include <IDocumentUndoRedo.hxx>
#include <docsh.hxx>
#include <sfx2/docfile.hxx>
#include <docary.hxx>
#include <swundo.hxx>
#include <tox.hxx>
#include <unoidx.hxx>
#include <doctxm.hxx>
#include <fmtftntx.hxx>
#include <fmtclbl.hxx>
#include <editeng/frmdiritem.hxx>
#include <fmtcntnt.hxx>
#include <editeng/lrspitem.hxx>
#include <comphelper/servicehelper.hxx>
#include <comphelper/string.hxx>
#include <o3tl/string_view.hxx>

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

namespace {

struct SwTextSectionProperties_Impl
{
    uno::Sequence<sal_Int8> m_Password;
    OUString  m_sCondition;
    OUString  m_sLinkFileName;
    OUString  m_sSectionFilter;
    OUString  m_sSectionRegion;

    std::unique_ptr<SwFormatCol>                 m_pColItem;
    std::unique_ptr<SvxBrushItem>             m_pBrushItem;
    std::unique_ptr<SwFormatFootnoteAtTextEnd>         m_pFootnoteItem;
    std::unique_ptr<SwFormatEndAtTextEnd>         m_pEndItem;
    std::unique_ptr<SvXMLAttrContainerItem> m_pXMLAttr;
    std::unique_ptr<SwFormatNoBalancedColumns> m_pNoBalanceItem;
    std::unique_ptr<SvxFrameDirectionItem>    m_pFrameDirItem;
    std::unique_ptr<SvxLRSpaceItem>           m_pLRSpaceItem;

    bool m_bDDE;
    bool m_bHidden;
    bool m_bCondHidden;
    bool m_bProtect;
    bool m_bEditInReadonly;
    bool m_bUpdateType;

    SwTextSectionProperties_Impl()
        : m_bDDE(false)
        , m_bHidden(false)
        , m_bCondHidden(false)
        , m_bProtect(false)
        , m_bEditInReadonly(false)
        , m_bUpdateType(true)
    {
    }

};

}

class SwXTextSection::Impl
    : public SvtListener
{
public:
    SwXTextSection &            m_rThis;
    unotools::WeakReference<SwXTextSection> m_wThis;
    const SfxItemPropertySet &  m_rPropSet;
    std::mutex m_Mutex; // just for OInterfaceContainerHelper4
    ::comphelper::OInterfaceContainerHelper4<css::lang::XEventListener> m_EventListeners;
    const bool                  m_bIndexHeader;
    bool                        m_bIsDescriptor;
    OUString             m_sName;
    std::unique_ptr<SwTextSectionProperties_Impl> m_pProps;
    SwSectionFormat* m_pFormat;

    Impl(   SwXTextSection& rThis,
            SwSectionFormat* const pFormat, const bool bIndexHeader)
        : m_rThis(rThis)
        , m_rPropSet(*aSwMapProvider.GetPropertySet(PROPERTY_MAP_SECTION))
        , m_bIndexHeader(bIndexHeader)
        , m_bIsDescriptor(nullptr == pFormat)
        , m_pProps(pFormat ? nullptr : new SwTextSectionProperties_Impl())
        , m_pFormat(pFormat)
    {
        if(m_pFormat)
            StartListening(m_pFormat->GetNotifier());
    }

    void Attach(SwSectionFormat* pFormat)
    {
        EndListeningAll();
        StartListening(pFormat->GetNotifier());
        m_pFormat = pFormat;
    }

    SwSectionFormat* GetSectionFormat() const
        { return m_pFormat; }

    SwSectionFormat & GetSectionFormatOrThrow() const {
        SwSectionFormat *const pFormat( GetSectionFormat() );
        if (!pFormat) {
            throw uno::RuntimeException(u"SwXTextSection: disposed or invalid"_ustr, nullptr);
        }
        return *pFormat;
    }

    /// @throws beans::UnknownPropertyException
    /// @throws beans::PropertyVetoException,
    /// @throws lang::IllegalArgumentException
    /// @throws lang::WrappedTargetException,
    /// @throws uno::RuntimeException
    void SetPropertyValues_Impl(
            const uno::Sequence< OUString >& rPropertyNames,
            const uno::Sequence< uno::Any >& aValues);
    /// @throws beans::UnknownPropertyException
    /// @throws lang::WrappedTargetException,
    /// @throws uno::RuntimeException
    uno::Sequence< uno::Any >
        GetPropertyValues_Impl(
            const uno::Sequence< OUString >& rPropertyNames);
    virtual void Notify(const SfxHint& rHint) override;
};

void SwXTextSection::Impl::Notify(const SfxHint& rHint)
{
    if(rHint.GetId() == SfxHintId::Dying)
    {
        m_pFormat = nullptr;
        uno::Reference<uno::XInterface> const xThis(m_wThis);
        if (!xThis.is())
        {   // fdo#72695: if UNO object is already dead, don't revive it with event
            return;
        }
        lang::EventObject const ev(xThis);
        std::unique_lock aGuard(m_Mutex);
        m_EventListeners.disposeAndClear(aGuard, ev);
    }
}

SwSectionFormat * SwXTextSection::GetFormat() const
{
    return m_pImpl->GetSectionFormat();
}

rtl::Reference< SwXTextSection >
SwXTextSection::CreateXTextSection(
        SwSectionFormat *const pFormat, const bool bIndexHeader)
{
    // re-use existing SwXTextSection
    // #i105557#: do not iterate over the registered clients: race condition
    rtl::Reference< SwXTextSection > xSection;
    if (pFormat)
    {
        xSection = pFormat->GetXTextSection();
    }
    if ( !xSection.is() )
    {
        rtl::Reference<SwXTextSection> pNew = new SwXTextSection(pFormat, bIndexHeader);
        xSection = pNew;
        if (pFormat)
        {
            pFormat->SetXTextSection(xSection);
        }
        // need a permanent Reference to initialize m_wThis
        pNew->m_pImpl->m_wThis = xSection.get();
    }
    return xSection;
}

SwXSection::~SwXSection() {}

SwXTextSection::SwXTextSection(
        SwSectionFormat *const pFormat, const bool bIndexHeader)
    : m_pImpl( new SwXTextSection::Impl(*this, pFormat, bIndexHeader) )
{
}

SwXTextSection::~SwXTextSection()
{
}

uno::Reference< text::XTextSection > SAL_CALL
SwXTextSection::getParentSection()
{
    SolarMutexGuard aGuard;

    SwSectionFormat & rSectionFormat( m_pImpl->GetSectionFormatOrThrow() );

    SwSectionFormat *const pParentFormat = rSectionFormat.GetParent();
    if (!pParentFormat)
        return nullptr;
    return CreateXTextSection(pParentFormat);
}

uno::Sequence< uno::Reference< text::XTextSection > > SAL_CALL
SwXTextSection::getChildSections()
{
    SolarMutexGuard aGuard;

    SwSectionFormat & rSectionFormat( m_pImpl->GetSectionFormatOrThrow() );

    SwSections aChildren;
    rSectionFormat.GetChildSections(aChildren, SectionSort::Not, false);
    uno::Sequence<uno::Reference<text::XTextSection> > aSeq(aChildren.size());
    uno::Reference< text::XTextSection > * pArray = aSeq.getArray();
    for (size_t i = 0; i < aChildren.size(); ++i)
    {
        SwSectionFormat *const pChild = aChildren[i]->GetFormat();
        pArray[i] = CreateXTextSection(pChild);
    }
    return aSeq;
}

void SAL_CALL
SwXTextSection::attach(const uno::Reference< text::XTextRange > & xTextRange)
{
    SolarMutexGuard g;

    if (!m_pImpl->m_bIsDescriptor)
    {
        throw uno::RuntimeException();
    }

    SwXTextRange* pRange = dynamic_cast<SwXTextRange*>(xTextRange.get());
    OTextCursorHelper* pCursor = dynamic_cast<OTextCursorHelper*>(xTextRange.get());

    SwDoc *const pDoc =
        pRange ? &pRange->GetDoc() : (pCursor ? pCursor->GetDoc() : nullptr);
    if (!pDoc)
    {
        throw lang::IllegalArgumentException();
    }

    SwUnoInternalPaM aPam(*pDoc);
    // this has to return true now
    ::sw::XTextRangeToSwPaM(aPam, xTextRange);
    UnoActionContext aCont(pDoc);
    pDoc->GetIDocumentUndoRedo().StartUndo( SwUndoId::INSSECTION, nullptr );

    if (m_pImpl->m_sName.isEmpty())
    {
        m_pImpl->m_sName = "TextSection";
    }
    SectionType eType(SectionType::FileLink);
    if( m_pImpl->m_pProps->m_bDDE )
        eType = SectionType::DdeLink;
    else if( m_pImpl->m_pProps->m_sLinkFileName.isEmpty() && m_pImpl->m_pProps->m_sSectionRegion.isEmpty() )
        eType = SectionType::Content;
    // index header section?
    if (m_pImpl->m_bIndexHeader)
    {
        // caller wants an index header section, but will only
        // give him one if a) we are inside an index, and b) said
        // index doesn't yet have a header section.
        const SwTOXBase* pBase = SwDoc::GetCurTOX(*aPam.Start());

        // are we inside an index?
        if (pBase)
        {
            // get all child sections
            SwSections aSectionsArr;
            static_cast<const SwTOXBaseSection*>(pBase)->GetFormat()->
                GetChildSections(aSectionsArr);

            // and search for current header section
            const size_t nCount = aSectionsArr.size();
            bool bHeaderPresent = false;
            for(size_t i = 0; i < nCount; ++i)
            {
                if (aSectionsArr[i]->GetType() == SectionType::ToxHeader)
                    bHeaderPresent = true;
            }
            if (! bHeaderPresent)
            {
                eType = SectionType::ToxHeader;
            }
        }
    }

    SwSectionData aSect(eType, UIName(pDoc->GetUniqueSectionName(&m_pImpl->m_sName)));
    aSect.SetCondition(m_pImpl->m_pProps->m_sCondition);
    aSect.SetLinkFileName(m_pImpl->m_pProps->m_sLinkFileName +
        OUStringChar(sfx2::cTokenSeparator) +
        m_pImpl->m_pProps->m_sSectionFilter +
        OUStringChar(sfx2::cTokenSeparator) +
        m_pImpl->m_pProps->m_sSectionRegion);

    aSect.SetHidden(m_pImpl->m_pProps->m_bHidden);
    aSect.SetProtectFlag(m_pImpl->m_pProps->m_bProtect);
    aSect.SetEditInReadonlyFlag(m_pImpl->m_pProps->m_bEditInReadonly);

    SfxItemSetFixed<
            RES_LR_SPACE, RES_LR_SPACE,
            RES_BACKGROUND, RES_BACKGROUND,
            RES_COL, RES_COL,
            RES_FTN_AT_TXTEND, RES_FRAMEDIR,
            RES_UNKNOWNATR_CONTAINER,RES_UNKNOWNATR_CONTAINER>
        aSet(pDoc->GetAttrPool());
    if (m_pImpl->m_pProps->m_pBrushItem)
    {
        aSet.Put(*m_pImpl->m_pProps->m_pBrushItem);
    }
    if (m_pImpl->m_pProps->m_pColItem)
    {
        aSet.Put(*m_pImpl->m_pProps->m_pColItem);
    }
    if (m_pImpl->m_pProps->m_pFootnoteItem)
    {
        aSet.Put(*m_pImpl->m_pProps->m_pFootnoteItem);
    }
    if (m_pImpl->m_pProps->m_pEndItem)
    {
        aSet.Put(*m_pImpl->m_pProps->m_pEndItem);
    }
    if (m_pImpl->m_pProps->m_pXMLAttr)
    {
        aSet.Put(*m_pImpl->m_pProps->m_pXMLAttr);
    }
    if (m_pImpl->m_pProps->m_pNoBalanceItem)
    {
        aSet.Put(*m_pImpl->m_pProps->m_pNoBalanceItem);
    }
    if (m_pImpl->m_pProps->m_pFrameDirItem)
    {
        aSet.Put(*m_pImpl->m_pProps->m_pFrameDirItem);
    }
    if (m_pImpl->m_pProps->m_pLRSpaceItem)
    {
        aSet.Put(*m_pImpl->m_pProps->m_pLRSpaceItem);
    }
    // section password
    if (m_pImpl->m_pProps->m_Password.hasElements())
    {
        aSect.SetPassword(m_pImpl->m_pProps->m_Password);
    }

    SwSection *const pRet =
        pDoc->InsertSwSection( aPam, aSect, nullptr, aSet.Count() ? &aSet : nullptr );
    if (!pRet) // fdo#42450 text range could partially overlap existing section
    {
        // shouldn't have created an undo object yet
        pDoc->GetIDocumentUndoRedo().EndUndo( SwUndoId::INSSECTION, nullptr );
        throw lang::IllegalArgumentException(
                u"SwXTextSection::attach(): invalid TextRange"_ustr,
                getXWeak(), 0);
    }
    m_pImpl->Attach(pRet->GetFormat());
    pRet->GetFormat()->SetXObject(getXWeak());

    // XML import must hide sections depending on their old
    //         condition status
    if (!m_pImpl->m_pProps->m_sCondition.isEmpty())
    {
        pRet->SetCondHidden(m_pImpl->m_pProps->m_bCondHidden);
    }

    // set update type if DDE link (and connect, if necessary)
    if (m_pImpl->m_pProps->m_bDDE)
    {
        if (! pRet->IsConnected())
        {
            pRet->CreateLink(LinkCreateType::Connect);
        }
        pRet->SetUpdateType( m_pImpl->m_pProps->m_bUpdateType ?
                SfxLinkUpdateMode::ALWAYS : SfxLinkUpdateMode::ONCALL );
    }

    // end the Undo bracketing here
    pDoc->GetIDocumentUndoRedo().EndUndo( SwUndoId::INSSECTION, nullptr );
    m_pImpl->m_pProps.reset();
    m_pImpl->m_bIsDescriptor = false;
}

uno::Reference< text::XTextRange > SAL_CALL
SwXTextSection::getAnchor()
{
    SolarMutexGuard aGuard;

    rtl::Reference<SwXTextRange> xRet;
    SwSectionFormat *const pSectFormat = m_pImpl->GetSectionFormat();
    if(pSectFormat)
    {
        const SwNodeIndex* pIdx;
        if( nullptr != ( pSectFormat->GetSection() ) &&
            nullptr != ( pIdx = pSectFormat->GetContent().GetContentIdx() ) &&
            pIdx->GetNode().GetNodes().IsDocNodes() )
        {
            bool isMoveIntoTable(false);
            SwPaM aPaM(*pIdx);
            aPaM.Move( fnMoveForward, GoInContent );
            assert(pIdx->GetNode().IsSectionNode());
            if (aPaM.GetPoint()->GetNode().FindTableNode() != pIdx->GetNode().FindTableNode()
                || aPaM.GetPoint()->GetNode().FindSectionNode() != &pIdx->GetNode())
            {
                isMoveIntoTable = true;
            }

            const SwEndNode* pEndNode = pIdx->GetNode().EndOfSectionNode();
            SwPaM aEnd(*pEndNode);
            aEnd.Move( fnMoveBackward, GoInContent );
            if (aEnd.GetPoint()->GetNode().FindTableNode() != pIdx->GetNode().FindTableNode()
                || aEnd.GetPoint()->GetNode().FindSectionNode() != &pIdx->GetNode())
            {
                isMoveIntoTable = true;
            }
            if (isMoveIntoTable)
            {
                css::uno::Reference<SwXText> const xParentText =
                    ::sw::CreateParentXText(pSectFormat->GetDoc(), SwPosition(*pIdx));
                xRet = new SwXTextRange(*pSectFormat);
            }
            else // for compatibility, keep the old way in this case
            {
                xRet = SwXTextRange::CreateXTextRange(pSectFormat->GetDoc(),
                    *aPaM.Start(), aEnd.Start());
            }
        }
    }
    return xRet;
}

void SAL_CALL SwXTextSection::dispose()
{
    SolarMutexGuard aGuard;

    SwSectionFormat *const pSectFormat = m_pImpl->GetSectionFormat();
    if (pSectFormat)
    {
        pSectFormat->GetDoc().DelSectionFormat( pSectFormat );
    }
}

void SAL_CALL SwXTextSection::addEventListener(
        const uno::Reference< lang::XEventListener > & xListener)
{
    // no need to lock here as m_pImpl is const and container threadsafe
    std::unique_lock aGuard(m_pImpl->m_Mutex);
    m_pImpl->m_EventListeners.addInterface(aGuard, xListener);
}

void SAL_CALL SwXTextSection::removeEventListener(
        const uno::Reference< lang::XEventListener > & xListener)
{
    // no need to lock here as m_pImpl is const and container threadsafe
    std::unique_lock aGuard(m_pImpl->m_Mutex);
    m_pImpl->m_EventListeners.removeInterface(aGuard, xListener);
}

uno::Reference< beans::XPropertySetInfo > SAL_CALL
SwXTextSection::getPropertySetInfo()
{
    SolarMutexGuard g;
    return m_pImpl->m_rPropSet.getPropertySetInfo();
}

static void
lcl_UpdateLinkType(SwSection & rSection, bool const bLinkUpdateAlways)
{
    if (rSection.GetType() == SectionType::DdeLink)
    {
        // set update type; needs an established link
        if (!rSection.IsConnected())
        {
            rSection.CreateLink(LinkCreateType::Connect);
        }
        rSection.SetUpdateType( bLinkUpdateAlways
            ? SfxLinkUpdateMode::ALWAYS : SfxLinkUpdateMode::ONCALL );
    }
}

static void
lcl_UpdateSection(SwSectionFormat *const pFormat,
    std::unique_ptr<SwSectionData> const& pSectionData,
    std::optional<SfxItemSet> const& oItemSet,
    bool const bLinkModeChanged, bool const bLinkUpdateAlways = true)
{
    if (!pFormat)
        return;

    SwSection & rSection = *pFormat->GetSection();
    SwDoc& rDoc = pFormat->GetDoc();
    SwSectionFormats const& rFormats = rDoc.GetSections();
    UnoActionContext aContext(&rDoc);
    for (size_t i = 0; i < rFormats.size(); ++i)
    {
        if (rFormats[i]->GetSection()->GetSectionName()
                == rSection.GetSectionName())
        {
            rDoc.UpdateSection(i, *pSectionData, oItemSet ? &*oItemSet : nullptr,
                    rDoc.IsInReading());
            {
                // temporarily remove actions to allow cursor update
                // TODO: why? no table cursor here!
                UnoActionRemoveContext aRemoveContext( &rDoc );
            }

            if (bLinkModeChanged)
            {
                lcl_UpdateLinkType(rSection, bLinkUpdateAlways);
            }
            // section found and processed: break from loop
            break;
        }
    }
}

void SwXTextSection::Impl::SetPropertyValues_Impl(
    const uno::Sequence< OUString >& rPropertyNames,
    const uno::Sequence< uno::Any >& rValues)
{
    if(rPropertyNames.getLength() != rValues.getLength())
    {
        throw lang::IllegalArgumentException();
    }
    SwSectionFormat *const pFormat = GetSectionFormat();
    if (!pFormat && !m_bIsDescriptor)
    {
        throw uno::RuntimeException();
    }

    std::unique_ptr<SwSectionData> const pSectionData(
        pFormat ? new SwSectionData(*pFormat->GetSection()) : nullptr);

    OUString const*const pPropertyNames = rPropertyNames.getConstArray();
    uno::Any const*const pValues = rValues.getConstArray();
    std::optional<SfxItemSet> oItemSet;
    bool bLinkModeChanged = false;
    bool bLinkMode = false;

    for (sal_Int32 nProperty = 0; nProperty < rPropertyNames.getLength();
         nProperty++)
    {
        SfxItemPropertyMapEntry const*const pEntry =
            m_rPropSet.getPropertyMap().getByName(pPropertyNames[nProperty]);
        if (!pEntry)
        {
            throw beans::UnknownPropertyException(
                "Unknown property: " + pPropertyNames[nProperty],
                m_rThis.getXWeak());
        }
        if (pEntry->nFlags & beans::PropertyAttribute::READONLY)
        {
            throw beans::PropertyVetoException(
                "Property is read-only: " + pPropertyNames[nProperty],
                m_rThis.getXWeak());
        }
        switch (pEntry->nWID)
        {
            case WID_SECT_CONDITION:
            {
                OUString uTmp;
                pValues[nProperty] >>= uTmp;
                if (m_bIsDescriptor)
                {
                    m_pProps->m_sCondition = uTmp;
                }
                else
                {
                    pSectionData->SetCondition(uTmp);
                }
            }
            break;
            case WID_SECT_DDE_TYPE:
            case WID_SECT_DDE_FILE:
            case WID_SECT_DDE_ELEMENT:
            {
                OUString sTmp;
                pValues[nProperty] >>= sTmp;
                if (m_bIsDescriptor)
                {
                    if (!m_pProps->m_bDDE)
                    {
                        m_pProps->m_sLinkFileName =
                            OUStringChar(sfx2::cTokenSeparator) + OUStringChar(sfx2::cTokenSeparator);
                        m_pProps->m_bDDE = true;
                    }
                    m_pProps->m_sLinkFileName = comphelper::string::setToken(
                        m_pProps->m_sLinkFileName,
                        pEntry->nWID - WID_SECT_DDE_TYPE, sfx2::cTokenSeparator, sTmp);
                }
                else
                {
                    OUString sLinkFileName(pSectionData->GetLinkFileName());
                    if (pSectionData->GetType() != SectionType::DdeLink)
                    {
                        sLinkFileName = OUStringChar(sfx2::cTokenSeparator) + OUStringChar(sfx2::cTokenSeparator);
                        pSectionData->SetType(SectionType::DdeLink);
                    }
                    sLinkFileName = comphelper::string::setToken(sLinkFileName,
                        pEntry->nWID - WID_SECT_DDE_TYPE,
                            sfx2::cTokenSeparator, sTmp);
                    pSectionData->SetLinkFileName(sLinkFileName);
                }
            }
            break;
            case WID_SECT_DDE_AUTOUPDATE:
            {
                bool bVal(false);
                if (!(pValues[nProperty] >>= bVal))
                {
                    throw lang::IllegalArgumentException();
                }
                if (m_bIsDescriptor)
                {
                    m_pProps->m_bUpdateType = bVal;
                }
                else
                {
                    bLinkModeChanged = true;
                    bLinkMode = bVal;
                }
            }
            break;
            case WID_SECT_LINK:
            {
                text::SectionFileLink aLink;
                if (!(pValues[nProperty] >>= aLink))
                {
                    throw lang::IllegalArgumentException();
                }
                if (m_bIsDescriptor)
                {
                    m_pProps->m_bDDE = false;
                    m_pProps->m_sLinkFileName = aLink.FileURL;
                    m_pProps->m_sSectionFilter = aLink.FilterName;
                }
                else
                {
                    if (pSectionData->GetType() != SectionType::FileLink &&
                        !aLink.FileURL.isEmpty())
                    {
                        pSectionData->SetType(SectionType::FileLink);
                    }
                    OUString sTmp;
                    SwDocShell* pShell = pFormat->GetDoc().GetDocShell();
                    if (pShell && !aLink.FileURL.isEmpty())
                    {
                        sTmp = URIHelper::SmartRel2Abs(
                            pShell->GetMedium()->GetURLObject(),
                            aLink.FileURL, URIHelper::GetMaybeFileHdl());
                    }
                    const OUString sFileName(
                        sTmp + OUStringChar(sfx2::cTokenSeparator) +
                        aLink.FilterName + OUStringChar(sfx2::cTokenSeparator) +
                        o3tl::getToken(pSectionData->GetLinkFileName(), 2, sfx2::cTokenSeparator));
                    pSectionData->SetLinkFileName(sFileName);
                    if (sFileName.getLength() < 3)
                    {
                        pSectionData->SetType(SectionType::Content);
                    }
                }
            }
            break;
            case WID_SECT_REGION:
            {
                OUString sLink;
                pValues[nProperty] >>= sLink;
                if (m_bIsDescriptor)
                {
                    m_pProps->m_bDDE = false;
                    m_pProps->m_sSectionRegion = sLink;
                }
                else
                {
                    if (pSectionData->GetType() != SectionType::FileLink &&
                        !sLink.isEmpty())
                    {
                        pSectionData->SetType(SectionType::FileLink);
                    }
                    OUString sSectLink(pSectionData->GetLinkFileName());
                    for (sal_Int32 i = comphelper::string::getTokenCount(sSectLink, sfx2::cTokenSeparator);
                         i < 3; ++i)
                    {
                        sSectLink += OUStringChar(sfx2::cTokenSeparator);
                    }
                    sSectLink = comphelper::string::setToken(sSectLink, 2, sfx2::cTokenSeparator, sLink);
                    pSectionData->SetLinkFileName(sSectLink);
                    if (sSectLink.getLength() < 3)
                    {
                        pSectionData->SetType(SectionType::Content);
                    }
                }
            }
            break;
            case WID_SECT_VISIBLE:
            {
                bool bVal(false);
                if (!(pValues[nProperty] >>= bVal))
                {
                    throw lang::IllegalArgumentException();
                }
                if (m_bIsDescriptor)
                {
                    m_pProps->m_bHidden = !bVal;
                }
                else
                {
                    pSectionData->SetHidden(!bVal);
                }
            }
            break;
            case WID_SECT_CURRENTLY_VISIBLE:
            {
                bool bVal(false);
                if (!(pValues[nProperty] >>= bVal))
                {
                    throw lang::IllegalArgumentException();
                }
                if (m_bIsDescriptor)
                {
                    m_pProps->m_bCondHidden = !bVal;
                }
                else
                {
                    if (!pSectionData->GetCondition().isEmpty())
                    {
                        pSectionData->SetCondHidden(!bVal);
                    }
                }
            }
            break;
            case WID_SECT_PROTECTED:
            {
                bool bVal(false);
                if (!(pValues[nProperty] >>= bVal))
                {
                    throw lang::IllegalArgumentException();
                }
                if (m_bIsDescriptor)
                {
                    m_pProps->m_bProtect = bVal;
                }
                else
                {
                    pSectionData->SetProtectFlag(bVal);
                }
            }
            break;
            case WID_SECT_EDIT_IN_READONLY:
            {
                bool bVal(false);
                if (!(pValues[nProperty] >>= bVal))
                {
                    throw lang::IllegalArgumentException();
                }
                if (m_bIsDescriptor)
                {
                    m_pProps->m_bEditInReadonly = bVal;
                }
                else
                {
                    pSectionData->SetEditInReadonlyFlag(bVal);
                }
            }
            break;
            case WID_SECT_PASSWORD:
            {
                uno::Sequence<sal_Int8> aSeq;
                pValues[nProperty] >>= aSeq;
                if (m_bIsDescriptor)
                {
                    m_pProps->m_Password = std::move(aSeq);
                }
                else
                {
                    pSectionData->SetPassword(aSeq);
                }
            }
            break;
            default:
            {
                if (pFormat)
                {
                    const SfxItemSet& rOldAttrSet = pFormat->GetAttrSet();
                    oItemSet.emplace(*rOldAttrSet.GetPool(), pEntry->nWID, pEntry->nWID);
                    oItemSet->Put(rOldAttrSet);
                    SfxItemPropertySet::setPropertyValue(*pEntry,
                            pValues[nProperty], *oItemSet);
                }
                else
                {
                    SfxPoolItem* pPutItem = nullptr;
                    if (RES_COL == pEntry->nWID)
                    {
                        if (!m_pProps->m_pColItem)
                        {
                            m_pProps->m_pColItem.reset(new SwFormatCol);
                        }
                        pPutItem = m_pProps->m_pColItem.get();
                    }
                    else if (RES_BACKGROUND == pEntry->nWID)
                    {
                        if (!m_pProps->m_pBrushItem)
                        {
                            m_pProps->m_pBrushItem.reset(
                                new SvxBrushItem(RES_BACKGROUND));
                        }
                        pPutItem = m_pProps->m_pBrushItem.get();
                    }
                    else if (RES_FTN_AT_TXTEND == pEntry->nWID)
                    {
                        if (!m_pProps->m_pFootnoteItem)
                        {
                            m_pProps->m_pFootnoteItem.reset(new SwFormatFootnoteAtTextEnd);
                        }
                        pPutItem = m_pProps->m_pFootnoteItem.get();
                    }
                    else if (RES_END_AT_TXTEND == pEntry->nWID)
                    {
                        if (!m_pProps->m_pEndItem)
                        {
                            m_pProps->m_pEndItem.reset(new SwFormatEndAtTextEnd);
                        }
                        pPutItem = m_pProps->m_pEndItem.get();
                    }
                    else if (RES_UNKNOWNATR_CONTAINER== pEntry->nWID)
                    {
                        if (!m_pProps->m_pXMLAttr)
                        {
                            m_pProps->m_pXMLAttr.reset(
                                new SvXMLAttrContainerItem(
                                    RES_UNKNOWNATR_CONTAINER));
                        }
                        pPutItem = m_pProps->m_pXMLAttr.get();
                    }
                    else if (RES_COLUMNBALANCE== pEntry->nWID)
                    {
                        if (!m_pProps->m_pNoBalanceItem)
                        {
                            m_pProps->m_pNoBalanceItem.reset(
                                new SwFormatNoBalancedColumns(true));
                        }
                        pPutItem = m_pProps->m_pNoBalanceItem.get();
                    }
                    else if (RES_FRAMEDIR == pEntry->nWID)
                    {
                        if (!m_pProps->m_pFrameDirItem)
                        {
                            m_pProps->m_pFrameDirItem.reset(
                                new SvxFrameDirectionItem(
                                SvxFrameDirection::Horizontal_LR_TB, RES_FRAMEDIR));
                        }
                        pPutItem = m_pProps->m_pFrameDirItem.get();
                    }
                    else if (RES_LR_SPACE == pEntry->nWID)
                    {
                        if (!m_pProps->m_pLRSpaceItem)
                        {
                            m_pProps->m_pLRSpaceItem.reset(
                                new SvxLRSpaceItem( RES_LR_SPACE ));
                        }
                        pPutItem = m_pProps->m_pLRSpaceItem.get();
                    }
                    if (pPutItem)
                    {
                        pPutItem->PutValue(pValues[nProperty],
                                pEntry->nMemberId);
                    }
                }
            }
        }
    }

    lcl_UpdateSection(pFormat, pSectionData, oItemSet, bLinkModeChanged,
        bLinkMode);
}

void SAL_CALL
SwXTextSection::setPropertyValues(
    const uno::Sequence< OUString >& rPropertyNames,
    const uno::Sequence< uno::Any >& rValues)
{
    SolarMutexGuard aGuard;

    // workaround for bad designed API
    try
    {
        m_pImpl->SetPropertyValues_Impl( rPropertyNames, rValues );
    }
    catch (const beans::UnknownPropertyException &rException)
    {
        // wrap the original (here not allowed) exception in
        // a WrappedTargetException that gets thrown instead.
        lang::WrappedTargetException aWExc;
        aWExc.TargetException <<= rException;
        throw aWExc;
    }
}

void SwXTextSection::setPropertyValue(
    const OUString& rPropertyName, const uno::Any& rValue)
{
    SolarMutexGuard aGuard;

    m_pImpl->SetPropertyValues_Impl( { rPropertyName } , { rValue } );
}

uno::Sequence< uno::Any >
SwXTextSection::Impl::GetPropertyValues_Impl(
        const uno::Sequence< OUString > & rPropertyNames )
{
    SwSectionFormat *const pFormat = GetSectionFormat();
    if (!pFormat && !m_bIsDescriptor)
    {
        throw uno::RuntimeException( u"non-descriptor section without format"_ustr);
    }

    uno::Sequence< uno::Any > aRet(rPropertyNames.getLength());
    uno::Any* pRet = aRet.getArray();
    SwSection *const pSect = pFormat ? pFormat->GetSection() : nullptr;
    const OUString* pPropertyNames = rPropertyNames.getConstArray();

    for (sal_Int32 nProperty = 0; nProperty < rPropertyNames.getLength();
        nProperty++)
    {
        SfxItemPropertyMapEntry const*const pEntry =
            m_rPropSet.getPropertyMap().getByName(pPropertyNames[nProperty]);
        if (!pEntry)
        {
            throw beans::UnknownPropertyException(
                "Unknown property: " + pPropertyNames[nProperty],
                m_rThis.getXWeak());
        }
        switch(pEntry->nWID)
        {
            case WID_SECT_CONDITION:
            {
                const OUString uTmp( m_bIsDescriptor
                    ? m_pProps->m_sCondition
                    : pSect->GetCondition());
                pRet[nProperty] <<= uTmp;
            }
            break;
            case WID_SECT_DDE_TYPE:
            case WID_SECT_DDE_FILE:
            case WID_SECT_DDE_ELEMENT:
            {
                OUString sRet;
                if (m_bIsDescriptor)
                {
                    if (m_pProps->m_bDDE)
                    {
                        sRet = m_pProps->m_sLinkFileName;
                    }
                }
                else if (SectionType::DdeLink == pSect->GetType())
                {
                    sRet = pSect->GetLinkFileName();
                }
                pRet[nProperty] <<= sRet.getToken(pEntry->nWID - WID_SECT_DDE_TYPE,
                    sfx2::cTokenSeparator);
            }
            break;
            case WID_SECT_DDE_AUTOUPDATE:
            {
                // GetUpdateType() returns .._ALWAYS or .._ONCALL
                if (pSect && pSect->IsLinkType() && pSect->IsConnected())  // #i73247#
                {
                    const bool bTemp =
                        (pSect->GetUpdateType() == SfxLinkUpdateMode::ALWAYS);
                    pRet[nProperty] <<= bTemp;
                }
            }
            break;
            case WID_SECT_LINK     :
            {
                text::SectionFileLink aLink;
                if (m_bIsDescriptor)
                {
                    if (!m_pProps->m_bDDE)
                    {
                        aLink.FileURL = m_pProps->m_sLinkFileName;
                        aLink.FilterName = m_pProps->m_sSectionFilter;
                    }
                }
                else if (SectionType::FileLink == pSect->GetType())
                {
                    const OUString& sRet( pSect->GetLinkFileName() );
                    sal_Int32 nIndex(0);
                    aLink.FileURL =
                        sRet.getToken(0, sfx2::cTokenSeparator, nIndex);
                    aLink.FilterName =
                        sRet.getToken(0, sfx2::cTokenSeparator, nIndex);
                }
                pRet[nProperty] <<= aLink;
            }
            break;
            case WID_SECT_REGION :
            {
                OUString sRet;
                if (m_bIsDescriptor)
                {
                    sRet = m_pProps->m_sSectionRegion;
                }
                else if (SectionType::FileLink == pSect->GetType())
                {
                    sRet = pSect->GetLinkFileName().getToken(2,
                            sfx2::cTokenSeparator);
                }
                pRet[nProperty] <<= sRet;
            }
            break;
            case WID_SECT_VISIBLE   :
            {
                const bool bTemp = m_bIsDescriptor
                    ? !m_pProps->m_bHidden : !pSect->IsHidden();
                pRet[nProperty] <<= bTemp;
            }
            break;
            case WID_SECT_CURRENTLY_VISIBLE:
            {
                const bool bTemp = m_bIsDescriptor
                    ? !m_pProps->m_bCondHidden : !pSect->IsCondHidden();
                pRet[nProperty] <<= bTemp;
            }
            break;
            case WID_SECT_PROTECTED:
            {
                const bool bTemp = m_bIsDescriptor
                    ? m_pProps->m_bProtect : pSect->IsProtect();
                pRet[nProperty] <<= bTemp;
            }
            break;
            case WID_SECT_EDIT_IN_READONLY:
            {
                const bool bTemp = m_bIsDescriptor
                    ? m_pProps->m_bEditInReadonly : pSect->IsEditInReadonly();
                pRet[nProperty] <<= bTemp;
            }
            break;
            case  FN_PARAM_LINK_DISPLAY_NAME:
            {
                if (pFormat)
                {
                    pRet[nProperty] <<= pFormat->GetSection()->GetSectionName().toString();
                }
            }
            break;
            case WID_SECT_DOCUMENT_INDEX:
            {
                // search enclosing index
                SwSection* pEnclosingSection = pSect;
                while ((pEnclosingSection != nullptr) &&
                       (SectionType::ToxContent != pEnclosingSection->GetType()))
                {
                    pEnclosingSection = pEnclosingSection->GetParent();
                }
                SwTOXBaseSection* const pTOXBaseSect = pEnclosingSection ?
                    dynamic_cast<SwTOXBaseSection*>( pEnclosingSection ) : nullptr;
                if (pTOXBaseSect)
                {
                    // convert section to TOXBase and get SwXDocumentIndex
                    const rtl::Reference<SwXDocumentIndex> xIndex =
                        SwXDocumentIndex::CreateXDocumentIndex(
                            pTOXBaseSect->GetFormat()->GetDoc(), pTOXBaseSect);
                    pRet[nProperty] <<= uno::Reference<text::XDocumentIndex>(xIndex);
                }
                // else: no enclosing index found -> empty return value
            }
            break;
            case WID_SECT_IS_GLOBAL_DOC_SECTION:
            {
                const bool bRet = pFormat && (nullptr != pFormat->GetGlobalDocSection());
                pRet[nProperty] <<= bRet;
            }
            break;
            case FN_UNO_ANCHOR_TYPES:
            case FN_UNO_TEXT_WRAP:
            case FN_UNO_ANCHOR_TYPE:
                (void)::sw::GetDefaultTextContentValue(pRet[nProperty], u"", pEntry->nWID);
            break;
            case FN_UNO_REDLINE_NODE_START:
            case FN_UNO_REDLINE_NODE_END:
            {
                if (!pFormat)
                    break;      // #i73247#
                SwNode* pSectNode = pFormat->GetSectionNode();
                if (FN_UNO_REDLINE_NODE_END == pEntry->nWID)
                {
                    pSectNode = pSectNode->EndOfSectionNode();
                }
                const SwRedlineTable& rRedTable =
                    pFormat->GetDoc().getIDocumentRedlineAccess().GetRedlineTable();
                for (SwRangeRedline* pRedline : rRedTable)
                {
                    const SwNode& rRedPointNode = pRedline->GetPointNode();
                    const SwNode& rRedMarkNode = pRedline->GetMarkNode();
                    if ((&rRedPointNode == pSectNode) ||
                        (&rRedMarkNode == pSectNode))
                    {
                        const SwNode& rStartOfRedline =
                            (SwNodeIndex(rRedPointNode) <=
                             SwNodeIndex(rRedMarkNode))
                                 ? rRedPointNode : rRedMarkNode;
                        const bool bIsStart = (&rStartOfRedline == pSectNode);
                        pRet[nProperty] <<=
                            SwXRedlinePortion::CreateRedlineProperties(
                                    *pRedline, bIsStart);
                        break;
                    }
                }
            }
            break;
            case WID_SECT_PASSWORD:
            {
                pRet[nProperty] <<= m_bIsDescriptor
                    ? m_pProps->m_Password : pSect->GetPassword();
            }
            break;
            default:
            {
                if (pFormat)
                {
                    SfxItemPropertySet::getPropertyValue(*pEntry,
                            pFormat->GetAttrSet(), pRet[nProperty]);
                }
                else
                {
                    const SfxPoolItem* pQueryItem = nullptr;
                    if (RES_COL == pEntry->nWID)
                    {
                        if (!m_pProps->m_pColItem)
                        {
                            m_pProps->m_pColItem.reset(new SwFormatCol);
                        }
                        pQueryItem = m_pProps->m_pColItem.get();
                    }
                    else if (RES_BACKGROUND == pEntry->nWID)
                    {
                        if (!m_pProps->m_pBrushItem)
                        {
                            m_pProps->m_pBrushItem.reset(
                                new SvxBrushItem(RES_BACKGROUND));
                        }
                        pQueryItem = m_pProps->m_pBrushItem.get();
                    }
                    else if (RES_FTN_AT_TXTEND == pEntry->nWID)
                    {
                        if (!m_pProps->m_pFootnoteItem)
                        {
                            m_pProps->m_pFootnoteItem.reset(new SwFormatFootnoteAtTextEnd);
                        }
                        pQueryItem = m_pProps->m_pFootnoteItem.get();
                    }
                    else if (RES_END_AT_TXTEND == pEntry->nWID)
                    {
                        if (!m_pProps->m_pEndItem)
                        {
                            m_pProps->m_pEndItem.reset(new SwFormatEndAtTextEnd);
                        }
                        pQueryItem = m_pProps->m_pEndItem.get();
                    }
                    else if (RES_UNKNOWNATR_CONTAINER== pEntry->nWID)
                    {
                        if (!m_pProps->m_pXMLAttr)
                        {
                            m_pProps->m_pXMLAttr.reset(
                                new SvXMLAttrContainerItem);
                        }
                        pQueryItem = m_pProps->m_pXMLAttr.get();
                    }
                    else if (RES_COLUMNBALANCE== pEntry->nWID)
                    {
                        if (!m_pProps->m_pNoBalanceItem)
                        {
                            m_pProps->m_pNoBalanceItem.reset(
                                new SwFormatNoBalancedColumns);
                        }
                        pQueryItem = m_pProps->m_pNoBalanceItem.get();
                    }
                    else if (RES_FRAMEDIR == pEntry->nWID)
                    {
                        if (!m_pProps->m_pFrameDirItem)
                        {
                            m_pProps->m_pFrameDirItem.reset(
                                new SvxFrameDirectionItem(
                                    SvxFrameDirection::Environment, RES_FRAMEDIR));
                        }
                        pQueryItem = m_pProps->m_pFrameDirItem.get();
                    }
                    else if (RES_LR_SPACE == pEntry->nWID)
                    {
                        if (!m_pProps->m_pLRSpaceItem)
                        {
                            m_pProps->m_pLRSpaceItem.reset(
                                new SvxLRSpaceItem( RES_LR_SPACE ));
                        }
                        pQueryItem = m_pProps->m_pLRSpaceItem.get();
                    }
                    if (pQueryItem)
                    {
                        pQueryItem->QueryValue(pRet[nProperty],
                                pEntry->nMemberId);
                    }
                }
            }
        }
    }
    return aRet;
}

uno::Sequence< uno::Any > SAL_CALL
SwXTextSection::getPropertyValues(
    const uno::Sequence< OUString >& rPropertyNames)
{
    SolarMutexGuard aGuard;
    uno::Sequence< uno::Any > aValues;

    // workaround for bad designed API
    try
    {
        aValues = m_pImpl->GetPropertyValues_Impl( rPropertyNames );
    }
    catch (beans::UnknownPropertyException &)
    {
        css::uno::Any anyEx = cppu::getCaughtException();
        throw lang::WrappedTargetRuntimeException(u"Unknown property exception caught"_ustr,
                getXWeak(), anyEx );
    }
    catch (lang::WrappedTargetException &)
    {
        css::uno::Any anyEx = cppu::getCaughtException();
        throw lang::WrappedTargetRuntimeException(u"WrappedTargetException caught"_ustr,
                getXWeak(), anyEx );
    }

    return aValues;
}

uno::Any SAL_CALL
SwXTextSection::getPropertyValue(const OUString& rPropertyName)
{
    SolarMutexGuard aGuard;

    uno::Sequence< OUString > aPropertyNames { rPropertyName };
    return m_pImpl->GetPropertyValues_Impl(aPropertyNames).getConstArray()[0];
}

void SAL_CALL SwXTextSection::addPropertiesChangeListener(
    const uno::Sequence< OUString >& /*aPropertyNames*/,
    const uno::Reference< beans::XPropertiesChangeListener >& /*xListener*/ )
{
    OSL_FAIL("SwXTextSection::addPropertiesChangeListener(): not implemented");
}

void SAL_CALL SwXTextSection::removePropertiesChangeListener(
    const uno::Reference< beans::XPropertiesChangeListener >& /*xListener*/ )
{
    OSL_FAIL("SwXTextSection::removePropertiesChangeListener(): not implemented");
}

void SAL_CALL SwXTextSection::firePropertiesChangeEvent(
    const uno::Sequence< OUString >& /*aPropertyNames*/,
    const uno::Reference< beans::XPropertiesChangeListener >& /*xListener*/ )
{
    OSL_FAIL("SwXTextSection::firePropertiesChangeEvent(): not implemented");
}

void SAL_CALL
SwXTextSection::addPropertyChangeListener(
        const OUString& /*rPropertyName*/,
        const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
{
    OSL_FAIL("SwXTextSection::addPropertyChangeListener(): not implemented");
}

void SAL_CALL
SwXTextSection::removePropertyChangeListener(
        const OUString& /*rPropertyName*/,
        const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
{
    OSL_FAIL("SwXTextSection::removePropertyChangeListener(): not implemented");
}

void SAL_CALL
SwXTextSection::addVetoableChangeListener(
        const OUString& /*rPropertyName*/,
        const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
{
    OSL_FAIL("SwXTextSection::addVetoableChangeListener(): not implemented");
}

void SAL_CALL
SwXTextSection::removeVetoableChangeListener(
        const OUString& /*rPropertyName*/,
        const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
{
    OSL_FAIL("SwXTextSection::removeVetoableChangeListener(): not implemented");
}

beans::PropertyState SAL_CALL
SwXTextSection::getPropertyState(const OUString& rPropertyName)
{
    SolarMutexGuard aGuard;

    uno::Sequence< OUString > aNames { rPropertyName };
    return getPropertyStates(aNames).getConstArray()[0];
}

uno::Sequence< beans::PropertyState > SAL_CALL
SwXTextSection::getPropertyStates(
        const uno::Sequence< OUString >& rPropertyNames)
{
    SolarMutexGuard aGuard;

    SwSectionFormat *const pFormat = m_pImpl->GetSectionFormat();
    if (!pFormat && !m_pImpl->m_bIsDescriptor)
    {
        throw uno::RuntimeException();
    }

    uno::Sequence< beans::PropertyState > aStates(rPropertyNames.getLength());
    beans::PropertyState *const pStates = aStates.getArray();
    const OUString* pNames = rPropertyNames.getConstArray();
    for (sal_Int32 i = 0; i < rPropertyNames.getLength(); i++)
    {
        pStates[i] = beans::PropertyState_DEFAULT_VALUE;
        SfxItemPropertyMapEntry const*const pEntry =
            m_pImpl->m_rPropSet.getPropertyMap().getByName( pNames[i]);
        if (!pEntry)
        {
            throw beans::UnknownPropertyException(
                "Unknown property: " + pNames[i],
                getXWeak());
        }
        switch (pEntry->nWID)
        {
            case WID_SECT_CONDITION:
            case WID_SECT_DDE_TYPE:
            case WID_SECT_DDE_FILE:
            case WID_SECT_DDE_ELEMENT:
            case WID_SECT_DDE_AUTOUPDATE:
            case WID_SECT_LINK:
            case WID_SECT_REGION :
            case WID_SECT_VISIBLE:
            case WID_SECT_PROTECTED:
            case WID_SECT_EDIT_IN_READONLY:
            case  FN_PARAM_LINK_DISPLAY_NAME:
            case  FN_UNO_ANCHOR_TYPES:
            case  FN_UNO_TEXT_WRAP:
            case  FN_UNO_ANCHOR_TYPE:
                pStates[i] = beans::PropertyState_DIRECT_VALUE;
            break;
            default:
            {
                if (pFormat)
                {
                    pStates[i] = m_pImpl->m_rPropSet.getPropertyState(
                                    pNames[i], pFormat->GetAttrSet());
                }
                else
                {
                    if (RES_COL == pEntry->nWID)
                    {
                        if (!m_pImpl->m_pProps->m_pColItem)
                        {
                            pStates[i] = beans::PropertyState_DEFAULT_VALUE;
                        }
                        else
                        {
                            pStates[i] = beans::PropertyState_DIRECT_VALUE;
                        }
                    }
                    else
                    {
                        if (!m_pImpl->m_pProps->m_pBrushItem)
                        {
                            pStates[i] = beans::PropertyState_DEFAULT_VALUE;
                        }
                        else
                        {
                            pStates[i] = beans::PropertyState_DIRECT_VALUE;
                        }
                    }
                }
            }
        }
    }
    return aStates;
}

void SAL_CALL
SwXTextSection::setPropertyToDefault(const OUString& rPropertyName)
{
    SolarMutexGuard aGuard;

    SwSectionFormat *const pFormat = m_pImpl->GetSectionFormat();
    if (!pFormat && !m_pImpl->m_bIsDescriptor)
    {
        throw uno::RuntimeException();
    }

    SfxItemPropertyMapEntry const*const pEntry =
        m_pImpl->m_rPropSet.getPropertyMap().getByName(rPropertyName);
    if (!pEntry)
    {
        throw beans::UnknownPropertyException(
            "Unknown property: " + rPropertyName,
            getXWeak());
    }
    if (pEntry->nFlags & beans::PropertyAttribute::READONLY)
    {
        throw uno::RuntimeException(
            "Property is read-only: " + rPropertyName,
            getXWeak());
    }

    std::unique_ptr<SwSectionData> pSectionData;
    if (pFormat)
        pSectionData.reset(new SwSectionData(*pFormat->GetSection()));

    std::optional<SfxItemSet> oNewAttrSet;
    bool bLinkModeChanged = false;

    switch (pEntry->nWID)
    {
        case WID_SECT_CONDITION:
        {
            if (m_pImpl->m_bIsDescriptor)
            {
                m_pImpl->m_pProps->m_sCondition.clear();
            }
            else
            {
                pSectionData->SetCondition(OUString());
            }
        }
        break;
        case WID_SECT_DDE_TYPE      :
        case WID_SECT_DDE_FILE      :
        case WID_SECT_DDE_ELEMENT   :
        case WID_SECT_LINK     :
        case WID_SECT_REGION :
            if (m_pImpl->m_bIsDescriptor)
            {
                m_pImpl->m_pProps->m_bDDE = false;
                m_pImpl->m_pProps->m_sLinkFileName.clear();
                m_pImpl->m_pProps->m_sSectionRegion.clear();
                m_pImpl->m_pProps->m_sSectionFilter.clear();
            }
            else
            {
                pSectionData->SetType(SectionType::Content);
            }
        break;
        case WID_SECT_DDE_AUTOUPDATE:
            if (m_pImpl->m_bIsDescriptor)
            {
                m_pImpl->m_pProps->m_bUpdateType = true;
            }
            else
            {
                bLinkModeChanged = true;
            }
        break;
        case WID_SECT_VISIBLE   :
        {
            if (m_pImpl->m_bIsDescriptor)
            {
                m_pImpl->m_pProps->m_bHidden = false;
            }
            else
            {
                pSectionData->SetHidden(false);
            }
        }
        break;
        case WID_SECT_PROTECTED:
        {
            if (m_pImpl->m_bIsDescriptor)
            {
                m_pImpl->m_pProps->m_bProtect = false;
            }
            else
            {
                pSectionData->SetProtectFlag(false);
            }
        }
        break;
        case WID_SECT_EDIT_IN_READONLY:
        {
            if (m_pImpl->m_bIsDescriptor)
            {
                m_pImpl->m_pProps->m_bEditInReadonly = false;
            }
            else
            {
                pSectionData->SetEditInReadonlyFlag(false);
            }
        }
        break;

        case  FN_UNO_ANCHOR_TYPES:
        case  FN_UNO_TEXT_WRAP:
        case  FN_UNO_ANCHOR_TYPE:
        break;
        default:
        {
            if (SfxItemPool::IsWhich(pEntry->nWID))
            {
                if (pFormat)
                {
                    const SfxItemSet& rOldAttrSet = pFormat->GetAttrSet();
                    oNewAttrSet.emplace(*rOldAttrSet.GetPool(), pEntry->nWID, pEntry->nWID);
                    oNewAttrSet->ClearItem(pEntry->nWID);
                }
                else
                {
                    if (RES_COL == pEntry->nWID)
                    {
                        m_pImpl->m_pProps->m_pColItem.reset();
                    }
                    else if (RES_BACKGROUND == pEntry->nWID)
                    {
                        m_pImpl->m_pProps->m_pBrushItem.reset();
                    }
                }
            }
        }
    }

    lcl_UpdateSection(pFormat, pSectionData, oNewAttrSet, bLinkModeChanged);
}

uno::Any SAL_CALL
SwXTextSection::getPropertyDefault(const OUString& rPropertyName)
{
    SolarMutexGuard aGuard;

    uno::Any aRet;
    SwSectionFormat *const pFormat = m_pImpl->GetSectionFormat();
    SfxItemPropertyMapEntry const*const pEntry =
        m_pImpl->m_rPropSet.getPropertyMap().getByName(rPropertyName);
    if (!pEntry)
    {
        throw beans::UnknownPropertyException(
            "Unknown property: " + rPropertyName,
            getXWeak());
    }

    switch(pEntry->nWID)
    {
        case WID_SECT_CONDITION:
        case WID_SECT_DDE_TYPE      :
        case WID_SECT_DDE_FILE      :
        case WID_SECT_DDE_ELEMENT   :
        case WID_SECT_REGION :
        case FN_PARAM_LINK_DISPLAY_NAME:
            aRet <<= OUString();
        break;
        case WID_SECT_LINK     :
            aRet <<= text::SectionFileLink();
        break;
        case WID_SECT_DDE_AUTOUPDATE:
        case WID_SECT_VISIBLE   :
            aRet <<= true;
        break;
        case WID_SECT_PROTECTED:
        case WID_SECT_EDIT_IN_READONLY:
            aRet <<= false;
        break;
        case  FN_UNO_ANCHOR_TYPES:
        case  FN_UNO_TEXT_WRAP:
        case  FN_UNO_ANCHOR_TYPE:
            (void)::sw::GetDefaultTextContentValue(aRet, u"", pEntry->nWID);
        break;
        default:
        if(pFormat && SfxItemPool::IsWhich(pEntry->nWID))
        {
            SwDoc& rDoc = pFormat->GetDoc();
            const SfxPoolItem& rDefItem =
                rDoc.GetAttrPool().GetUserOrPoolDefaultItem(pEntry->nWID);
            rDefItem.QueryValue(aRet, pEntry->nMemberId);
        }
    }
    return aRet;
}

OUString SAL_CALL SwXTextSection::getName()
{
    SolarMutexGuard aGuard;

    OUString sRet;
    SwSectionFormat const*const pFormat = m_pImpl->GetSectionFormat();
    if(pFormat)
    {
        sRet = pFormat->GetSection()->GetSectionName().toString();
    }
    else if (m_pImpl->m_bIsDescriptor)
    {
        sRet = m_pImpl->m_sName;
    }
    else
    {
        throw uno::RuntimeException();
    }
    return sRet;
}

void SAL_CALL SwXTextSection::setName(const OUString& rName)
{
    SolarMutexGuard aGuard;

    SwSectionFormat *const pFormat = m_pImpl->GetSectionFormat();
    if(pFormat)
    {
        SwSection *const pSect = pFormat->GetSection();
        SwSectionData aSection(*pSect);
        aSection.SetSectionName(UIName(rName));

        const SwSectionFormats& rFormats = pFormat->GetDoc().GetSections();
        size_t nApplyPos = SIZE_MAX;
        for( size_t i = 0; i < rFormats.size(); ++i )
        {
            if(rFormats[i]->GetSection() == pSect)
            {
                nApplyPos = i;
            }
            else if (rName == rFormats[i]->GetSection()->GetSectionName())
            {
                throw uno::RuntimeException();
            }
        }
        if (nApplyPos != SIZE_MAX)
        {
            {
                UnoActionContext aContext(&pFormat->GetDoc());
                pFormat->GetDoc().UpdateSection(nApplyPos, aSection);
            }
            {
                // temporarily remove actions to allow cursor update
                // TODO: why? no table cursor here!
                UnoActionRemoveContext aRemoveContext( &pFormat->GetDoc() );
            }
        }
    }
    else if (m_pImpl->m_bIsDescriptor)
    {
        m_pImpl->m_sName = rName;
    }
    else
    {
        throw uno::RuntimeException();
    }
}

OUString SAL_CALL
SwXTextSection::getImplementationName()
{
    return u"SwXTextSection"_ustr;
}

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

uno::Sequence< OUString > SAL_CALL
SwXTextSection::getSupportedServiceNames()
{
    return {
        u"com.sun.star.text.TextContent"_ustr,
        u"com.sun.star.text.TextSection"_ustr,
        u"com.sun.star.document.LinkTarget"_ustr
    };
}

// MetadatableMixin
::sfx2::Metadatable* SwXTextSection::GetCoreObject()
{
    SwSectionFormat *const pSectionFormat( m_pImpl->GetSectionFormat() );
    return pSectionFormat;
}

uno::Reference<frame::XModel> SwXTextSection::GetModel()
{
    SwSectionFormat *const pSectionFormat( m_pImpl->GetSectionFormat() );
    if (pSectionFormat)
    {
        SwDocShell const*const pShell( pSectionFormat->GetDoc().GetDocShell() );
        return pShell ? pShell->GetModel() : nullptr;
    }
    return nullptr;
}

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