/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 * 
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sw.hxx"

#include <osl/mutex.hxx>
#include <vcl/svapp.hxx>

#include <unomid.h>
#include <unotextrange.hxx>
#include <unorefmark.hxx>
#include <unotextcursor.hxx>
#include <unomap.hxx>
#include <unocrsr.hxx>
#include <unoevtlstnr.hxx>
#include <unocrsrhelper.hxx>
#include <doc.hxx>
#include <ndtxt.hxx>
#include <fmtrfmrk.hxx>
#include <txtrfmrk.hxx>
#include <hints.hxx>


using namespace ::com::sun::star;
using ::rtl::OUString;

/******************************************************************
 * SwXReferenceMark
 ******************************************************************/
class SwXReferenceMark::Impl
    : public SwClient
{

public:
    SwEventListenerContainer    m_ListenerContainer;
    bool                        m_bIsDescriptor;
    SwDoc *                     m_pDoc;
    const SwFmtRefMark *        m_pMarkFmt;
    ::rtl::OUString             m_sMarkName;

    Impl(   SwXReferenceMark & rThis,
            SwDoc *const pDoc, SwFmtRefMark const*const pRefMark)
        : SwClient((pDoc) ? pDoc->GetUnoCallBack() : 0)
        , m_ListenerContainer(static_cast< ::cppu::OWeakObject* >(&rThis))
        // #i111177# unxsols4 (Sun C++ 5.9 SunOS_sparc) may generate wrong code
        , m_bIsDescriptor((0 == pRefMark) ? true : false)
        , m_pDoc(pDoc)
        , m_pMarkFmt(pRefMark)
    {
        if (pRefMark)
        {
            m_sMarkName = pRefMark->GetRefName();
        }
    }

    bool    IsValid() const { return 0 != GetRegisteredIn(); }
    void    InsertRefMark( SwPaM & rPam, SwXTextCursor const*const pCursor );
    void    Invalidate();
protected:
    // SwClient
    virtual void    Modify( const SfxPoolItem* pOld, const SfxPoolItem *pNew);

};

void SwXReferenceMark::Impl::Invalidate()
{
    if (IsValid())
    {
        const_cast<SwModify*>(GetRegisteredIn())->Remove(this);
    }
    m_ListenerContainer.Disposing();
    m_pDoc = 0;
    m_pMarkFmt = 0;
}

void SwXReferenceMark::Impl::Modify( const SfxPoolItem* pOld, const SfxPoolItem *pNew)
{
    ClientModify(this, pOld, pNew);

    if (!GetRegisteredIn()) // removed => dispose
    {
        Invalidate();
    }
    else if (pOld)
    {
        switch (pOld->Which())
        {
            case RES_REFMARK_DELETED:
                if (static_cast<const void*>(m_pMarkFmt) ==
                        static_cast<const SwPtrMsgPoolItem *>(pOld)->pObject)
                {
                    Invalidate();
                }
                break;
        }
    }
}

SwXReferenceMark::SwXReferenceMark(
        SwDoc *const pDoc, SwFmtRefMark const*const pRefMark)
    : m_pImpl( new SwXReferenceMark::Impl(*this, pDoc, pRefMark) )
{
}

SwXReferenceMark::~SwXReferenceMark()
{
}

SwXReferenceMark *
SwXReferenceMark::GetReferenceMark(
        SwModify const& /*rUnoCB*/, SwFmtRefMark const& /*rMarkFmt*/)
{
    // #i105557#: do not iterate over the registered clients: race condition
    // to do this properly requires the SwXReferenceMark to register at the
    // SwFmtRefMark directly, not at the unocallback
    return 0;
}

SwXReferenceMark *
SwXReferenceMark::CreateXReferenceMark(
        SwDoc & rDoc, SwFmtRefMark const& rMarkFmt)
{
    SwXReferenceMark *const pXMark(
        GetReferenceMark(*rDoc.GetUnoCallBack(), rMarkFmt) );
    return (pXMark)
        ?   pXMark
        :   new SwXReferenceMark(&rDoc, &rMarkFmt);
}

const uno::Sequence< sal_Int8 > & SwXReferenceMark::getUnoTunnelId()
{
    static uno::Sequence< sal_Int8 > aSeq = ::CreateUnoTunnelId();
    return aSeq;
}

sal_Int64 SAL_CALL
SwXReferenceMark::getSomething(const uno::Sequence< sal_Int8 >& rId)
throw (uno::RuntimeException)
{
    return ::sw::UnoTunnelImpl<SwXReferenceMark>(rId, this);
}

