/* -*- 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 <utility>
 
#include <comphelper/interfacecontainer2.hxx>
#include <comphelper/processfactory.hxx>
#include <comphelper/servicehelper.hxx>
#include <cppuhelper/exc_hlp.hxx>
#include <cppuhelper/supportsservice.hxx>
#include <cppuhelper/weak.hxx>
#include <osl/mutex.hxx>
#include <sal/config.h>
#include <svl/listener.hxx>
#include <vcl/svapp.hxx>
#include <sal/log.hxx>
 
#include <unomid.h>
#include <unotextrange.hxx>
#include <unorefmark.hxx>
#include <unotextcursor.hxx>
#include <unomap.hxx>
#include <unocrsr.hxx>
#include <unocrsrhelper.hxx>
#include <doc.hxx>
#include <ndtxt.hxx>
#include <fmtrfmrk.hxx>
#include <txtrfmrk.hxx>
#include <hints.hxx>
#include <unometa.hxx>
#include <unotext.hxx>
#include <unoport.hxx>
#include <txtatr.hxx>
#include <fmtmeta.hxx>
#include <docsh.hxx>
 
#include <com/sun/star/lang/NoSupportException.hpp>
#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
#include <com/sun/star/rdf/Statement.hpp>
#include <com/sun/star/rdf/URI.hpp>
#include <com/sun/star/rdf/URIs.hpp>
#include <com/sun/star/rdf/XLiteral.hpp>
#include <com/sun/star/rdf/XRepositorySupplier.hpp>
#include <com/sun/star/lang/DisposedException.hpp>
 
using namespace ::com::sun::star;
 
class SwXReferenceMark::Impl
    : public SvtListener
{
private:
    ::osl::Mutex m_Mutex; // just for OInterfaceContainerHelper2
 
public:
    uno::WeakReference<uno::XInterface> m_wThis;
    ::comphelper::OInterfaceContainerHelper2 m_EventListeners;
    bool m_bIsDescriptor;
    SwDoc* m_pDoc;
    const SwFormatRefMark* m_pMarkFormat;
    OUString m_sMarkName;
 
    Impl(SwDoc* const pDoc, SwFormatRefMark* const pRefMark)
        : m_EventListeners(m_Mutex)
        , m_bIsDescriptor(nullptr == pRefMark)
        , m_pDoc(pDoc)
        , m_pMarkFormat(pRefMark)
    {
        if (pRefMark)
        {
            StartListening(pRefMark->GetNotifier());
            m_sMarkName = pRefMark->GetRefName();
        }
    }
 
    bool IsValid() const { return m_pMarkFormat; }
    void InsertRefMark( SwPaM & rPam, SwXTextCursor const*const pCursor );
    void Invalidate();
protected:
    virtual void Notify(const SfxHint&) override;
 
};
 
void SwXReferenceMark::Impl::Invalidate()
{
    EndListeningAll();
    m_pDoc = nullptr;
    m_pMarkFormat = 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);
    m_EventListeners.disposeAndClear(ev);
}
 
void SwXReferenceMark::Impl::Notify(const SfxHint& rHint)
{
    if(rHint.GetId() == SfxHintId::Dying)
        Invalidate();
}
 
SwXReferenceMark::SwXReferenceMark(
        SwDoc *const pDoc, SwFormatRefMark *const pRefMark)
    : m_pImpl( new SwXReferenceMark::Impl(pDoc, pRefMark) )
{
}
 
SwXReferenceMark::~SwXReferenceMark()
{
}
 
uno::Reference<text::XTextContent>
SwXReferenceMark::CreateXReferenceMark(
        SwDoc & rDoc, SwFormatRefMark *const pMarkFormat)
{
    // i#105557: do not iterate over the registered clients: race condition
    uno::Reference<text::XTextContent> xMark;
    if (pMarkFormat)
    {
        xMark = pMarkFormat->GetXRefMark();
    }
    if (!xMark.is())
    {
        SwXReferenceMark *const pMark(new SwXReferenceMark(&rDoc, pMarkFormat));
        xMark.set(pMark);
        if (pMarkFormat)
        {
            pMarkFormat->SetXRefMark(xMark);
        }
        // need a permanent Reference to initialize m_wThis
        pMark->m_pImpl->m_wThis = xMark;
    }
    return xMark;
}
 
namespace
{
    class theSwXReferenceMarkUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXReferenceMarkUnoTunnelId > {};
}
 
const uno::Sequence< sal_Int8 > & SwXReferenceMark::getUnoTunnelId()
{
    return theSwXReferenceMarkUnoTunnelId::get().getSeq();
}
 
sal_Int64 SAL_CALL
SwXReferenceMark::getSomething(const uno::Sequence< sal_Int8 >& rId)
{
    return ::sw::UnoTunnelImpl<SwXReferenceMark>(rId, this);
}
 
OUString SAL_CALL SwXReferenceMark::getImplementationName()
{
    return OUString("SwXReferenceMark");
}
 
static char const*const g_ServicesReferenceMark[] =
{
    "com.sun.star.text.TextContent",
    "com.sun.star.text.ReferenceMark",
};
 
static const size_t g_nServicesReferenceMark(SAL_N_ELEMENTS(g_ServicesReferenceMark));
 
sal_Bool SAL_CALL
SwXReferenceMark::supportsService(const OUString& rServiceName)
{
    return cppu::supportsService(this, rServiceName);
}
 
uno::Sequence< OUString > SAL_CALL
SwXReferenceMark::getSupportedServiceNames()
{
    return ::sw::GetSupportedServiceNamesImpl(
            g_nServicesReferenceMark, g_ServicesReferenceMark);
}
 
template<typename T> struct NotContainedIn
{
    std::vector<T> const& m_rVector;
    explicit NotContainedIn(std::vector<T> const& rVector)
        : m_rVector(rVector) { }
    bool operator() (T const& rT) {
        return std::find(m_rVector.begin(), m_rVector.end(), rT)
                    == m_rVector.end();
    }
};
 
void SwXReferenceMark::Impl::InsertRefMark(SwPaM& rPam,
        SwXTextCursor const*const pCursor)
{
    //! in some cases when this function is called the pDoc pointer member may have become
    //! invalid/deleted thus we obtain the document pointer from rPaM where it should always
    //! be valid.
    SwDoc *pDoc2 = rPam.GetDoc();
 
    UnoActionContext aCont(pDoc2);
    SwFormatRefMark aRefMark(m_sMarkName);
    bool bMark = *rPam.GetPoint() != *rPam.GetMark();
 
    const bool bForceExpandHints( !bMark && pCursor && pCursor->IsAtEndOfMeta() );
    const SetAttrMode nInsertFlags = bForceExpandHints
        ?   ( SetAttrMode::FORCEHINTEXPAND
            | SetAttrMode::DONTEXPAND)
        : SetAttrMode::DONTEXPAND;
 
    std::vector<SwTextAttr *> oldMarks;
    if (bMark)
    {
        oldMarks = rPam.GetNode().GetTextNode()->GetTextAttrsAt(
            rPam.GetPoint()->nContent.GetIndex(), RES_TXTATR_REFMARK);
    }
 
    pDoc2->getIDocumentContentOperations().InsertPoolItem( rPam, aRefMark, nInsertFlags );
 
    if( bMark && *rPam.GetPoint() > *rPam.GetMark())
    {
        rPam.Exchange();
    }
 
    // aRefMark was copied into the document pool; now retrieve real format...
    SwTextAttr * pTextAttr(nullptr);
    if (bMark)
    {
        // #i107672#
        // ensure that we do not retrieve a different mark at the same position
        std::vector<SwTextAttr *> const newMarks(
            rPam.GetNode().GetTextNode()->GetTextAttrsAt(
                rPam.GetPoint()->nContent.GetIndex(), RES_TXTATR_REFMARK));
        std::vector<SwTextAttr *>::const_iterator const iter(
            std::find_if(newMarks.begin(), newMarks.end(),
                NotContainedIn<SwTextAttr *>(oldMarks)));
        assert(newMarks.end() != iter);
        if (newMarks.end() != iter)
        {
            pTextAttr = *iter;
        }
    }
    else
    {
        SwTextNode *pTextNd = rPam.GetNode().GetTextNode();
        assert(pTextNd);
        pTextAttr = pTextNd ? rPam.GetNode().GetTextNode()->GetTextAttrForCharAt(
                rPam.GetPoint()->nContent.GetIndex() - 1, RES_TXTATR_REFMARK) : nullptr;
    }
 
    if (!pTextAttr)
    {
        throw uno::RuntimeException(
            "SwXReferenceMark::InsertRefMark(): cannot insert attribute", nullptr);
    }
 
    m_pMarkFormat = &pTextAttr->GetRefMark();
    EndListeningAll();
    StartListening(const_cast<SwFormatRefMark*>(m_pMarkFormat)->GetNotifier());
}
 
void SAL_CALL
SwXReferenceMark::attach(const uno::Reference< text::XTextRange > & xTextRange)
{
    SolarMutexGuard aGuard;
 
    if (!m_pImpl->m_bIsDescriptor)
    {
        throw uno::RuntimeException();
    }
    uno::Reference<lang::XUnoTunnel> xRangeTunnel( xTextRange, uno::UNO_QUERY);
    SwXTextRange* pRange = nullptr;
    OTextCursorHelper* pCursor = nullptr;
    if(xRangeTunnel.is())
    {
        pRange = ::sw::UnoTunnelGetImplementation<SwXTextRange>(xRangeTunnel);
        pCursor =
            ::sw::UnoTunnelGetImplementation<OTextCursorHelper>(xRangeTunnel);
    }
    SwDoc *const pDocument =
        pRange ? &pRange->GetDoc() : (pCursor ? pCursor->GetDoc() : nullptr);
    if (!pDocument)
    {
        throw lang::IllegalArgumentException();
    }
 
    SwUnoInternalPaM aPam(*pDocument);
    // this now needs to return TRUE
    ::sw::XTextRangeToSwPaM(aPam, xTextRange);
    m_pImpl->InsertRefMark(aPam, dynamic_cast<SwXTextCursor*>(pCursor));
    m_pImpl->m_bIsDescriptor = false;
    m_pImpl->m_pDoc = pDocument;
}
 
uno::Reference< text::XTextRange > SAL_CALL
SwXReferenceMark::getAnchor()
{
    SolarMutexGuard aGuard;
 
    if (m_pImpl->IsValid())
    {
        SwFormatRefMark const*const pNewMark =
            m_pImpl->m_pDoc->GetRefMark(m_pImpl->m_sMarkName);
        if (pNewMark && (pNewMark == m_pImpl->m_pMarkFormat))
        {
            SwTextRefMark const*const pTextMark =
                m_pImpl->m_pMarkFormat->GetTextRefMark();
            if (pTextMark &&
                (&pTextMark->GetTextNode().GetNodes() ==
                    &m_pImpl->m_pDoc->GetNodes()))
            {
                SwTextNode const& rTextNode = pTextMark->GetTextNode();
                const std::unique_ptr<SwPaM> pPam( (pTextMark->End())
                    ?   new SwPaM( rTextNode, *pTextMark->End(),
                                   rTextNode, pTextMark->GetStart())
                    :   new SwPaM( rTextNode, pTextMark->GetStart()) );
 
                return SwXTextRange::CreateXTextRange(
                            *m_pImpl->m_pDoc, *pPam->Start(), pPam->End());
            }
        }
    }
    return nullptr;
}
 
void SAL_CALL SwXReferenceMark::dispose()
{
    SolarMutexGuard aGuard;
    if (m_pImpl->IsValid())
    {
        SwFormatRefMark const*const pNewMark =
            m_pImpl->m_pDoc->GetRefMark(m_pImpl->m_sMarkName);
        if (pNewMark && (pNewMark == m_pImpl->m_pMarkFormat))
        {
            SwTextRefMark const*const pTextMark =
                m_pImpl->m_pMarkFormat->GetTextRefMark();
            if (pTextMark &&
                (&pTextMark->GetTextNode().GetNodes() ==
                    &m_pImpl->m_pDoc->GetNodes()))
            {
                SwTextNode const& rTextNode = pTextMark->GetTextNode();
                const sal_Int32 nStt = pTextMark->GetStart();
                const sal_Int32 nEnd = pTextMark->End()
                                  ? *pTextMark->End()
                                  : nStt + 1;
 
                SwPaM aPam( rTextNode, nStt, rTextNode, nEnd );
                m_pImpl->m_pDoc->getIDocumentContentOperations().DeleteAndJoin( aPam );
            }
        }
    }
    else if (m_pImpl->m_bIsDescriptor)
    {
        m_pImpl->Invalidate();
    }
}
 
void SAL_CALL SwXReferenceMark::addEventListener(
        const uno::Reference< lang::XEventListener > & xListener)
{
    // no need to lock here as m_pImpl is const and container threadsafe
    m_pImpl->m_EventListeners.addInterface(xListener);
}
 
void SAL_CALL SwXReferenceMark::removeEventListener(
        const uno::Reference< lang::XEventListener > & xListener)
{
    // no need to lock here as m_pImpl is const and container threadsafe
    m_pImpl->m_EventListeners.removeInterface(xListener);
}
 
OUString SAL_CALL SwXReferenceMark::getName()
{
    SolarMutexGuard aGuard;
    if (!m_pImpl->IsValid() ||
        !m_pImpl->m_pDoc->GetRefMark(m_pImpl->m_sMarkName))
    {
        throw uno::RuntimeException();
    }
    return m_pImpl->m_sMarkName;
}
 
void SAL_CALL SwXReferenceMark::setName(const OUString& rName)
{
    SolarMutexGuard aGuard;
    if (m_pImpl->m_bIsDescriptor)
    {
        m_pImpl->m_sMarkName = rName;
    }
    else
    {
        if (!m_pImpl->IsValid()
            || !m_pImpl->m_pDoc->GetRefMark(m_pImpl->m_sMarkName)
            || m_pImpl->m_pDoc->GetRefMark(rName))
        {
            throw uno::RuntimeException();
        }
        SwFormatRefMark const*const pCurMark =
            m_pImpl->m_pDoc->GetRefMark(m_pImpl->m_sMarkName);
        if ((rName != m_pImpl->m_sMarkName)
            && pCurMark && (pCurMark == m_pImpl->m_pMarkFormat))
        {
            const UnoActionContext aCont(m_pImpl->m_pDoc);
            SwTextRefMark const*const pTextMark =
                m_pImpl->m_pMarkFormat->GetTextRefMark();
            if (pTextMark &&
                (&pTextMark->GetTextNode().GetNodes() ==
                     &m_pImpl->m_pDoc->GetNodes()))
            {
                SwTextNode const& rTextNode = pTextMark->GetTextNode();
                const sal_Int32 nStt = pTextMark->GetStart();
                const sal_Int32 nEnd = pTextMark->End()
                                        ? *pTextMark->End()
                                        : nStt + 1;
 
                SwPaM aPam( rTextNode, nStt, rTextNode, nEnd );
                // deletes the m_pImpl->m_pDoc member in the SwXReferenceMark!
                m_pImpl->m_pDoc->getIDocumentContentOperations().DeleteAndJoin( aPam );
                // The aPam will keep the correct and functional doc though
 
                m_pImpl->m_sMarkName = rName;
                //create a new one
                m_pImpl->InsertRefMark( aPam, nullptr );
                m_pImpl->m_pDoc = aPam.GetDoc();
            }
        }
    }
}
 
uno::Reference< beans::XPropertySetInfo > SAL_CALL
SwXReferenceMark::getPropertySetInfo()
{
    SolarMutexGuard g;
 
    static uno::Reference< beans::XPropertySetInfo >  xRef =
        aSwMapProvider.GetPropertySet(PROPERTY_MAP_PARAGRAPH_EXTENSIONS)
            ->getPropertySetInfo();
    return xRef;
}
 
void SAL_CALL SwXReferenceMark::setPropertyValue(
    const OUString& /*rPropertyName*/, const uno::Any& /*rValue*/ )
{
    throw lang::IllegalArgumentException();
}
 