OUString SAL_CALL SwXReferenceMark::getImplementationName()
throw (uno::RuntimeException)
{
    return C2U("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)
throw (uno::RuntimeException)
{
    return ::sw::SupportsServiceImpl(
            g_nServicesReferenceMark, g_ServicesReferenceMark, rServiceName);
}

uno::Sequence< OUString > SAL_CALL
SwXReferenceMark::getSupportedServiceNames()
throw (uno::RuntimeException)
{
    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);
    SwFmtRefMark aRefMark(m_sMarkName);
    sal_Bool bMark = *rPam.GetPoint() != *rPam.GetMark();

    const bool bForceExpandHints( (!bMark && pCursor)
            ? pCursor->IsAtEndOfMeta() : false );
    const SetAttrMode nInsertFlags = (bForceExpandHints)
        ?   ( nsSetAttrMode::SETATTR_FORCEHINTEXPAND
            | nsSetAttrMode::SETATTR_DONTEXPAND)
        : nsSetAttrMode::SETATTR_DONTEXPAND;

    ::std::vector<SwTxtAttr *> oldMarks;
    if (bMark)
    {
        oldMarks = rPam.GetNode()->GetTxtNode()->GetTxtAttrsAt(
            rPam.GetPoint()->nContent.GetIndex(), RES_TXTATR_REFMARK);
    }

    pDoc2->InsertPoolItem( rPam, aRefMark, nInsertFlags );

    if( bMark && *rPam.GetPoint() > *rPam.GetMark())
    {
        rPam.Exchange();
    }

    // aRefMark was copied into the document pool; now retrieve real format...
    SwTxtAttr * pTxtAttr(0);
    if (bMark)
    {
        // #i107672#
        // ensure that we do not retrieve a different mark at the same position
        ::std::vector<SwTxtAttr *> const newMarks(
            rPam.GetNode()->GetTxtNode()->GetTxtAttrsAt(
                rPam.GetPoint()->nContent.GetIndex(), RES_TXTATR_REFMARK));
        ::std::vector<SwTxtAttr *>::const_iterator const iter(
            ::std::find_if(newMarks.begin(), newMarks.end(),
                NotContainedIn<SwTxtAttr *>(oldMarks)));
        OSL_ASSERT(newMarks.end() != iter);
        if (newMarks.end() != iter)
        {
            pTxtAttr = *iter;
        }
    }
    else
    {
        SwTxtNode *pTxtNd = rPam.GetNode()->GetTxtNode();
        OSL_ASSERT(pTxtNd);
        pTxtAttr = pTxtNd ? rPam.GetNode()->GetTxtNode()->GetTxtAttrForCharAt(
                rPam.GetPoint()->nContent.GetIndex() - 1, RES_TXTATR_REFMARK) : NULL;
    }

    if (!pTxtAttr)
    {
        throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(
            "SwXReferenceMark::InsertRefMark(): cannot insert attribute")), 0);
    }

    m_pMarkFmt = &pTxtAttr->GetRefMark();

    pDoc2->GetUnoCallBack()->Add(this);
}

void SAL_CALL
SwXReferenceMark::attach(const uno::Reference< text::XTextRange > & xTextRange)
throw (lang::IllegalArgumentException, uno::RuntimeException)
{
    SolarMutexGuard aGuard;

    if (!m_pImpl->m_bIsDescriptor)
    {
        throw uno::RuntimeException();
    }
    uno::Reference<lang::XUnoTunnel> xRangeTunnel( xTextRange, uno::UNO_QUERY);
    SwXTextRange* pRange = 0;
    OTextCursorHelper* pCursor = 0;
    if(xRangeTunnel.is())
    {
        pRange = ::sw::UnoTunnelGetImplementation<SwXTextRange>(xRangeTunnel);
        pCursor =
            ::sw::UnoTunnelGetImplementation<OTextCursorHelper>(xRangeTunnel);
    }
    SwDoc *const pDocument =
        (pRange) ? pRange->GetDoc() : ((pCursor) ? pCursor->GetDoc() : 0);
    if (!pDocument)
    {
        throw lang::IllegalArgumentException();
    }

    SwUnoInternalPaM aPam(*pDocument);
    //das muss jetzt sal_True liefern
    ::sw::XTextRangeToSwPaM(aPam, xTextRange);
    m_pImpl->InsertRefMark(aPam, dynamic_cast<SwXTextCursor*>(pCursor));
    m_pImpl->m_bIsDescriptor = sal_False;
    m_pImpl->m_pDoc = pDocument;
}

uno::Reference< text::XTextRange > SAL_CALL
SwXReferenceMark::getAnchor() throw (uno::RuntimeException)
{
    SolarMutexGuard aGuard;

    if (m_pImpl->IsValid())
    {
        SwFmtRefMark const*const pNewMark =
            m_pImpl->m_pDoc->GetRefMark(m_pImpl->m_sMarkName);
        if (pNewMark && (pNewMark == m_pImpl->m_pMarkFmt))
        {
            SwTxtRefMark const*const pTxtMark =
                m_pImpl->m_pMarkFmt->GetTxtRefMark();
            if (pTxtMark &&
                (&pTxtMark->GetTxtNode().GetNodes() ==
                    &m_pImpl->m_pDoc->GetNodes()))
            {
                SwTxtNode const& rTxtNode = pTxtMark->GetTxtNode();
                const ::std::auto_ptr<SwPaM> pPam( (pTxtMark->GetEnd())
                    ?   new SwPaM( rTxtNode, *pTxtMark->GetEnd(),
                                   rTxtNode, *pTxtMark->GetStart())
                    :   new SwPaM( rTxtNode, *pTxtMark->GetStart()) );

                return SwXTextRange::CreateXTextRange(
                            *m_pImpl->m_pDoc, *pPam->Start(), pPam->End());
            }
        }
    }
    return 0;
}

void SAL_CALL SwXReferenceMark::dispose() throw (uno::RuntimeException)
{
    SolarMutexGuard aGuard;
    if (m_pImpl->IsValid())
    {
        SwFmtRefMark const*const pNewMark =
            m_pImpl->m_pDoc->GetRefMark(m_pImpl->m_sMarkName);
        if (pNewMark && (pNewMark == m_pImpl->m_pMarkFmt))
        {
            SwTxtRefMark const*const pTxtMark =
                m_pImpl->m_pMarkFmt->GetTxtRefMark();
            if (pTxtMark &&
                (&pTxtMark->GetTxtNode().GetNodes() ==
                    &m_pImpl->m_pDoc->GetNodes()))
            {
                SwTxtNode const& rTxtNode = pTxtMark->GetTxtNode();
                xub_StrLen nStt = *pTxtMark->GetStart(),
                           nEnd = pTxtMark->GetEnd() ? *pTxtMark->GetEnd()
                                                     : nStt + 1;

                SwPaM aPam( rTxtNode, nStt, rTxtNode, nEnd );
                m_pImpl->m_pDoc->DeleteAndJoin( aPam );
            }
        }
    }
    else if (m_pImpl->m_bIsDescriptor)
    {
        m_pImpl->Invalidate();
    }
}

void SAL_CALL SwXReferenceMark::addEventListener(
        const uno::Reference< lang::XEventListener > & xListener)
throw (uno::RuntimeException)
{
    SolarMutexGuard g;

    if (!m_pImpl->IsValid())
    {
        throw uno::RuntimeException();
    }
    m_pImpl->m_ListenerContainer.AddListener(xListener);
}

void SAL_CALL SwXReferenceMark::removeEventListener(
        const uno::Reference< lang::XEventListener > & xListener)
throw (uno::RuntimeException)
{
    SolarMutexGuard g;

    if (!m_pImpl->IsValid() ||
        !m_pImpl->m_ListenerContainer.RemoveListener(xListener))
    {
        throw uno::RuntimeException();
    }
}

OUString SAL_CALL SwXReferenceMark::getName()
throw (uno::RuntimeException)
{
    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)
throw (uno::RuntimeException)
{
    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();
        }
        SwFmtRefMark const*const pCurMark =
            m_pImpl->m_pDoc->GetRefMark(m_pImpl->m_sMarkName);
        if ((rName != m_pImpl->m_sMarkName)
            && pCurMark && (pCurMark == m_pImpl->m_pMarkFmt))
        {
            const UnoActionContext aCont(m_pImpl->m_pDoc);
            SwTxtRefMark const*const pTxtMark =
                m_pImpl->m_pMarkFmt->GetTxtRefMark();
            if (pTxtMark &&
                (&pTxtMark->GetTxtNode().GetNodes() ==
                     &m_pImpl->m_pDoc->GetNodes()))
            {
                SwTxtNode const& rTxtNode = pTxtMark->GetTxtNode();
                xub_StrLen nStt = *pTxtMark->GetStart(),
                           nEnd = pTxtMark->GetEnd() ? *pTxtMark->GetEnd()
                                                     : nStt + 1;

                SwPaM aPam( rTxtNode, nStt, rTxtNode, nEnd );
                // deletes the m_pImpl->m_pDoc member in the SwXReferenceMark!
                m_pImpl->m_pDoc->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, 0 );
                m_pImpl->m_pDoc = aPam.GetDoc();
            }
        }
    }
}