uno::Any SAL_CALL
SwXReferenceMark::getPropertyValue(const OUString& rPropertyName)
{
    // does not seem to need SolarMutex
    uno::Any aRet;
    if (! ::sw::GetDefaultTextContentValue(aRet, rPropertyName))
    {
        throw beans::UnknownPropertyException();
    }
    return aRet;
}
 
void SAL_CALL SwXReferenceMark::addPropertyChangeListener(
        const OUString& /*rPropertyName*/,
        const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
{
    OSL_FAIL("SwXReferenceMark::addPropertyChangeListener(): not implemented");
}
 
void SAL_CALL SwXReferenceMark::removePropertyChangeListener(
        const OUString& /*rPropertyName*/,
        const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
{
    OSL_FAIL("SwXReferenceMark::removePropertyChangeListener(): not implemented");
}
 
void SAL_CALL SwXReferenceMark::addVetoableChangeListener(
        const OUString& /*rPropertyName*/,
        const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
{
    OSL_FAIL("SwXReferenceMark::addVetoableChangeListener(): not implemented");
}
 
void SAL_CALL SwXReferenceMark::removeVetoableChangeListener(
    const OUString& /*rPropertyName*/,
    const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
{
    OSL_FAIL("SwXReferenceMark::removeVetoableChangeListener(): not implemented");
}
 
class SwXMetaText : public cppu::OWeakObject, public SwXText
{
private:
    SwXMeta & m_rMeta;
 
    virtual void PrepareForAttach(uno::Reference< text::XTextRange > & xRange,
            const SwPaM & rPam) override;
 
    virtual bool CheckForOwnMemberMeta(const SwPaM & rPam, const bool bAbsorb) override;
 
protected:
    virtual const SwStartNode *GetStartNode() const override;
    virtual uno::Reference< text::XTextCursor >
        CreateCursor() override;
 
public:
    SwXMetaText(SwDoc & rDoc, SwXMeta & rMeta);
 
    /// make available for SwXMeta
    using SwXText::Invalidate;
 
    // XInterface
    virtual void SAL_CALL acquire() throw() override { cppu::OWeakObject::acquire(); }
    virtual void SAL_CALL release() throw() override { cppu::OWeakObject::release(); }
 
    // XTypeProvider
    virtual uno::Sequence< sal_Int8 > SAL_CALL
        getImplementationId() override;
 
    // XText
    virtual uno::Reference< text::XTextCursor >  SAL_CALL
        createTextCursor() override;
    virtual uno::Reference< text::XTextCursor >  SAL_CALL
        createTextCursorByRange(
            const uno::Reference< text::XTextRange > & xTextPosition) override;
 
};
 
SwXMetaText::SwXMetaText(SwDoc & rDoc, SwXMeta & rMeta)
    : SwXText(&rDoc, CursorType::Meta)
    , m_rMeta(rMeta)
{
}
 
const SwStartNode *SwXMetaText::GetStartNode() const
{
    SwXText const * const pParent(
            dynamic_cast<SwXText*>(m_rMeta.GetParentText().get()));
    return pParent ? pParent->GetStartNode() : nullptr;
}
 
void SwXMetaText::PrepareForAttach( uno::Reference<text::XTextRange> & xRange,
        const SwPaM & rPam)
{
    // create a new cursor to prevent modifying SwXTextRange
    xRange = static_cast<text::XWordCursor*>(
        new SwXTextCursor(*GetDoc(), &m_rMeta, CursorType::Meta, *rPam.GetPoint(),
                (rPam.HasMark()) ? rPam.GetMark() : nullptr));
}
 
bool SwXMetaText::CheckForOwnMemberMeta(const SwPaM & rPam, const bool bAbsorb)
{
    return m_rMeta.CheckForOwnMemberMeta(rPam, bAbsorb);
}
 
uno::Reference< text::XTextCursor > SwXMetaText::CreateCursor()
{
    uno::Reference< text::XTextCursor > xRet;
    if (IsValid())
    {
        SwTextNode * pTextNode;
        sal_Int32 nMetaStart;
        sal_Int32 nMetaEnd;
        const bool bSuccess(
                m_rMeta.SetContentRange(pTextNode, nMetaStart, nMetaEnd) );
        if (bSuccess)
        {
            SwPosition aPos(*pTextNode, nMetaStart);
            xRet = static_cast<text::XWordCursor*>(
                    new SwXTextCursor(*GetDoc(), &m_rMeta, CursorType::Meta, aPos));
        }
    }
    return xRet;
}
 
uno::Sequence<sal_Int8> SAL_CALL
SwXMetaText::getImplementationId()
{
    return css::uno::Sequence<sal_Int8>();
}
 
// XText
uno::Reference< text::XTextCursor > SAL_CALL
SwXMetaText::createTextCursor()
{
    return CreateCursor();
}
 
uno::Reference< text::XTextCursor > SAL_CALL
SwXMetaText::createTextCursorByRange(
        const uno::Reference<text::XTextRange> & xTextPosition)
{
    const uno::Reference<text::XTextCursor> xCursor( CreateCursor() );
    xCursor->gotoRange(xTextPosition, false);
    return xCursor;
}
 
// the Meta has a cached list of text portions for its contents
// this list is created by SwXTextPortionEnumeration
// the Meta listens at the SwTextNode and throws away the cache when it changes
class SwXMeta::Impl : public SvtListener
{
private:
    ::osl::Mutex m_Mutex; // just for OInterfaceContainerHelper2
 
public:
    uno::WeakReference<uno::XInterface> m_wThis;
    ::comphelper::OInterfaceContainerHelper2 m_EventListeners;
    std::unique_ptr<const TextRangeList_t> m_pTextPortions;
    // 3 possible states: not attached, attached, disposed
    bool m_bIsDisposed;
    bool m_bIsDescriptor;
    uno::Reference<text::XText> m_xParentText;
    rtl::Reference<SwXMetaText> m_xText;
    sw::Meta* m_pMeta;
 
    Impl(SwXMeta& rThis, SwDoc& rDoc,
            ::sw::Meta* const pMeta,
            uno::Reference<text::XText> const& xParentText,
            std::unique_ptr<TextRangeList_t const> pPortions)
        : m_EventListeners(m_Mutex)
        , m_pTextPortions(std::move(pPortions))
        , m_bIsDisposed(false)
        , m_bIsDescriptor(nullptr == pMeta)
        , m_xParentText(xParentText)
        , m_xText(new SwXMetaText(rDoc, rThis))
        , m_pMeta(pMeta)
    {
        !m_bIsDescriptor && StartListening(m_pMeta->GetNotifier());
    }
 
    inline const ::sw::Meta* GetMeta() const;
    // only for SwXMetaField!
    inline const ::sw::MetaField* GetMetaField() const;
protected:
    virtual void Notify(const SfxHint& rHint) override;
 
};
 
inline const ::sw::Meta* SwXMeta::Impl::GetMeta() const
{
    return m_pMeta;
}
 
// SwModify
void SwXMeta::Impl::Notify(const SfxHint& rHint)
{
    m_pTextPortions.reset(); // throw away cache (SwTextNode changed)
    if(rHint.GetId() == SfxHintId::Dying || rHint.GetId() == SfxHintId::Deinitializing)
    {
        m_bIsDisposed = true;
        m_pMeta = nullptr;
        m_xText->Invalidate();
        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);
        m_EventListeners.disposeAndClear(ev);
    }
}
 
uno::Reference<text::XText> const & SwXMeta::GetParentText() const
{
    return m_pImpl->m_xParentText;
}
 
SwXMeta::SwXMeta(SwDoc *const pDoc, ::sw::Meta *const pMeta,
        uno::Reference<text::XText> const& xParentText,
        std::unique_ptr<TextRangeList_t const> pPortions)
    : m_pImpl( new SwXMeta::Impl(*this, *pDoc, pMeta, xParentText, std::move(pPortions)) )
{
}
 
SwXMeta::SwXMeta(SwDoc *const pDoc)
    : m_pImpl( new SwXMeta::Impl(*this, *pDoc, nullptr, nullptr, nullptr) )
{
}
 
SwXMeta::~SwXMeta()
{
}
 
uno::Reference<rdf::XMetadatable>
SwXMeta::CreateXMeta(SwDoc & rDoc, bool const isField)
{
    SwXMeta *const pXMeta(isField
            ? new SwXMetaField(& rDoc) : new SwXMeta(& rDoc));
    // this is why the constructor is private: need to acquire pXMeta here
    uno::Reference<rdf::XMetadatable> const xMeta(pXMeta);
    // need a permanent Reference to initialize m_wThis
    pXMeta->m_pImpl->m_wThis = xMeta;
    return xMeta;
}
 
uno::Reference<rdf::XMetadatable>
SwXMeta::CreateXMeta(::sw::Meta & rMeta,
            uno::Reference<text::XText> const& i_xParent,
            std::unique_ptr<TextRangeList_t const> && pPortions)
{
    // re-use existing SwXMeta
    // #i105557#: do not iterate over the registered clients: race condition
    uno::Reference<rdf::XMetadatable> xMeta(rMeta.GetXMeta());
    if (xMeta.is())
    {
        if (pPortions.get()) // set cache in the XMeta to the given portions
        {
            const uno::Reference<lang::XUnoTunnel> xUT(xMeta, uno::UNO_QUERY);
            SwXMeta *const pXMeta(
                ::sw::UnoTunnelGetImplementation<SwXMeta>(xUT));
            assert(pXMeta);
            // NB: the meta must always be created with the complete content
            // if SwXTextPortionEnumeration is created for a selection,
            // it must be checked that the Meta is contained in the selection!
            pXMeta->m_pImpl->m_pTextPortions = std::move(pPortions);
            // ??? is this necessary?
            if (pXMeta->m_pImpl->m_xParentText.get() != i_xParent.get())
            {
                SAL_WARN("sw.uno", "SwXMeta with different parent?");
                pXMeta->m_pImpl->m_xParentText.set(i_xParent);
            }
        }
        return xMeta;
    }
 
    // create new SwXMeta
    SwTextNode * const pTextNode( rMeta.GetTextNode() );
    SAL_WARN_IF(!pTextNode, "sw.uno", "CreateXMeta: no text node?");
    if (!pTextNode) { return nullptr; }
    uno::Reference<text::XText> xParentText(i_xParent);
    if (!xParentText.is())
    {
        SwTextMeta * const pTextAttr( rMeta.GetTextAttr() );
        SAL_WARN_IF(!pTextAttr, "sw.uno", "CreateXMeta: no text attr?");
        if (!pTextAttr) { return nullptr; }
        const SwPosition aPos(*pTextNode, pTextAttr->GetStart());
        xParentText.set( ::sw::CreateParentXText(*pTextNode->GetDoc(), aPos) );
    }
    if (!xParentText.is()) { return nullptr; }
    SwXMeta *const pXMeta( (RES_TXTATR_META == rMeta.GetFormatMeta()->Which())
        ? new SwXMeta     (pTextNode->GetDoc(), &rMeta, xParentText,
                            std::move(pPortions))
        : new SwXMetaField(pTextNode->GetDoc(), &rMeta, xParentText,
                            std::move(pPortions)));
    // this is why the constructor is private: need to acquire pXMeta here
    xMeta.set(pXMeta);
    // in order to initialize the weak pointer cache in the core object
    rMeta.SetXMeta(xMeta);
    // need a permanent Reference to initialize m_wThis
    pXMeta->m_pImpl->m_wThis = xMeta;
    return xMeta;
}
 
bool SwXMeta::SetContentRange(
        SwTextNode *& rpNode, sal_Int32 & rStart, sal_Int32 & rEnd ) const
{
    ::sw::Meta const * const pMeta( m_pImpl->GetMeta() );
    if (pMeta)
    {
        SwTextMeta const * const pTextAttr( pMeta->GetTextAttr() );
        if (pTextAttr)
        {
            rpNode = pMeta->GetTextNode();
            if (rpNode)
            {
                // rStart points at the first position _within_ the meta!
                rStart = pTextAttr->GetStart() + 1;
                rEnd = *pTextAttr->End();
                return true;
            }
        }
    }
    return false;
}
 
bool SwXMeta::CheckForOwnMemberMeta(const SwPaM & rPam, const bool bAbsorb)
{
    SwTextNode * pTextNode;
    sal_Int32 nMetaStart;
    sal_Int32 nMetaEnd;
    const bool bSuccess( SetContentRange(pTextNode, nMetaStart, nMetaEnd) );
    OSL_ENSURE(bSuccess, "no pam?");
    if (!bSuccess)
        throw lang::DisposedException();
 
    SwPosition const * const pStartPos( rPam.Start() );
    if (&pStartPos->nNode.GetNode() != pTextNode)
    {
        throw lang::IllegalArgumentException(
            "trying to insert into a nesting text content, but start "
                "of text range not in same paragraph as text content",
                nullptr, 0);
    }
    bool bForceExpandHints(false);
    const sal_Int32 nStartPos(pStartPos->nContent.GetIndex());
    // not <= but < because nMetaStart is behind dummy char!
    // not >= but > because == means insert at end!
    if ((nStartPos < nMetaStart) || (nStartPos > nMetaEnd))
    {
        throw lang::IllegalArgumentException(
            "trying to insert into a nesting text content, but start "
                "of text range not inside text content",
                nullptr, 0);
    }
    else if (nStartPos == nMetaEnd)
    {
        bForceExpandHints = true;
    }
    if (rPam.HasMark() && bAbsorb)
    {
        SwPosition const * const pEndPos( rPam.End() );
        if (&pEndPos->nNode.GetNode() != pTextNode)
        {
            throw lang::IllegalArgumentException(
                "trying to insert into a nesting text content, but end "
                    "of text range not in same paragraph as text content",
                    nullptr, 0);
        }
        const sal_Int32 nEndPos(pEndPos->nContent.GetIndex());
        // not <= but < because nMetaStart is behind dummy char!
        // not >= but > because == means insert at end!
        if ((nEndPos < nMetaStart) || (nEndPos > nMetaEnd))
        {
            throw lang::IllegalArgumentException(
                "trying to insert into a nesting text content, but end "
                    "of text range not inside text content",
                    nullptr, 0);
        }
        else if (nEndPos == nMetaEnd)
        {
            bForceExpandHints = true;
        }
    }
    return bForceExpandHints;
}
 
namespace
{
    class theSwXMetaUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXMetaUnoTunnelId > {};
}
 
const uno::Sequence< sal_Int8 > & SwXMeta::getUnoTunnelId()
{
    return theSwXMetaUnoTunnelId::get().getSeq();
}
 
// XUnoTunnel
sal_Int64 SAL_CALL
SwXMeta::getSomething( const uno::Sequence< sal_Int8 > & i_rId )
{
    return ::sw::UnoTunnelImpl<SwXMeta>(i_rId, this);
}
 
// XServiceInfo
OUString SAL_CALL
SwXMeta::getImplementationName()
{
    return OUString("SwXMeta");
}
 
static char const*const g_ServicesMeta[] =
{
    "com.sun.star.text.TextContent",
    "com.sun.star.text.InContentMetadata",
};
 
static const size_t g_nServicesMeta(SAL_N_ELEMENTS(g_ServicesMeta));
 
sal_Bool SAL_CALL
SwXMeta::supportsService(const OUString& rServiceName)
{
    return cppu::supportsService(this, rServiceName);
}
 
uno::Sequence< OUString > SAL_CALL
SwXMeta::getSupportedServiceNames()
{
    return ::sw::GetSupportedServiceNamesImpl(g_nServicesMeta, g_ServicesMeta);
}
 
// XComponent
void SAL_CALL
SwXMeta::addEventListener(
        uno::Reference< lang::XEventListener> const & xListener )
{
    // no need to lock here as m_pImpl is const and container threadsafe
    m_pImpl->m_EventListeners.addInterface(xListener);
}
 
void SAL_CALL
SwXMeta::removeEventListener(
        uno::Reference< lang::XEventListener> const & xListener )
{
    // no need to lock here as m_pImpl is const and container threadsafe
    m_pImpl->m_EventListeners.removeInterface(xListener);
}
 
void SAL_CALL
SwXMeta::dispose()
{
    SolarMutexGuard g;
 
    if (m_pImpl->m_bIsDescriptor)
    {
        m_pImpl->m_pTextPortions.reset();
        lang::EventObject const ev(static_cast< ::cppu::OWeakObject&>(*this));
        m_pImpl->m_EventListeners.disposeAndClear(ev);
        m_pImpl->m_bIsDisposed = true;
        m_pImpl->m_xText->Invalidate();
    }
    else if (!m_pImpl->m_bIsDisposed)
    {
        SwTextNode * pTextNode;
        sal_Int32 nMetaStart;
        sal_Int32 nMetaEnd;
        const bool bSuccess(SetContentRange(pTextNode, nMetaStart, nMetaEnd));
        OSL_ENSURE(bSuccess, "no pam?");
        if (bSuccess)
        {
            // -1 because of CH_TXTATR
            SwPaM aPam( *pTextNode, nMetaStart - 1, *pTextNode, nMetaEnd );
            SwDoc * const pDoc( pTextNode->GetDoc() );
            pDoc->getIDocumentContentOperations().DeleteAndJoin( aPam );
 
            // removal should call Modify and do the dispose
            assert(m_pImpl->m_bIsDisposed);
        }
    }
}
 
void
SwXMeta::AttachImpl(const uno::Reference< text::XTextRange > & i_xTextRange,
        const sal_uInt16 i_nWhich)
{
    SolarMutexGuard g;
 
    if (m_pImpl->m_bIsDisposed)
    {
        throw lang::DisposedException();
    }
    if (!m_pImpl->m_bIsDescriptor)
    {
        throw uno::RuntimeException(
            "SwXMeta::attach(): already attached",
            static_cast< ::cppu::OWeakObject* >(this));
    }
 
    uno::Reference<lang::XUnoTunnel> xRangeTunnel(i_xTextRange, uno::UNO_QUERY);
    if (!xRangeTunnel.is())
    {
        throw lang::IllegalArgumentException(
            "SwXMeta::attach(): argument is no XUnoTunnel",
            static_cast< ::cppu::OWeakObject* >(this), 0);
    }
    SwXTextRange *const pRange(
            ::sw::UnoTunnelGetImplementation<SwXTextRange>(xRangeTunnel));
    OTextCursorHelper *const pCursor( pRange ? nullptr :
            ::sw::UnoTunnelGetImplementation<OTextCursorHelper>(xRangeTunnel));
    if (!pRange && !pCursor)
    {
        throw lang::IllegalArgumentException(
            "SwXMeta::attach(): argument not supported type",
            static_cast< ::cppu::OWeakObject* >(this), 0);
    }
 
    SwDoc * const pDoc(
            pRange ? &pRange->GetDoc() : pCursor->GetDoc());
    if (!pDoc)
    {
        throw lang::IllegalArgumentException(
            "SwXMeta::attach(): argument has no SwDoc",
            static_cast< ::cppu::OWeakObject* >(this), 0);
    }
 
    SwUnoInternalPaM aPam(*pDoc);
    ::sw::XTextRangeToSwPaM(aPam, i_xTextRange);
 
    UnoActionContext aContext(pDoc);
 
    SwXTextCursor const*const pTextCursor(
            dynamic_cast<SwXTextCursor*>(pCursor));
    const bool bForceExpandHints(pTextCursor && pTextCursor->IsAtEndOfMeta());
    const SetAttrMode nInsertFlags( bForceExpandHints
        ?   ( SetAttrMode::FORCEHINTEXPAND
            | SetAttrMode::DONTEXPAND)
        : SetAttrMode::DONTEXPAND );
 
    const std::shared_ptr< ::sw::Meta> pMeta( (RES_TXTATR_META == i_nWhich)
        ? std::make_shared< ::sw::Meta>( nullptr )
        : std::shared_ptr< ::sw::Meta>(
            pDoc->GetMetaFieldManager().makeMetaField()) );
    SwFormatMeta meta(pMeta, i_nWhich); // this is cloned by Insert!
    const bool bSuccess( pDoc->getIDocumentContentOperations().InsertPoolItem( aPam, meta, nInsertFlags ) );
    SwTextAttr * const pTextAttr( pMeta->GetTextAttr() );
    if (!bSuccess)
    {
        throw lang::IllegalArgumentException(
            "SwXMeta::attach(): cannot create meta: range invalid?",
            static_cast< ::cppu::OWeakObject* >(this), 1);
    }
    if (!pTextAttr)
    {
        OSL_FAIL("meta inserted, but has no text attribute?");
        throw uno::RuntimeException(
            "SwXMeta::attach(): cannot create meta",
            static_cast< ::cppu::OWeakObject* >(this));
    }
 
    m_pImpl->EndListeningAll();
    m_pImpl->m_pMeta = pMeta.get();
    m_pImpl->StartListening(pMeta->GetNotifier());
    pMeta->SetXMeta(uno::Reference<rdf::XMetadatable>(this));
 
    m_pImpl->m_xParentText = ::sw::CreateParentXText(*pDoc, *aPam.GetPoint());
 
    m_pImpl->m_bIsDescriptor = false;
}
 
// XTextContent
void SAL_CALL
SwXMeta::attach(const uno::Reference< text::XTextRange > & i_xTextRange)
{
    return SwXMeta::AttachImpl(i_xTextRange, RES_TXTATR_META);
}
 
uno::Reference< text::XTextRange > SAL_CALL
SwXMeta::getAnchor()
{
    SolarMutexGuard g;
 
    if (m_pImpl->m_bIsDisposed)
    {
        throw lang::DisposedException();
    }
    if (m_pImpl->m_bIsDescriptor)
    {
        throw uno::RuntimeException(
                "SwXMeta::getAnchor(): not inserted",
                static_cast< ::cppu::OWeakObject* >(this));
    }
 
    SwTextNode * pTextNode;
    sal_Int32 nMetaStart;
    sal_Int32 nMetaEnd;
    const bool bSuccess(SetContentRange(pTextNode, nMetaStart, nMetaEnd));
    OSL_ENSURE(bSuccess, "no pam?");
    if (!bSuccess)
    {
        throw lang::DisposedException(
                "SwXMeta::getAnchor(): not attached",
                static_cast< ::cppu::OWeakObject* >(this));
    }
 
    const SwPosition start(*pTextNode, nMetaStart - 1); // -1 due to CH_TXTATR
    const SwPosition end(*pTextNode, nMetaEnd);
    return SwXTextRange::CreateXTextRange(*pTextNode->GetDoc(), start, &end);
}
 
// XTextRange
uno::Reference< text::XText > SAL_CALL
SwXMeta::getText()
{
    return this;
}
 
uno::Reference< text::XTextRange > SAL_CALL
SwXMeta::getStart()
{
    SolarMutexGuard g;
    return m_pImpl->m_xText->getStart();
}
 
uno::Reference< text::XTextRange > SAL_CALL
SwXMeta::getEnd()
{
    SolarMutexGuard g;
    return m_pImpl->m_xText->getEnd();
}
 
OUString SAL_CALL
SwXMeta::getString()
{
    SolarMutexGuard g;
    return m_pImpl->m_xText->getString();
}
 
void SAL_CALL
SwXMeta::setString(const OUString& rString)
{
    SolarMutexGuard g;
    return m_pImpl->m_xText->setString(rString);
}
 
// XSimpleText
uno::Reference< text::XTextCursor > SAL_CALL
SwXMeta::createTextCursor()
{
    SolarMutexGuard g;
    return m_pImpl->m_xText->createTextCursor();
}
 
uno::Reference< text::XTextCursor > SAL_CALL
SwXMeta::createTextCursorByRange(
        const uno::Reference<text::XTextRange> & xTextPosition)
{
    SolarMutexGuard g;
    return m_pImpl->m_xText->createTextCursorByRange(xTextPosition);
}
 
void SAL_CALL
SwXMeta::insertString(const uno::Reference<text::XTextRange> & xRange,
        const OUString& rString, sal_Bool bAbsorb)
{
    SolarMutexGuard g;
    return m_pImpl->m_xText->insertString(xRange, rString, bAbsorb);
}
 
void SAL_CALL
SwXMeta::insertControlCharacter(const uno::Reference<text::XTextRange> & xRange,
        sal_Int16 nControlCharacter, sal_Bool bAbsorb)
{
    SolarMutexGuard g;
    return m_pImpl->m_xText->insertControlCharacter(xRange, nControlCharacter,
                bAbsorb);
}
 
// XText
void SAL_CALL
SwXMeta::insertTextContent( const uno::Reference<text::XTextRange> & xRange,
        const uno::Reference<text::XTextContent> & xContent, sal_Bool bAbsorb)
{
    SolarMutexGuard g;
    return m_pImpl->m_xText->insertTextContent(xRange, xContent, bAbsorb);
}
 
void SAL_CALL
SwXMeta::removeTextContent(
        const uno::Reference< text::XTextContent > & xContent)
{
    SolarMutexGuard g;
    return m_pImpl->m_xText->removeTextContent(xContent);
}
 
// XChild
uno::Reference< uno::XInterface > SAL_CALL
SwXMeta::getParent()
{
    SolarMutexGuard g;
    SwTextNode * pTextNode;
    sal_Int32 nMetaStart;
    sal_Int32 nMetaEnd;
    bool const bSuccess( SetContentRange(pTextNode, nMetaStart, nMetaEnd) );
    OSL_ENSURE(bSuccess, "no pam?");
    if (!bSuccess) { throw lang::DisposedException(); }
    // in order to prevent getting this meta, subtract 1 from nMetaStart;
    // so we get the index of the dummy character, and we exclude it
    // by calling GetTextAttrAt(_, _, PARENT) in GetNestedTextContent
    uno::Reference<text::XTextContent> const xRet(
        SwUnoCursorHelper::GetNestedTextContent(*pTextNode, nMetaStart - 1,
            true) );
    return xRet;
}
 
void SAL_CALL
SwXMeta::setParent(uno::Reference< uno::XInterface > const& /*xParent*/)
{
    throw lang::NoSupportException("setting parent not supported", *this);
}
 
// XElementAccess
uno::Type SAL_CALL
SwXMeta::getElementType()
{
    return cppu::UnoType<text::XTextRange>::get();
}
 
sal_Bool SAL_CALL SwXMeta::hasElements()
{
    SolarMutexGuard g;
    return m_pImpl->m_pMeta != nullptr;
}
 
// XEnumerationAccess
uno::Reference< container::XEnumeration > SAL_CALL
SwXMeta::createEnumeration()
{
    SolarMutexGuard g;
 
    if (m_pImpl->m_bIsDisposed)
    {
        throw lang::DisposedException();
    }
    if (m_pImpl->m_bIsDescriptor)
    {
        throw uno::RuntimeException(
                "createEnumeration(): not inserted",
                static_cast< ::cppu::OWeakObject* >(this));
    }
 
    SwTextNode * pTextNode;
    sal_Int32 nMetaStart;
    sal_Int32 nMetaEnd;
    const bool bSuccess(SetContentRange(pTextNode, nMetaStart, nMetaEnd));
    OSL_ENSURE(bSuccess, "no pam?");
    if (!bSuccess)
        throw lang::DisposedException();
 
    SwPaM aPam(*pTextNode, nMetaStart);
 
    if (!m_pImpl->m_pTextPortions.get())
    {
        return new SwXTextPortionEnumeration(
                    aPam, GetParentText(), nMetaStart, nMetaEnd);
    }
    else // cached!
    {
        return new SwXTextPortionEnumeration(aPam, *m_pImpl->m_pTextPortions);
    }
}
 
// MetadatableMixin
::sfx2::Metadatable* SwXMeta::GetCoreObject()
{
    return const_cast< ::sw::Meta * >(m_pImpl->GetMeta());
}
 
uno::Reference<frame::XModel> SwXMeta::GetModel()
{
    ::sw::Meta const * const pMeta( m_pImpl->GetMeta() );
    if (pMeta)
    {
        SwTextNode const * const pTextNode( pMeta->GetTextNode() );
        if (pTextNode)
        {
            SwDocShell const * const pShell(pTextNode->GetDoc()->GetDocShell());
            return pShell ? pShell->GetModel() : nullptr;
        }
    }
    return nullptr;
}
 
inline const ::sw::MetaField* SwXMeta::Impl::GetMetaField() const
{
    return dynamic_cast<sw::MetaField*>(m_pMeta);
}
 
SwXMetaField::SwXMetaField(SwDoc *const pDoc, ::sw::Meta *const pMeta,
        uno::Reference<text::XText> const& xParentText,
        std::unique_ptr<TextRangeList_t const> pPortions)
    : SwXMetaField_Base(pDoc, pMeta, xParentText, std::move(pPortions))
{
    OSL_ENSURE(dynamic_cast< ::sw::MetaField* >(pMeta),
        "SwXMetaField created for wrong hint!");
}
 
SwXMetaField::SwXMetaField(SwDoc *const pDoc)
    :  SwXMetaField_Base(pDoc)
{
}
 
SwXMetaField::~SwXMetaField()
{
}
 
// XServiceInfo
OUString SAL_CALL
SwXMetaField::getImplementationName()
{
    return OUString("SwXMetaField");
}
 
static char const*const g_ServicesMetaField[] =
{
    "com.sun.star.text.TextContent",
    "com.sun.star.text.TextField",
    "com.sun.star.text.textfield.MetadataField",
};
 
static const size_t g_nServicesMetaField(SAL_N_ELEMENTS(g_ServicesMetaField));
 
sal_Bool SAL_CALL
SwXMetaField::supportsService(const OUString& rServiceName)
{
    return cppu::supportsService(this, rServiceName);
}
 
uno::Sequence< OUString > SAL_CALL
SwXMetaField::getSupportedServiceNames()
{
    return ::sw::GetSupportedServiceNamesImpl(
            g_nServicesMetaField, g_ServicesMetaField);
}
 
// XComponent
void SAL_CALL
SwXMetaField::addEventListener(
        uno::Reference< lang::XEventListener> const & xListener )
{
    return SwXMeta::addEventListener(xListener);
}
 
void SAL_CALL
SwXMetaField::removeEventListener(
        uno::Reference< lang::XEventListener> const & xListener )
{
    return SwXMeta::removeEventListener(xListener);
}
 
void SAL_CALL
SwXMetaField::dispose()
{
    return SwXMeta::dispose();
}
 
// XTextContent
void SAL_CALL
SwXMetaField::attach(const uno::Reference< text::XTextRange > & i_xTextRange)
{
    return SwXMeta::AttachImpl(i_xTextRange, RES_TXTATR_METAFIELD);
}
 
uno::Reference< text::XTextRange > SAL_CALL
SwXMetaField::getAnchor()
{
    return SwXMeta::getAnchor();
}
 
// XPropertySet
uno::Reference< beans::XPropertySetInfo > SAL_CALL
SwXMetaField::getPropertySetInfo()
{
    SolarMutexGuard g;
 
    static uno::Reference< beans::XPropertySetInfo > xRef(
        aSwMapProvider.GetPropertySet(PROPERTY_MAP_METAFIELD)
            ->getPropertySetInfo() );
    return xRef;
}
 
void SAL_CALL
SwXMetaField::setPropertyValue(
        const OUString& rPropertyName, const uno::Any& rValue)
{
    SolarMutexGuard g;
 
    ::sw::MetaField * const pMeta(
            const_cast< ::sw::MetaField * >(m_pImpl->GetMetaField()) );
    if (!pMeta)
        throw lang::DisposedException();
 
    if ( rPropertyName == "NumberFormat" )
    {
        sal_Int32 nNumberFormat(0);
        if (rValue >>= nNumberFormat)
        {
            pMeta->SetNumberFormat(static_cast<sal_uInt32>(nNumberFormat));
        }
    }
    else if ( rPropertyName == "IsFixedLanguage" )
    {
        bool b(false);
        if (rValue >>= b)
        {
            pMeta->SetIsFixedLanguage(b);
        }
    }
    else
    {
        throw beans::UnknownPropertyException();
    }
}
 
uno::Any SAL_CALL
SwXMetaField::getPropertyValue(const OUString& rPropertyName)
{
    SolarMutexGuard g;
 
    ::sw::MetaField const * const pMeta( m_pImpl->GetMetaField() );
    if (!pMeta)
        throw lang::DisposedException();
 
    uno::Any any;
 
    if ( rPropertyName == "NumberFormat" )
    {
        const OUString text( getPresentation(false) );
        any <<= static_cast<sal_Int32>(pMeta->GetNumberFormat(text));
    }
    else if ( rPropertyName == "IsFixedLanguage" )
    {
        any <<= pMeta->IsFixedLanguage();
    }
    else
    {
        throw beans::UnknownPropertyException();
    }
 
    return any;
}
 
void SAL_CALL
SwXMetaField::addPropertyChangeListener(
        const OUString& /*rPropertyName*/,
        const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
{
    OSL_FAIL("SwXMetaField::addPropertyChangeListener(): not implemented");
}
 
void SAL_CALL
SwXMetaField::removePropertyChangeListener(
        const OUString& /*rPropertyName*/,
        const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
{
    OSL_FAIL("SwXMetaField::removePropertyChangeListener(): not implemented");
}
 
void SAL_CALL
SwXMetaField::addVetoableChangeListener(
        const OUString& /*rPropertyName*/,
        const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
{
    OSL_FAIL("SwXMetaField::addVetoableChangeListener(): not implemented");
}
 
void SAL_CALL
SwXMetaField::removeVetoableChangeListener(
        const OUString& /*rPropertyName*/,
        const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
{
    OSL_FAIL("SwXMetaField::removeVetoableChangeListener(): not implemented");
}
 
static uno::Reference<rdf::XURI> const&
lcl_getURI(const bool bPrefix)
{
    static uno::Reference< uno::XComponentContext > xContext(
        ::comphelper::getProcessComponentContext());
    static uno::Reference< rdf::XURI > xOdfPrefix(
        rdf::URI::createKnown(xContext, rdf::URIs::ODF_PREFIX),
        uno::UNO_SET_THROW);
    static uno::Reference< rdf::XURI > xOdfSuffix(
        rdf::URI::createKnown(xContext, rdf::URIs::ODF_SUFFIX),
        uno::UNO_SET_THROW);
    return bPrefix ? xOdfPrefix : xOdfSuffix;
}
 
static OUString
lcl_getPrefixOrSuffix(
    uno::Reference<rdf::XRepository> const & xRepository,
    uno::Reference<rdf::XResource> const & xMetaField,
    uno::Reference<rdf::XURI> const & xPredicate)
{
    const uno::Reference<container::XEnumeration> xEnum(
        xRepository->getStatements(xMetaField, xPredicate, nullptr),
        uno::UNO_SET_THROW);
    while (xEnum->hasMoreElements()) {
        rdf::Statement stmt;
        if (!(xEnum->nextElement() >>= stmt)) {
            throw uno::RuntimeException();
        }
        const uno::Reference<rdf::XLiteral> xObject(stmt.Object,
            uno::UNO_QUERY);
        if (!xObject.is()) continue;
        if (xEnum->hasMoreElements()) {
            SAL_INFO("sw.uno", "ignoring other odf:Prefix/odf:Suffix statements");
        }
        return xObject->getValue();
    }
    return OUString();
}
 
void
getPrefixAndSuffix(
        const uno::Reference<frame::XModel>& xModel,
        const uno::Reference<rdf::XMetadatable>& xMetaField,
        OUString *const o_pPrefix, OUString *const o_pSuffix)
{
    try {
        const uno::Reference<rdf::XRepositorySupplier> xRS(
                xModel, uno::UNO_QUERY_THROW);
        const uno::Reference<rdf::XRepository> xRepo(
                xRS->getRDFRepository(), uno::UNO_SET_THROW);
        const uno::Reference<rdf::XResource> xMeta(
                xMetaField, uno::UNO_QUERY_THROW);
        if (o_pPrefix)
        {
            *o_pPrefix = lcl_getPrefixOrSuffix(xRepo, xMeta, lcl_getURI(true));
        }
        if (o_pSuffix)
        {
            *o_pSuffix = lcl_getPrefixOrSuffix(xRepo, xMeta, lcl_getURI(false));
        }
    } catch (uno::RuntimeException &) {
        throw;
    } catch (const uno::Exception &) {
        css::uno::Any anyEx = cppu::getCaughtException();
        throw lang::WrappedTargetRuntimeException("getPrefixAndSuffix: exception", nullptr, anyEx);
    }
}
 
// XTextField
OUString SAL_CALL
SwXMetaField::getPresentation(sal_Bool bShowCommand)
{
    SolarMutexGuard g;
 
    if (bShowCommand)
    {
//FIXME ?
        return OUString();
    }
    else
    {
        // getString should check if this is invalid
        const OUString content( getString() );
        OUString prefix;
        OUString suffix;
        getPrefixAndSuffix(GetModel(), this, &prefix, &suffix);
        return prefix + content + suffix;
    }
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V572 It is odd that the object which was created using 'new' operator is immediately cast to another type.

V572 It is odd that the object which was created using 'new' operator is immediately cast to another type.