uno::Reference< beans::XPropertySetInfo > SAL_CALL
SwXReferenceMark::getPropertySetInfo() throw (uno::RuntimeException)
{
    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 (beans::UnknownPropertyException, beans::PropertyVetoException,
    lang::IllegalArgumentException, lang::WrappedTargetException,
    uno::RuntimeException)
{
    throw lang::IllegalArgumentException();
}

uno::Any SAL_CALL
SwXReferenceMark::getPropertyValue(const OUString& rPropertyName)
throw (beans::UnknownPropertyException, lang::WrappedTargetException,
    uno::RuntimeException)
{
    // 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*/)
throw (beans::UnknownPropertyException, lang::WrappedTargetException,
    uno::RuntimeException)
{
    OSL_FAIL("SwXReferenceMark::addPropertyChangeListener(): not implemented");
}

void SAL_CALL SwXReferenceMark::removePropertyChangeListener(
        const OUString& /*rPropertyName*/,
        const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
throw (beans::UnknownPropertyException, lang::WrappedTargetException,
        uno::RuntimeException)
{
    OSL_FAIL("SwXReferenceMark::removePropertyChangeListener(): not implemented");
}

void SAL_CALL SwXReferenceMark::addVetoableChangeListener(
        const OUString& /*rPropertyName*/,
        const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
throw (beans::UnknownPropertyException, lang::WrappedTargetException,
        uno::RuntimeException)
{
    OSL_FAIL("SwXReferenceMark::addVetoableChangeListener(): not implemented");
}

void SAL_CALL SwXReferenceMark::removeVetoableChangeListener(
    const OUString& /*rPropertyName*/,
    const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
throw (beans::UnknownPropertyException, lang::WrappedTargetException,
        uno::RuntimeException)
{
    OSL_FAIL("SwXReferenceMark::removeVetoableChangeListener(): not implemented");
}

#include <com/sun/star/lang/DisposedException.hpp>
#include <unometa.hxx>
#include <unotext.hxx>
#include <unoport.hxx>
#include <txtatr.hxx>
#include <fmtmeta.hxx>
#include <docsh.hxx>

/******************************************************************
 * SwXMetaText
 ******************************************************************/
class SwXMetaText
    : public SwXText
{
private:
    SwXMeta & m_rMeta;

    virtual void PrepareForAttach(uno::Reference< text::XTextRange > & xRange,
            const SwPaM & rPam);

    virtual bool CheckForOwnMemberMeta(const SwPaM & rPam, const bool bAbsorb)
        throw (lang::IllegalArgumentException, uno::RuntimeException);

protected:
    virtual const SwStartNode *GetStartNode() const;
    virtual uno::Reference< text::XTextCursor >
        CreateCursor() throw (uno::RuntimeException);

public:
    SwXMetaText(SwDoc & rDoc, SwXMeta & rMeta);

    /// make available for SwXMeta
    void Invalidate() { SwXText::Invalidate(); };

    // XInterface
    virtual void SAL_CALL acquire() throw()
        { OSL_FAIL("ERROR: SwXMetaText::acquire"); }
    virtual void SAL_CALL release() throw()
        { OSL_FAIL("ERROR: SwXMetaText::release"); }

    // XTypeProvider
    virtual uno::Sequence< sal_Int8 > SAL_CALL
        getImplementationId() throw (uno::RuntimeException);

    // XText
    virtual uno::Reference< text::XTextCursor >  SAL_CALL
        createTextCursor() throw (uno::RuntimeException);
    virtual uno::Reference< text::XTextCursor >  SAL_CALL
        createTextCursorByRange(
            const uno::Reference< text::XTextRange > & xTextPosition)
        throw (uno::RuntimeException);

    SwXMeta & GetXMeta() { return m_rMeta; }

};

SwXMetaText::SwXMetaText(SwDoc & rDoc, SwXMeta & rMeta)
    : SwXText(&rDoc, CURSOR_META)
    , m_rMeta(rMeta)
{
}

const SwStartNode *SwXMetaText::GetStartNode() const
{
    SwXText const * const pParent(
            dynamic_cast<SwXText*>(m_rMeta.GetParentText().get()));
    return (pParent) ? pParent->GetStartNode() : 0;
}

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, CURSOR_META, *rPam.GetPoint(),
                (rPam.HasMark()) ? rPam.GetMark() : 0));
}

bool SwXMetaText::CheckForOwnMemberMeta(const SwPaM & rPam, const bool bAbsorb)
    throw (lang::IllegalArgumentException, uno::RuntimeException)
{
    return m_rMeta.CheckForOwnMemberMeta(rPam, bAbsorb);
}

uno::Reference< text::XTextCursor > SwXMetaText::CreateCursor()
throw (uno::RuntimeException)
{
    uno::Reference< text::XTextCursor > xRet;
    if (IsValid())
    {
        SwTxtNode * pTxtNode;
        xub_StrLen nMetaStart;
        xub_StrLen nMetaEnd;
        const bool bSuccess(
                m_rMeta.SetContentRange(pTxtNode, nMetaStart, nMetaEnd) );
        if (bSuccess)
        {
            SwPosition aPos(*pTxtNode, nMetaStart);
            xRet = static_cast<text::XWordCursor*>(
                    new SwXTextCursor(*GetDoc(), &m_rMeta, CURSOR_META, aPos));
        }
    }
    return xRet;
}

uno::Sequence<sal_Int8> SAL_CALL
SwXMetaText::getImplementationId() throw (uno::RuntimeException)
{
    return m_rMeta.getImplementationId();
}

// XText
uno::Reference< text::XTextCursor > SAL_CALL
SwXMetaText::createTextCursor() throw (uno::RuntimeException)
{
    return CreateCursor();
}

uno::Reference< text::XTextCursor > SAL_CALL
SwXMetaText::createTextCursorByRange(
        const uno::Reference<text::XTextRange> & xTextPosition)
    throw (uno::RuntimeException)
{
    const uno::Reference<text::XTextCursor> xCursor( CreateCursor() );
    xCursor->gotoRange(xTextPosition, sal_False);
    return xCursor;
}

/******************************************************************
 * SwXMeta
 ******************************************************************/
// the Meta has a cached list of text portions for its contents
// this list is created by SwXTextPortionEnumeration
// the Meta listens at the SwTxtNode and throws away the cache when it changes
class SwXMeta::Impl
    : public SwClient
{

public:

    SwEventListenerContainer m_ListenerContainer;
    ::std::auto_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;
    SwXMetaText m_Text;

    Impl(   SwXMeta & rThis, SwDoc & rDoc,
            ::sw::Meta * const pMeta,
            uno::Reference<text::XText> const& xParentText,
            TextRangeList_t const * const pPortions)
        : SwClient(pMeta)
        , m_ListenerContainer(static_cast< ::cppu::OWeakObject* >(&rThis))
        , m_pTextPortions( pPortions )
        , m_bIsDisposed( false )
        // #i111177# unxsols4 (Sun C++ 5.9 SunOS_sparc) may generate wrong code
        , m_bIsDescriptor((0 == pMeta) ? true : false)
        , m_xParentText(xParentText)
        , m_Text(rDoc, rThis)
    {
    }

    inline const ::sw::Meta * GetMeta() const;
    // only for SwXMetaField!
    inline const ::sw::MetaField * GetMetaField() const;
protected:
    // SwClient
    virtual void Modify( const SfxPoolItem* pOld, const SfxPoolItem *pNew);

};

inline const ::sw::Meta * SwXMeta::Impl::GetMeta() const
{
    return static_cast< const ::sw::Meta * >(GetRegisteredIn());
}

// SwModify
void SwXMeta::Impl::Modify( const SfxPoolItem* pOld, const SfxPoolItem *pNew )
{
    m_pTextPortions.reset(); // throw away cache (SwTxtNode changed)

    ClientModify(this, pOld, pNew);

    if (!GetRegisteredIn()) // removed => dispose
    {
        m_ListenerContainer.Disposing();
        m_bIsDisposed = true;
        m_Text.Invalidate();
    }
}

uno::Reference<text::XText> SwXMeta::GetParentText() const
{
    return m_pImpl->m_xParentText;
}

SwXMeta::SwXMeta(SwDoc *const pDoc, ::sw::Meta *const pMeta,
        uno::Reference<text::XText> const& xParentText,
        TextRangeList_t const*const pPortions)
    : m_pImpl( new SwXMeta::Impl(*this, *pDoc, pMeta, xParentText, pPortions) )
{
}

SwXMeta::SwXMeta(SwDoc *const pDoc)
    : m_pImpl( new SwXMeta::Impl(*this, *pDoc, 0, 0, 0) )
{
}

SwXMeta::~SwXMeta()
{
}

uno::Reference<rdf::XMetadatable>
SwXMeta::CreateXMeta(::sw::Meta & rMeta,
            uno::Reference<text::XText> const& i_xParent,
            ::std::auto_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));
            OSL_ENSURE(pXMeta, "no 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 = pPortions;
            // ??? is this necessary?
            if (pXMeta->m_pImpl->m_xParentText.get() != i_xParent.get())
            {
                OSL_FAIL("SwXMeta with different parent?");
                pXMeta->m_pImpl->m_xParentText.set(i_xParent);
            }
        }
        return xMeta;
    }

    // create new SwXMeta
    SwTxtNode * const pTxtNode( rMeta.GetTxtNode() );
    OSL_ENSURE(pTxtNode, "CreateXMeta: no text node?");
    if (!pTxtNode) { return 0; }
    uno::Reference<text::XText> xParentText(i_xParent);
    if (!xParentText.is())
    {
        SwTxtMeta * const pTxtAttr( rMeta.GetTxtAttr() );
        OSL_ENSURE(pTxtAttr, "CreateXMeta: no text attr?");
        if (!pTxtAttr) { return 0; }
        const SwPosition aPos(*pTxtNode, *pTxtAttr->GetStart());
        xParentText.set( ::sw::CreateParentXText(*pTxtNode->GetDoc(), aPos) );
    }
    if (!xParentText.is()) { return 0; }
    SwXMeta *const pXMeta( (RES_TXTATR_META == rMeta.GetFmtMeta()->Which())
        ? new SwXMeta     (pTxtNode->GetDoc(), &rMeta, xParentText,
                            pPortions.release()) // temporarily un-auto_ptr :-(
        : new SwXMetaField(pTxtNode->GetDoc(), &rMeta, xParentText,
                            pPortions.release()));
    // 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);
    return xMeta;
}

bool SwXMeta::SetContentRange(
        SwTxtNode *& rpNode, xub_StrLen & rStart, xub_StrLen & rEnd ) const
{
    ::sw::Meta const * const pMeta( m_pImpl->GetMeta() );
    if (pMeta)
    {
        SwTxtMeta const * const pTxtAttr( pMeta->GetTxtAttr() );
        if (pTxtAttr)
        {
            rpNode = pMeta->GetTxtNode();
            if (rpNode)
            {
                // rStart points at the first position _within_ the meta!
                rStart = *pTxtAttr->GetStart() + 1;
                rEnd = *pTxtAttr->GetEnd();
                return true;
            }
        }
    }
    return false;
}

bool SwXMeta::CheckForOwnMemberMeta(const SwPaM & rPam, const bool bAbsorb)
    throw (lang::IllegalArgumentException, uno::RuntimeException)
{
    SwTxtNode * pTxtNode;
    xub_StrLen nMetaStart;
    xub_StrLen nMetaEnd;
    const bool bSuccess( SetContentRange(pTxtNode, nMetaStart, nMetaEnd) );
    OSL_ENSURE(bSuccess, "no pam?");
    if (!bSuccess)
        throw lang::DisposedException();

    SwPosition const * const pStartPos( rPam.Start() );
    if (&pStartPos->nNode.GetNode() != pTxtNode)
    {
        throw lang::IllegalArgumentException(
            C2U("trying to insert into a nesting text content, but start "
                "of text range not in same paragraph as text content"),
                0, 0);
    }
    bool bForceExpandHints(false);
    const xub_StrLen 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(
            C2U("trying to insert into a nesting text content, but start "
                "of text range not inside text content"),
                0, 0);
    }
    else if (nStartPos == nMetaEnd)
    {
        bForceExpandHints = true;
    }
    if (rPam.HasMark() && bAbsorb)
    {
        SwPosition const * const pEndPos( rPam.End() );
        if (&pEndPos->nNode.GetNode() != pTxtNode)
        {
            throw lang::IllegalArgumentException(
                C2U("trying to insert into a nesting text content, but end "
                    "of text range not in same paragraph as text content"),
                    0, 0);
        }
        const xub_StrLen 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(
                C2U("trying to insert into a nesting text content, but end "
                    "of text range not inside text content"),
                    0, 0);
        }
        else if (nEndPos == nMetaEnd)
        {
            bForceExpandHints = true;
        }
    }
    return bForceExpandHints;
}

const uno::Sequence< sal_Int8 > & SwXMeta::getUnoTunnelId()
{
    static uno::Sequence< sal_Int8 > aSeq( ::CreateUnoTunnelId() );
    return aSeq;
}

// XUnoTunnel
sal_Int64 SAL_CALL
SwXMeta::getSomething( const uno::Sequence< sal_Int8 > & i_rId )
throw (uno::RuntimeException)
{
    return ::sw::UnoTunnelImpl<SwXMeta>(i_rId, this);
}

// XServiceInfo
::rtl::OUString SAL_CALL
SwXMeta::getImplementationName() throw (uno::RuntimeException)
{
    return C2U("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 ::rtl::OUString& rServiceName)
throw (uno::RuntimeException)
{
    return ::sw::SupportsServiceImpl(
            g_nServicesMeta, g_ServicesMeta, rServiceName);
}

uno::Sequence< ::rtl::OUString > SAL_CALL
SwXMeta::getSupportedServiceNames() throw (uno::RuntimeException)
{
    return ::sw::GetSupportedServiceNamesImpl(g_nServicesMeta, g_ServicesMeta);
}

// XComponent
void SAL_CALL
SwXMeta::addEventListener(
        uno::Reference< lang::XEventListener> const & xListener )
throw (uno::RuntimeException)
{
    SolarMutexGuard g;

    m_pImpl->m_ListenerContainer.AddListener(xListener);
    if (m_pImpl->m_bIsDisposed)
    {
        m_pImpl->m_ListenerContainer.Disposing();
    }
}

void SAL_CALL
SwXMeta::removeEventListener(
        uno::Reference< lang::XEventListener> const & xListener )
throw (uno::RuntimeException)
{
    SolarMutexGuard g;

    if (!m_pImpl->m_bIsDisposed)
    {
        m_pImpl->m_ListenerContainer.RemoveListener(xListener);
    }
}

void SAL_CALL
SwXMeta::dispose() throw (uno::RuntimeException)
{
    SolarMutexGuard g;

    if (m_pImpl->m_bIsDescriptor)
    {
        m_pImpl->m_pTextPortions.reset();
        m_pImpl->m_ListenerContainer.Disposing();
        m_pImpl->m_bIsDisposed = true;
        m_pImpl->m_Text.Invalidate();
    }
    else if (!m_pImpl->m_bIsDisposed)
    {
        SwTxtNode * pTxtNode;
        xub_StrLen nMetaStart;
        xub_StrLen nMetaEnd;
        const bool bSuccess(SetContentRange(pTxtNode, nMetaStart, nMetaEnd));
        OSL_ENSURE(bSuccess, "no pam?");
        if (bSuccess)
        {
            // -1 because of CH_TXTATR
            SwPaM aPam( *pTxtNode, nMetaStart - 1, *pTxtNode, nMetaEnd );
            SwDoc * const pDoc( pTxtNode->GetDoc() );
            pDoc->DeleteAndJoin( aPam );

            // removal should call Modify and do the dispose
            OSL_ENSURE(m_pImpl->m_bIsDisposed, "zombie meta");
        }
    }
}

void SAL_CALL
SwXMeta::AttachImpl(const uno::Reference< text::XTextRange > & i_xTextRange,
        const sal_uInt16 i_nWhich)
throw (lang::IllegalArgumentException, uno::RuntimeException)
{
    SolarMutexGuard g;

    if (m_pImpl->m_bIsDisposed)
    {
        throw lang::DisposedException();
    }
    if (!m_pImpl->m_bIsDescriptor)
    {
        throw uno::RuntimeException(
            C2S("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(
            C2S("SwXMeta::attach(): argument is no XUnoTunnel"),
                static_cast< ::cppu::OWeakObject* >(this), 0);
    }
    SwXTextRange *const pRange(
            ::sw::UnoTunnelGetImplementation<SwXTextRange>(xRangeTunnel));
    OTextCursorHelper *const pCursor( (pRange) ? 0 :
            ::sw::UnoTunnelGetImplementation<OTextCursorHelper>(xRangeTunnel));
    if (!pRange && !pCursor)
    {
        throw lang::IllegalArgumentException(
            C2S("SwXMeta::attach(): argument not supported type"),
                static_cast< ::cppu::OWeakObject* >(this), 0);
    }

    SwDoc * const pDoc(
            pRange ? pRange->GetDoc() : pCursor ? pCursor->GetDoc() : 0 );
    if (!pDoc)
    {
        throw lang::IllegalArgumentException(
            C2S("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() : false);
    const SetAttrMode nInsertFlags( (bForceExpandHints)
        ?   ( nsSetAttrMode::SETATTR_FORCEHINTEXPAND
            | nsSetAttrMode::SETATTR_DONTEXPAND)
        : nsSetAttrMode::SETATTR_DONTEXPAND );

    const ::boost::shared_ptr< ::sw::Meta> pMeta( (RES_TXTATR_META == i_nWhich)
        ? ::boost::shared_ptr< ::sw::Meta>( new ::sw::Meta() )
        : ::boost::shared_ptr< ::sw::Meta>(
            pDoc->GetMetaFieldManager().makeMetaField()) );
    SwFmtMeta meta(pMeta, i_nWhich); // this is cloned by Insert!
    const bool bSuccess( pDoc->InsertPoolItem( aPam, meta, nInsertFlags ) );
    SwTxtAttr * const pTxtAttr( pMeta->GetTxtAttr() );
    if (!bSuccess)
    {
        throw lang::IllegalArgumentException(
            C2S("SwXMeta::attach(): cannot create meta: range invalid?"),
                static_cast< ::cppu::OWeakObject* >(this), 1);
    }
    if (!pTxtAttr)
    {
        OSL_FAIL("meta inserted, but has no text attribute?");
        throw uno::RuntimeException(
            C2S("SwXMeta::attach(): cannot create meta"),
                static_cast< ::cppu::OWeakObject* >(this));
    }

    pMeta->Add(m_pImpl.get());
    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)
throw (lang::IllegalArgumentException, uno::RuntimeException)
{
    return SwXMeta::AttachImpl(i_xTextRange, RES_TXTATR_META);
}

uno::Reference< text::XTextRange > SAL_CALL
SwXMeta::getAnchor() throw (uno::RuntimeException)
{
    SolarMutexGuard g;

    if (m_pImpl->m_bIsDisposed)
    {
        throw lang::DisposedException();
    }
    if (m_pImpl->m_bIsDescriptor)
    {
        throw uno::RuntimeException(
                C2S("SwXMeta::getAnchor(): not inserted"),
                static_cast< ::cppu::OWeakObject* >(this));
    }

    SwTxtNode * pTxtNode;
    xub_StrLen nMetaStart;
    xub_StrLen nMetaEnd;
    const bool bSuccess(SetContentRange(pTxtNode, nMetaStart, nMetaEnd));
    OSL_ENSURE(bSuccess, "no pam?");
    if (!bSuccess)
    {
        throw lang::DisposedException(
                C2S("SwXMeta::getAnchor(): not attached"),
                static_cast< ::cppu::OWeakObject* >(this));
    }

    const SwPosition start(*pTxtNode, nMetaStart - 1); // -1 due to CH_TXTATR
    const SwPosition end(*pTxtNode, nMetaEnd);
    return SwXTextRange::CreateXTextRange(*pTxtNode->GetDoc(), start, &end);
}

// XTextRange
uno::Reference< text::XText > SAL_CALL
SwXMeta::getText() throw (uno::RuntimeException)
{
    SolarMutexGuard g;
    return this;
}

uno::Reference< text::XTextRange > SAL_CALL
SwXMeta::getStart() throw (uno::RuntimeException)
{
    SolarMutexGuard g;
    return m_pImpl->m_Text.getStart();
}

uno::Reference< text::XTextRange > SAL_CALL
SwXMeta::getEnd() throw (uno::RuntimeException)
{
    SolarMutexGuard g;
    return m_pImpl->m_Text.getEnd();
}

rtl::OUString SAL_CALL
SwXMeta::getString() throw (uno::RuntimeException)
{
    SolarMutexGuard g;
    return m_pImpl->m_Text.getString();
}

void SAL_CALL
SwXMeta::setString(const rtl::OUString& rString) throw (uno::RuntimeException)
{
    SolarMutexGuard g;
    return m_pImpl->m_Text.setString(rString);
}

// XSimpleText
uno::Reference< text::XTextCursor > SAL_CALL
SwXMeta::createTextCursor() throw (uno::RuntimeException)
{
    SolarMutexGuard g;
    return m_pImpl->m_Text.createTextCursor();
}

uno::Reference< text::XTextCursor > SAL_CALL
SwXMeta::createTextCursorByRange(
        const uno::Reference<text::XTextRange> & xTextPosition)
    throw (uno::RuntimeException)
{
    SolarMutexGuard g;
    return m_pImpl->m_Text.createTextCursorByRange(xTextPosition);
}

void SAL_CALL
SwXMeta::insertString(const uno::Reference<text::XTextRange> & xRange,
        const rtl::OUString& rString, sal_Bool bAbsorb)
throw (uno::RuntimeException)
{
    SolarMutexGuard g;
    return m_pImpl->m_Text.insertString(xRange, rString, bAbsorb);
}

void SAL_CALL
SwXMeta::insertControlCharacter(const uno::Reference<text::XTextRange> & xRange,
        sal_Int16 nControlCharacter, sal_Bool bAbsorb)
throw (lang::IllegalArgumentException, uno::RuntimeException)
{
    SolarMutexGuard g;
    return m_pImpl->m_Text.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)
throw (lang::IllegalArgumentException, uno::RuntimeException)
{
    SolarMutexGuard g;
    return m_pImpl->m_Text.insertTextContent(xRange, xContent, bAbsorb);
}

void SAL_CALL
SwXMeta::removeTextContent(
        const uno::Reference< text::XTextContent > & xContent)
    throw (container::NoSuchElementException, uno::RuntimeException)
{
    SolarMutexGuard g;
    return m_pImpl->m_Text.removeTextContent(xContent);
}

// XChild
uno::Reference< uno::XInterface > SAL_CALL
SwXMeta::getParent() throw (uno::RuntimeException)
{
    SolarMutexGuard g;
    SwTxtNode * pTxtNode;
    xub_StrLen nMetaStart;
    xub_StrLen nMetaEnd;
    bool const bSuccess( SetContentRange(pTxtNode, 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 GetTxtAttrAt(_, _, PARENT) in GetNestedTextContent
    uno::Reference<text::XTextContent> const xRet(
        SwUnoCursorHelper::GetNestedTextContent(*pTxtNode, nMetaStart - 1,
            true) );
    return xRet;
}

void SAL_CALL
SwXMeta::setParent(uno::Reference< uno::XInterface > const& /*xParent*/)
    throw (uno::RuntimeException, lang::NoSupportException)
{
    throw lang::NoSupportException(C2S("setting parent not supported"), *this);
}

// XElementAccess
uno::Type SAL_CALL
SwXMeta::getElementType() throw (uno::RuntimeException)
{
    return text::XTextRange::static_type();
}

sal_Bool SAL_CALL
SwXMeta::hasElements() throw (uno::RuntimeException)
{
    SolarMutexGuard g;

    return m_pImpl->GetRegisteredIn() ? sal_True : sal_False;
}

// XEnumerationAccess
uno::Reference< container::XEnumeration > SAL_CALL
SwXMeta::createEnumeration() throw (uno::RuntimeException)
{
    SolarMutexGuard g;

    if (m_pImpl->m_bIsDisposed)
    {
        throw lang::DisposedException();
    }
    if (m_pImpl->m_bIsDescriptor)
    {
        throw uno::RuntimeException(
                C2S("createEnumeration(): not inserted"),
                static_cast< ::cppu::OWeakObject* >(this));
    }

    SwTxtNode * pTxtNode;
    xub_StrLen nMetaStart;
    xub_StrLen nMetaEnd;
    const bool bSuccess(SetContentRange(pTxtNode, nMetaStart, nMetaEnd));
    OSL_ENSURE(bSuccess, "no pam?");
    if (!bSuccess)
        throw lang::DisposedException();

    SwPaM aPam(*pTxtNode, 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)
    {
        SwTxtNode const * const pTxtNode( pMeta->GetTxtNode() );
        if (pTxtNode)
        {
            SwDocShell const * const pShell(pTxtNode->GetDoc()->GetDocShell());
            return (pShell) ? pShell->GetModel() : 0;
        }
    }
    return 0;
}

/******************************************************************
 * SwXMetaField
 ******************************************************************/
inline const ::sw::MetaField * SwXMeta::Impl::GetMetaField() const
{
    return static_cast< const ::sw::MetaField * >(GetRegisteredIn());
}

SwXMetaField::SwXMetaField(SwDoc *const pDoc, ::sw::Meta *const pMeta,
        uno::Reference<text::XText> const& xParentText,
        TextRangeList_t const*const pPortions)
    : SwXMetaField_Base(pDoc, pMeta, xParentText, pPortions)
{
    OSL_ENSURE(pMeta && dynamic_cast< ::sw::MetaField* >(pMeta),
        "SwXMetaField created for wrong hint!");
}

SwXMetaField::SwXMetaField(SwDoc *const pDoc)
    :  SwXMetaField_Base(pDoc)
{
}

SwXMetaField::~SwXMetaField()
{
}

// XServiceInfo
::rtl::OUString SAL_CALL
SwXMetaField::getImplementationName() throw (uno::RuntimeException)
{
    return C2U("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 ::rtl::OUString& rServiceName)
throw (uno::RuntimeException)
{
    return ::sw::SupportsServiceImpl(
            g_nServicesMetaField, g_ServicesMetaField, rServiceName);
}

uno::Sequence< ::rtl::OUString > SAL_CALL
SwXMetaField::getSupportedServiceNames() throw (uno::RuntimeException)
{
    return ::sw::GetSupportedServiceNamesImpl(
            g_nServicesMetaField, g_ServicesMetaField);
}

// XComponent
void SAL_CALL
SwXMetaField::addEventListener(
        uno::Reference< lang::XEventListener> const & xListener )
throw (uno::RuntimeException)
{
    return SwXMeta::addEventListener(xListener);
}

void SAL_CALL
SwXMetaField::removeEventListener(
        uno::Reference< lang::XEventListener> const & xListener )
throw (uno::RuntimeException)
{
    return SwXMeta::removeEventListener(xListener);
}

void SAL_CALL
SwXMetaField::dispose() throw (uno::RuntimeException)
{
    return SwXMeta::dispose();
}

// XTextContent
void SAL_CALL
SwXMetaField::attach(const uno::Reference< text::XTextRange > & i_xTextRange)
throw (lang::IllegalArgumentException, uno::RuntimeException)
{
    return SwXMeta::AttachImpl(i_xTextRange, RES_TXTATR_METAFIELD);
}

uno::Reference< text::XTextRange > SAL_CALL
SwXMetaField::getAnchor() throw (uno::RuntimeException)
{
    return SwXMeta::getAnchor();
}

// XPropertySet
uno::Reference< beans::XPropertySetInfo > SAL_CALL
SwXMetaField::getPropertySetInfo() throw (uno::RuntimeException)
{
    SolarMutexGuard g;

    static uno::Reference< beans::XPropertySetInfo > xRef(
        aSwMapProvider.GetPropertySet(PROPERTY_MAP_METAFIELD)
            ->getPropertySetInfo() );
    return xRef;
}

void SAL_CALL
SwXMetaField::setPropertyValue(
        const ::rtl::OUString& rPropertyName, const uno::Any& rValue)
throw (beans::UnknownPropertyException, beans::PropertyVetoException,
    lang::IllegalArgumentException, lang::WrappedTargetException,
    uno::RuntimeException)
{
    SolarMutexGuard g;

    ::sw::MetaField * const pMeta(
            const_cast< ::sw::MetaField * >(m_pImpl->GetMetaField()) );
    if (!pMeta)
        throw lang::DisposedException();

    if (rPropertyName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("NumberFormat")))
    {
        sal_Int32 nNumberFormat(0);
        if (rValue >>= nNumberFormat)
        {
            pMeta->SetNumberFormat(static_cast<sal_uInt32>(nNumberFormat));
        }
    }
    else if (rPropertyName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("IsFixedLanguage")))
    {
        bool b(false);
        if (rValue >>= b)
        {
            pMeta->SetIsFixedLanguage(b);
        }
    }
    else
    {
        throw beans::UnknownPropertyException();
    }
}

uno::Any SAL_CALL
SwXMetaField::getPropertyValue(const ::rtl::OUString& rPropertyName)
throw (beans::UnknownPropertyException, lang::WrappedTargetException,
    uno::RuntimeException)
{
    SolarMutexGuard g;

    ::sw::MetaField const * const pMeta( m_pImpl->GetMetaField() );
    if (!pMeta)
        throw lang::DisposedException();

    uno::Any any;

    if (rPropertyName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("NumberFormat")))
    {
        const ::rtl::OUString text( getPresentation(sal_False) );
        any <<= static_cast<sal_Int32>(pMeta->GetNumberFormat(text));
    }
    else if (rPropertyName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("IsFixedLanguage")))
    {
        any <<= pMeta->IsFixedLanguage();
    }
    else
    {
        throw beans::UnknownPropertyException();
    }

    return any;
}

void SAL_CALL
SwXMetaField::addPropertyChangeListener(
        const ::rtl::OUString& /*rPropertyName*/,
        const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
throw (beans::UnknownPropertyException, lang::WrappedTargetException,
    uno::RuntimeException)
{
    OSL_FAIL("SwXMetaField::addPropertyChangeListener(): not implemented");
}

void SAL_CALL
SwXMetaField::removePropertyChangeListener(
        const ::rtl::OUString& /*rPropertyName*/,
        const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
throw (beans::UnknownPropertyException, lang::WrappedTargetException,
    uno::RuntimeException)
{
    OSL_FAIL("SwXMetaField::removePropertyChangeListener(): not implemented");
}

void SAL_CALL
SwXMetaField::addVetoableChangeListener(
        const ::rtl::OUString& /*rPropertyName*/,
        const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
throw (beans::UnknownPropertyException, lang::WrappedTargetException,
    uno::RuntimeException)
{
    OSL_FAIL("SwXMetaField::addVetoableChangeListener(): not implemented");
}

void SAL_CALL
SwXMetaField::removeVetoableChangeListener(
        const ::rtl::OUString& /*rPropertyName*/,
        const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
throw (beans::UnknownPropertyException, lang::WrappedTargetException,
        uno::RuntimeException)
{
    OSL_FAIL("SwXMetaField::removeVetoableChangeListener(): not implemented");
}

#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 <comphelper/processfactory.hxx>

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 ::rtl::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, 0),
        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()) {
            OSL_TRACE("ignoring other odf:Prefix/odf:Suffix statements");
        }
        return xObject->getValue();
    }
    return ::rtl::OUString();
}

void
getPrefixAndSuffix(
        const uno::Reference<frame::XModel>& xModel,
        const uno::Reference<rdf::XMetadatable>& xMetaField,
        ::rtl::OUString *const o_pPrefix, ::rtl::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 (uno::Exception & e) {
        throw lang::WrappedTargetRuntimeException(
            ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("getPrefixAndSuffix: exception")),
            0, uno::makeAny(e));
    }
}

// XTextField
::rtl::OUString SAL_CALL
SwXMetaField::getPresentation(sal_Bool bShowCommand)
throw (uno::RuntimeException)
{
    SolarMutexGuard g;

    if (bShowCommand)
    {
//FIXME ?
        return ::rtl::OUString();
    }
    else
    {
        // getString should check if this is invalid
        const ::rtl::OUString content( this->getString() );
        ::rtl::OUString prefix;
        ::rtl::OUString suffix;
        getPrefixAndSuffix(GetModel(), this, &prefix, &suffix);
        return prefix + content + suffix;
    }
}

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