/**************************************************************
 * 
 * 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
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sw.hxx"
#include <com/sun/star/embed/NoVisualAreaSizeException.hpp>
#include <com/sun/star/container/XChild.hpp>
#include <com/sun/star/embed/XEmbedPersist.hpp>
#include <com/sun/star/embed/XLinkageSupport.hpp>
#include <com/sun/star/embed/Aspects.hpp>
#include <com/sun/star/embed/EmbedMisc.hpp>
#include <com/sun/star/embed/EmbedStates.hpp>
#include <com/sun/star/util/XCloseable.hpp>
#include <com/sun/star/util/XModifiable.hpp>
#include <com/sun/star/document/XEventBroadcaster.hpp>
#include <cppuhelper/implbase1.hxx>

#include <cppuhelper/implbase2.hxx>
#include <toolkit/helper/vclunohelper.hxx>
#include <hintids.hxx>
#include <tools/urlobj.hxx>
#include <sfx2/docfile.hxx>
#include <sfx2/app.hxx>
#include <sfx2/linkmgr.hxx>
#include <unotools/configitem.hxx>
#ifndef _OUTDEV_HXX //autogen
#include <vcl/outdev.hxx>
#endif
#include <fmtanchr.hxx>
#include <frmfmt.hxx>
#include <doc.hxx>
#include <docsh.hxx>
#include <pam.hxx>
#include <section.hxx>
#include <cntfrm.hxx>
#include <frmatr.hxx>
#ifndef _DOCSH_HXX
#include <docsh.hxx>
#endif
#include <ndole.hxx>

#include <comphelper/classids.hxx>
#include <vcl/graph.hxx>
#include <sot/formats.hxx>
#include <unotools/ucbstreamhelper.hxx>
#include <svtools/filter.hxx>
#ifndef _COMCORE_HRC
#include <comcore.hrc>
#endif

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

class SwOLELRUCache : private SvPtrarr, private utl::ConfigItem
{
	sal_uInt16 nLRU_InitSize;
	sal_Bool bInUnload;
    uno::Sequence< rtl::OUString > GetPropertyNames();

public:
	SwOLELRUCache();

    virtual void Notify( const uno::Sequence<
								rtl::OUString>& aPropertyNames );
	virtual void Commit();
	void Load();

	void SetInUnload( sal_Bool bFlag ) 	{ bInUnload = bFlag; }
	using SvPtrarr::Count;

	void InsertObj( SwOLEObj& rObj );
	void RemoveObj( SwOLEObj& rObj );

	void RemovePtr( SwOLEObj* pObj )
	{
		sal_uInt16 nPos = SvPtrarr::GetPos( pObj );
		if( USHRT_MAX != nPos )
			SvPtrarr::Remove( nPos );
	}
};

SwOLELRUCache* pOLELRU_Cache = 0;

class SwOLEListener_Impl : public ::cppu::WeakImplHelper1< embed::XStateChangeListener >
{
    SwOLEObj* mpObj;
public:
    SwOLEListener_Impl( SwOLEObj* pObj );
    void Release();
    virtual void SAL_CALL changingState( const lang::EventObject& aEvent, ::sal_Int32 nOldState, ::sal_Int32 nNewState ) throw (embed::WrongStateException, uno::RuntimeException);
    virtual void SAL_CALL stateChanged( const lang::EventObject& aEvent, ::sal_Int32 nOldState, ::sal_Int32 nNewState ) throw (uno::RuntimeException);
    virtual void SAL_CALL disposing( const lang::EventObject& aEvent ) throw (uno::RuntimeException);
};

SwOLEListener_Impl::SwOLEListener_Impl( SwOLEObj* pObj )
: mpObj( pObj )
{
    if ( mpObj->IsOleRef() && mpObj->GetOleRef()->getCurrentState() == embed::EmbedStates::RUNNING )
    {
        pOLELRU_Cache->InsertObj( *mpObj );
    }
}

void SAL_CALL SwOLEListener_Impl::changingState( const lang::EventObject&, ::sal_Int32 , ::sal_Int32 ) throw (embed::WrongStateException, uno::RuntimeException)
{
}

void SAL_CALL SwOLEListener_Impl::stateChanged( const lang::EventObject&, ::sal_Int32 nOldState, ::sal_Int32 nNewState ) throw (uno::RuntimeException)
{
    if ( mpObj && nOldState == embed::EmbedStates::LOADED && nNewState == embed::EmbedStates::RUNNING )
    {
        if( !pOLELRU_Cache )
            pOLELRU_Cache = new SwOLELRUCache;
        pOLELRU_Cache->InsertObj( *mpObj );
    }
    else if ( mpObj && nNewState == embed::EmbedStates::LOADED && nOldState == embed::EmbedStates::RUNNING )
    {
        if ( pOLELRU_Cache )
            pOLELRU_Cache->RemoveObj( *mpObj );
    }
}

void SwOLEListener_Impl::Release()
{
    if ( mpObj && pOLELRU_Cache )
        pOLELRU_Cache->RemoveObj( *mpObj );
    mpObj=0;
    release();
}

void SAL_CALL SwOLEListener_Impl::disposing( const lang::EventObject& ) throw (uno::RuntimeException)
{
    if ( mpObj && pOLELRU_Cache )
        pOLELRU_Cache->RemoveObj( *mpObj );
}

// --------------------
// SwEmbedObjectLink
// --------------------
// TODO/LATER: actually SwEmbedObjectLink should be used here, but because different objects are used to control
//             embedded object different link objects with the same functionality had to be implemented

class SwEmbedObjectLink : public sfx2::SvBaseLink
{
	SwOLENode*			pOleNode;

public:
						SwEmbedObjectLink(SwOLENode* pNode);
	virtual				~SwEmbedObjectLink();

	virtual void		Closed();
	virtual void		DataChanged( const String& rMimeType,
                                const uno::Any & rValue );

	sal_Bool			Connect() { return GetRealObject() != NULL; }
};

// -----------------------------------------------------------------------------

SwEmbedObjectLink::SwEmbedObjectLink(SwOLENode* pNode):
	::sfx2::SvBaseLink( ::sfx2::LINKUPDATE_ONCALL, SOT_FORMATSTR_ID_SVXB ),
	pOleNode(pNode)
{
	SetSynchron( sal_False );
}

// -----------------------------------------------------------------------------

SwEmbedObjectLink::~SwEmbedObjectLink()
{
}

// -----------------------------------------------------------------------------

void SwEmbedObjectLink::DataChanged( const String& ,
                                const uno::Any & )
{
	if ( !pOleNode->UpdateLinkURL_Impl() )
	{
		// the link URL was not changed
		uno::Reference< embed::XEmbeddedObject > xObject = pOleNode->GetOLEObj().GetOleRef();
		OSL_ENSURE( xObject.is(), "The object must exist always!\n" );
		if ( xObject.is() )
		{
			// let the object reload the link
			// TODO/LATER: reload call could be used for this case

			try
			{
				sal_Int32 nState = xObject->getCurrentState();
				if ( nState != embed::EmbedStates::LOADED )
				{
					// in some cases the linked file probably is not locked so it could be changed
					xObject->changeState( embed::EmbedStates::LOADED );
					xObject->changeState( nState );
				}
			}
			catch ( uno::Exception& )
			{
			}
		}
	}

	pOleNode->GetNewReplacement();
	// Initiate repainting
	// pObj->SetChanged();
}

// -----------------------------------------------------------------------------

void SwEmbedObjectLink::Closed()
{
	pOleNode->BreakFileLink_Impl();
	SvBaseLink::Closed();
}


// --------------------
// SwOLENode
// --------------------

SwOLENode::SwOLENode( const SwNodeIndex &rWhere,
                    const svt::EmbeddedObjectRef& xObj,
					SwGrfFmtColl *pGrfColl,
					SwAttrSet* pAutoAttr ) :
	SwNoTxtNode( rWhere, ND_OLENODE, pGrfColl, pAutoAttr ),
    aOLEObj( xObj ),
    pGraphic(0),
	bOLESizeInvalid( sal_False ),
	mpObjectLink( NULL )
{
	aOLEObj.SetNode( this );
}

SwOLENode::SwOLENode( const SwNodeIndex &rWhere,
					const String &rString,
					sal_Int64 nAspect,
					SwGrfFmtColl *pGrfColl,
					SwAttrSet* pAutoAttr ) :
	SwNoTxtNode( rWhere, ND_OLENODE, pGrfColl, pAutoAttr ),
	aOLEObj( rString, nAspect ),
    pGraphic(0),
	bOLESizeInvalid( sal_False ),
	mpObjectLink( NULL )
{
	aOLEObj.SetNode( this );
}

SwOLENode::~SwOLENode()
{
	DisconnectFileLink_Impl();
    delete pGraphic;
}

Graphic* SwOLENode::GetGraphic()
{
    if ( aOLEObj.GetOleRef().is() )
        return aOLEObj.xOLERef.GetGraphic();
    return pGraphic;
}

Graphic* SwOLENode::GetHCGraphic()
{
	return aOLEObj.xOLERef.GetHCGraphic();
}

SwCntntNode *SwOLENode::SplitCntntNode( const SwPosition & )
{
	// OLE-Objecte vervielfaeltigen ??
	ASSERT( sal_False, "OleNode: can't split." );
	return this;
}

// Laden eines in den Undo-Bereich verschobenen OLE-Objekts

sal_Bool SwOLENode::RestorePersistentData()
{
    DBG_ASSERT( aOLEObj.GetOleRef().is(), "No object to restore!" );
    if ( aOLEObj.xOLERef.is() )
    {
		// Falls bereits eine SvPersist-Instanz existiert, nehmen wir diese
        SfxObjectShell* p = GetDoc()->GetPersist();
        if( !p )
        {
            // TODO/LATER: reicht hier nicht ein EmbeddedObjectContainer? Was passiert mit
            // diesem Dokument?
			ASSERT( !this, "warum wird hier eine DocShell angelegt?" );
            p = new SwDocShell( GetDoc(), SFX_CREATE_MODE_INTERNAL );
            p->DoInitNew( NULL );
        }

        uno::Reference < container::XChild > xChild( aOLEObj.xOLERef.GetObject(), uno::UNO_QUERY );
        if ( xChild.is() )
            xChild->setParent( p->GetModel() );

        DBG_ASSERT( aOLEObj.aName.Len(), "No object name!" );
        ::rtl::OUString aObjName;
        if ( !p->GetEmbeddedObjectContainer().InsertEmbeddedObject( aOLEObj.xOLERef.GetObject(), aObjName ) )
        {
            if ( xChild.is() )
                xChild->setParent( 0 );
            DBG_ERROR( "InsertObject failed" );
        }
        else
        {
            aOLEObj.aName = aObjName;
            aOLEObj.xOLERef.AssignToContainer( &p->GetEmbeddedObjectContainer(), aObjName );
			CheckFileLink_Impl();
        }
    }

	return sal_True;
}

// OLE object is transported into UNDO area
sal_Bool SwOLENode::SavePersistentData()
{
    if( aOLEObj.xOLERef.is() )
	{
		comphelper::EmbeddedObjectContainer* pCnt = aOLEObj.xOLERef.GetContainer();

#if OSL_DEBUG_LEVEL > 0
        SfxObjectShell* p = GetDoc()->GetPersist();
        DBG_ASSERT( p, "No document!" );
        if( p )
        {
            comphelper::EmbeddedObjectContainer& rCnt = p->GetEmbeddedObjectContainer();
			OSL_ENSURE( !pCnt || &rCnt == pCnt, "The helper is assigned to unexpected container!\n" );
        }
#endif

		if ( pCnt && pCnt->HasEmbeddedObject( aOLEObj.aName ) )
		{
			uno::Reference < container::XChild > xChild( aOLEObj.xOLERef.GetObject(), uno::UNO_QUERY );
			if ( xChild.is() )
				xChild->setParent( 0 );

            pCnt->RemoveEmbeddedObject( aOLEObj.aName, sal_False );

			// TODO/LATER: aOLEObj.aName has no meaning here, since the undo container contains the object
			// by different name, in future it might makes sence that the name is transported here.
            aOLEObj.xOLERef.AssignToContainer( 0, aOLEObj.aName );
            try
            {
                // "unload" object
                aOLEObj.xOLERef->changeState( embed::EmbedStates::LOADED );
            }
            catch ( uno::Exception& )
            {
            }
		}
	}

	DisconnectFileLink_Impl();

	return sal_True;
}


SwOLENode * SwNodes::MakeOLENode( const SwNodeIndex & rWhere,
                    const svt::EmbeddedObjectRef& xObj,
									SwGrfFmtColl* pGrfColl,
									SwAttrSet* pAutoAttr )
{
	ASSERT( pGrfColl,"SwNodes::MakeOLENode: Formatpointer ist 0." );

	SwOLENode *pNode =
        new SwOLENode( rWhere, xObj, pGrfColl, pAutoAttr );

    // set parent if XChild is supported
    //!! needed to supply Math objects with a valid reference device
    uno::Reference< container::XChild > xChild( pNode->GetOLEObj().GetObject().GetObject(), UNO_QUERY );
    if (xChild.is())
    {
        SwDocShell *pDocSh = GetDoc()->GetDocShell();
        if (pDocSh)
            xChild->setParent( pDocSh->GetModel() );
    }

    return pNode;
}


SwOLENode * SwNodes::MakeOLENode( const SwNodeIndex & rWhere,
	const String &rName, sal_Int64 nAspect, SwGrfFmtColl* pGrfColl, SwAttrSet* pAutoAttr )
{
	ASSERT( pGrfColl,"SwNodes::MakeOLENode: Formatpointer ist 0." );

	SwOLENode *pNode =
		new SwOLENode( rWhere, rName, nAspect, pGrfColl, pAutoAttr );

    // set parent if XChild is supported
    //!! needed to supply Math objects with a valid reference device
    uno::Reference< container::XChild > xChild( pNode->GetOLEObj().GetObject().GetObject(), UNO_QUERY );
    if (xChild.is())
    {
        SwDocShell *pDocSh= GetDoc()->GetDocShell();
        if (pDocSh)
            xChild->setParent( pDocSh->GetModel() );
    }

    return pNode;
}

Size SwOLENode::GetTwipSize() const
{
	MapMode aMapMode( MAP_TWIP );
	return ((SwOLENode*)this)->aOLEObj.GetObject().GetSize( &aMapMode );
}

SwCntntNode* SwOLENode::MakeCopy( SwDoc* pDoc, const SwNodeIndex& rIdx ) const
{
	// Falls bereits eine SvPersist-Instanz existiert, nehmen wir diese
    SfxObjectShell* pPersistShell = pDoc->GetPersist();
	if( !pPersistShell )
	{
        // TODO/LATER: is EmbeddedObjectContainer not enough?
        // the created document will be closed by pDoc ( should use SfxObjectShellLock )
		pPersistShell = new SwDocShell( pDoc, SFX_CREATE_MODE_INTERNAL );
		pDoc->SetTmpDocShell( pPersistShell );
		pPersistShell->DoInitNew( NULL );
	}

	// Wir hauen das Ding auf SvPersist-Ebene rein
    // TODO/LATER: check if using the same naming scheme for all apps works here
    ::rtl::OUString aNewName/*( Sw3Io::UniqueName( p->GetStorage(), "Obj" ) )*/;
    SfxObjectShell* pSrc = GetDoc()->GetPersist();

	pPersistShell->GetEmbeddedObjectContainer().CopyAndGetEmbeddedObject(
		pSrc->GetEmbeddedObjectContainer(),
		pSrc->GetEmbeddedObjectContainer().GetEmbeddedObject( aOLEObj.aName ),
		aNewName );

	SwOLENode* pOLENd = pDoc->GetNodes().MakeOLENode( rIdx, aNewName, GetAspect(),
									(SwGrfFmtColl*)pDoc->GetDfltGrfFmtColl(),
									(SwAttrSet*)GetpSwAttrSet() );

	pOLENd->SetChartTblName( GetChartTblName() );
    pOLENd->SetTitle( GetTitle() );
    pOLENd->SetDescription( GetDescription() );
    pOLENd->SetContour( HasContour(), HasAutomaticContour() );
	pOLENd->SetAspect( GetAspect() ); // the replacement image must be already copied

	pOLENd->SetOLESizeInvalid( sal_True );
	pDoc->SetOLEPrtNotifyPending();

	return pOLENd;
}

sal_Bool SwOLENode::IsInGlobalDocSection() const
{
	// suche den "Body Anchor"
	sal_uLong nEndExtraIdx = GetNodes().GetEndOfExtras().GetIndex();
	const SwNode* pAnchorNd = this;
	do {
		SwFrmFmt* pFlyFmt = pAnchorNd->GetFlyFmt();
		if( !pFlyFmt )
			return sal_False;

		const SwFmtAnchor& rAnchor = pFlyFmt->GetAnchor();
		if( !rAnchor.GetCntntAnchor() )
			return sal_False;

		pAnchorNd = &rAnchor.GetCntntAnchor()->nNode.GetNode();
	} while( pAnchorNd->GetIndex() < nEndExtraIdx );

	const SwSectionNode* pSectNd = pAnchorNd->FindSectionNode();
	if( !pSectNd )
		return sal_False;

	while( pSectNd )
	{
		pAnchorNd = pSectNd;
        pSectNd = pAnchorNd->StartOfSectionNode()->FindSectionNode();
	}

	// in pAnchorNd steht der zuletzt gefundene Section Node. Der muss
	// jetzt die Bedingung fuers GlobalDoc erfuellen.
	pSectNd = (SwSectionNode*)pAnchorNd;
	return FILE_LINK_SECTION == pSectNd->GetSection().GetType() &&
			pSectNd->GetIndex() > nEndExtraIdx;
}

sal_Bool SwOLENode::IsOLEObjectDeleted() const
{
	sal_Bool bRet = sal_False;
    if( aOLEObj.xOLERef.is() )
	{
        SfxObjectShell* p = GetDoc()->GetPersist();
		if( p )		// muss da sein
		{
            return !p->GetEmbeddedObjectContainer().HasEmbeddedObject( aOLEObj.aName );
            //SvInfoObjectRef aRef( p->Find( aOLEObj.aName ) );
            //if( aRef.Is() )
            //    bRet = aRef->IsDeleted();
		}
	}
	return bRet;
}

void SwOLENode::GetNewReplacement()
{
	if ( aOLEObj.xOLERef.is() )
		aOLEObj.xOLERef.UpdateReplacement();
}

sal_Bool SwOLENode::UpdateLinkURL_Impl()
{
	sal_Bool bResult = sal_False;

	if ( mpObjectLink )
	{
		String aNewLinkURL;
		GetDoc()->GetLinkManager().GetDisplayNames( mpObjectLink, 0, &aNewLinkURL, 0, 0 );
		if ( !aNewLinkURL.EqualsIgnoreCaseAscii( maLinkURL ) )
		{
			if ( !aOLEObj.xOLERef.is() )
				aOLEObj.GetOleRef();

			uno::Reference< embed::XEmbeddedObject > xObj = aOLEObj.xOLERef.GetObject();
			uno::Reference< embed::XCommonEmbedPersist > xPersObj( xObj, uno::UNO_QUERY );
			OSL_ENSURE( xPersObj.is(), "The object must exist!\n" );
    		if ( xPersObj.is() )
			{
				try
				{
					sal_Int32 nCurState = xObj->getCurrentState();
					if ( nCurState != embed::EmbedStates::LOADED )
						xObj->changeState( embed::EmbedStates::LOADED );

					// TODO/LATER: there should be possible to get current mediadescriptor settings from the object
					uno::Sequence< beans::PropertyValue > aArgs( 1 );
					aArgs[0].Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "URL" ) );
					aArgs[0].Value <<= ::rtl::OUString( aNewLinkURL );
					xPersObj->reload( aArgs, uno::Sequence< beans::PropertyValue >() );

					maLinkURL = aNewLinkURL;
					bResult = sal_True;

					if ( nCurState != embed::EmbedStates::LOADED )
						xObj->changeState( nCurState );
				}
				catch( uno::Exception& )
				{}
			}

			if ( !bResult )
			{
				// TODO/LATER: return the old name to the link manager, is it possible?
			}
		}
	}

	return bResult;
}

void SwOLENode::BreakFileLink_Impl()
{
	SfxObjectShell* pPers = GetDoc()->GetPersist();

	if ( pPers )
	{
		uno::Reference< embed::XStorage > xStorage = pPers->GetStorage();
		if ( xStorage.is() )
		{
			try
			{
				uno::Reference< embed::XLinkageSupport > xLinkSupport( aOLEObj.GetOleRef(), uno::UNO_QUERY_THROW );
				xLinkSupport->breakLink( xStorage, aOLEObj.GetCurrentPersistName() );
				DisconnectFileLink_Impl();
				maLinkURL = String();
			}
			catch( uno::Exception& )
			{
			}
		}
	}
}

void SwOLENode::DisconnectFileLink_Impl()
{
	if ( mpObjectLink )
	{
        GetDoc()->GetLinkManager().Remove( mpObjectLink );
		mpObjectLink = NULL;
	}
}

void SwOLENode::CheckFileLink_Impl()
{
	if ( aOLEObj.xOLERef.GetObject().is() && !mpObjectLink )
	{
		try
		{
			uno::Reference< embed::XLinkageSupport > xLinkSupport( aOLEObj.xOLERef.GetObject(), uno::UNO_QUERY_THROW );
			if ( xLinkSupport->isLink() )
			{
				String aLinkURL = xLinkSupport->getLinkURL();
				if ( aLinkURL.Len() )
				{
					// this is a file link so the model link manager should handle it
					mpObjectLink = new SwEmbedObjectLink( this );
					maLinkURL = aLinkURL;
					GetDoc()->GetLinkManager().InsertFileLink( *mpObjectLink, OBJECT_CLIENT_OLE, aLinkURL, NULL, NULL );
					mpObjectLink->Connect();
				}
			}
		}
		catch( uno::Exception& )
		{
		}
	}
}

// --> OD 2009-03-05 #i99665#
bool SwOLENode::IsChart() const
{
    bool bIsChart( false );

    const uno::Reference< embed::XEmbeddedObject > xEmbObj =
                            const_cast<SwOLEObj&>(GetOLEObj()).GetOleRef();
    if ( xEmbObj.is() )
    {
        SvGlobalName aClassID( xEmbObj->getClassID() );
        bIsChart = SotExchange::IsChart( aClassID );
    }

    return bIsChart;
}
// <--

SwOLEObj::SwOLEObj( const svt::EmbeddedObjectRef& xObj ) :
    pOLENd( 0 ),
    pListener( 0 ),
    xOLERef( xObj )
{
    xOLERef.Lock( sal_True );
    if ( xObj.is() )
    {
        pListener = new SwOLEListener_Impl( this );
        pListener->acquire();
        xObj->addStateChangeListener( pListener );
    }
}


SwOLEObj::SwOLEObj( const String &rString, sal_Int64 nAspect ) :
	pOLENd( 0 ),
    pListener( 0 ),
    aName( rString )
{
    xOLERef.Lock( sal_True );
	xOLERef.SetViewAspect( nAspect );
}


SwOLEObj::~SwOLEObj()
{
    if( pListener )
    {
        if ( xOLERef.is() )
            xOLERef->removeStateChangeListener( pListener );
        pListener->Release();
    }

    if( pOLENd && !pOLENd->GetDoc()->IsInDtor() )
	{
        // if the model is not currently in destruction it means that this object should be removed from the model
		comphelper::EmbeddedObjectContainer* pCnt = xOLERef.GetContainer();

#if OSL_DEBUG_LEVEL > 0
        SfxObjectShell* p = pOLENd->GetDoc()->GetPersist();
        DBG_ASSERT( p, "No document!" );
        if( p )
        {
            comphelper::EmbeddedObjectContainer& rCnt = p->GetEmbeddedObjectContainer();
			OSL_ENSURE( !pCnt || &rCnt == pCnt, "The helper is assigned to unexpected container!\n" );
        }
#endif

		if ( pCnt && pCnt->HasEmbeddedObject( aName ) )
		{
			uno::Reference < container::XChild > xChild( xOLERef.GetObject(), uno::UNO_QUERY );
			if ( xChild.is() )
				xChild->setParent( 0 );

			// not already removed by deleting the object
			xOLERef.AssignToContainer( 0, aName );

			// unlock object so that object can be closed in RemoveEmbeddedObject
			// successful closing of the object will automatically clear the reference then
			xOLERef.Lock(sal_False);

			// Always remove object from conteiner it is connected to
            try
            {
                pCnt->RemoveEmbeddedObject( aName );
            }
            catch ( uno::Exception& )
            {
            }
		}

	}

    if ( xOLERef.is() )
        // in case the object wasn't closed: release it
        // in case the object was not in the container: it's still locked, try to close
        xOLERef.Clear();
}


void SwOLEObj::SetNode( SwOLENode* pNode )
{
	pOLENd = pNode;
    if ( !aName.Len() )
	{
		SwDoc* pDoc = pNode->GetDoc();

		// Falls bereits eine SvPersist-Instanz existiert, nehmen wir diese
        SfxObjectShell* p = pDoc->GetPersist();
        if( !p )
        {
            // TODO/LATER: reicht hier nicht ein EmbeddedObjectContainer? Was passiert mit
            // diesem Dokument?
			ASSERT( !this, "warum wird hier eine DocShell angelegt?" );
            p = new SwDocShell( pDoc, SFX_CREATE_MODE_INTERNAL );
            p->DoInitNew( NULL );
        }

        ::rtl::OUString aObjName;
        uno::Reference < container::XChild > xChild( xOLERef.GetObject(), uno::UNO_QUERY );
        if ( xChild.is() && xChild->getParent() != p->GetModel() )
            // it is possible that the parent was set already
            xChild->setParent( p->GetModel() );
        if (!p->GetEmbeddedObjectContainer().InsertEmbeddedObject( xOLERef.GetObject(), aObjName ) )
        {
            DBG_ERROR( "InsertObject failed" );
        if ( xChild.is() )
            xChild->setParent( 0 );
        }
        else
            xOLERef.AssignToContainer( &p->GetEmbeddedObjectContainer(), aObjName );

		( (SwOLENode*)pOLENd )->CheckFileLink_Impl(); // for this notification nonconst access is required

        aName = aObjName;
	}
}

sal_Bool SwOLEObj::IsOleRef() const
{
    return xOLERef.is();
}

const uno::Reference < embed::XEmbeddedObject > SwOLEObj::GetOleRef()
{
    if( !xOLERef.is() )
	{
        SfxObjectShell* p = pOLENd->GetDoc()->GetPersist();
		ASSERT( p, "kein SvPersist vorhanden" );

        uno::Reference < embed::XEmbeddedObject > xObj = p->GetEmbeddedObjectContainer().GetEmbeddedObject( aName );
        ASSERT( !xOLERef.is(), "rekursiver Aufruf von GetOleRef() ist nicht erlaubt" )

		if ( !xObj.is() )
		{
			//Das Teil konnte nicht geladen werden (wahrsch. Kaputt).
			Rectangle aArea;
			SwFrm *pFrm = pOLENd->getLayoutFrm(0);
			if ( pFrm )
			{
				Size aSz( pFrm->Frm().SSize() );
				const MapMode aSrc ( MAP_TWIP );
				const MapMode aDest( MAP_100TH_MM );
				aSz = OutputDevice::LogicToLogic( aSz, aSrc, aDest );
				aArea.SetSize( aSz );
			}
			else
				aArea.SetSize( Size( 5000,  5000 ) );
            // TODO/LATER: set replacement graphic for dead object
            // It looks as if it should work even without the object, because the replace will be generated automatically
            ::rtl::OUString aTmpName;
            xObj = p->GetEmbeddedObjectContainer().CreateEmbeddedObject( SvGlobalName( SO3_DUMMY_CLASSID ).GetByteSequence(), aTmpName );
		}
        // else
        {
            xOLERef.Assign( xObj, xOLERef.GetViewAspect() );
            xOLERef.AssignToContainer( &p->GetEmbeddedObjectContainer(), aName );
            pListener = new SwOLEListener_Impl( this );
            pListener->acquire();
            xObj->addStateChangeListener( pListener );
        }

		( (SwOLENode*)pOLENd )->CheckFileLink_Impl(); // for this notification nonconst access is required
	}
    else if ( xOLERef->getCurrentState() == embed::EmbedStates::RUNNING )
    {
        // move object to first position in cache
        if( !pOLELRU_Cache )
            pOLELRU_Cache = new SwOLELRUCache;
        pOLELRU_Cache->InsertObj( *this );
    }

    return xOLERef.GetObject();
}

svt::EmbeddedObjectRef& SwOLEObj::GetObject()
{
    GetOleRef();
    return xOLERef;
}

sal_Bool SwOLEObj::UnloadObject()
{
	sal_Bool bRet = sal_True;
	//Nicht notwendig im Doc DTor (MM)
    //ASSERT( pOLERef && pOLERef->Is() && 1 < (*pOLERef)->GetRefCount(),
    //        "Falscher RefCount fuers Unload" );
	if ( pOLENd )
	{
		const SwDoc* pDoc = pOLENd->GetDoc();
		bRet = UnloadObject( xOLERef.GetObject(), pDoc, xOLERef.GetViewAspect() );
	}

	return bRet;
}

sal_Bool SwOLEObj::UnloadObject( uno::Reference< embed::XEmbeddedObject > xObj, const SwDoc* pDoc, sal_Int64 nAspect )
{
	if ( !pDoc )
		return sal_False;

	sal_Bool bRet = sal_True;
   	sal_Int32 nState = xObj.is() ? xObj->getCurrentState() : embed::EmbedStates::LOADED;
   	sal_Bool bIsActive = ( nState != embed::EmbedStates::LOADED && nState != embed::EmbedStates::RUNNING );
	sal_Int64 nMiscStatus = xObj->getStatus( nAspect );

   	if( nState != embed::EmbedStates::LOADED && !pDoc->IsInDtor() && !bIsActive &&
		embed::EmbedMisc::MS_EMBED_ALWAYSRUN != ( nMiscStatus & embed::EmbedMisc::MS_EMBED_ALWAYSRUN ) &&
		embed::EmbedMisc::EMBED_ACTIVATEIMMEDIATELY != ( nMiscStatus & embed::EmbedMisc::EMBED_ACTIVATEIMMEDIATELY ) )
	{
        SfxObjectShell* p = pDoc->GetPersist();
		if( p )
		{
			if( pDoc->get(IDocumentSettingAccess::PURGE_OLE) )
			{
                try
                {
                    uno::Reference < util::XModifiable > xMod( xObj->getComponent(), uno::UNO_QUERY );
                    if( xMod.is() && xMod->isModified() )
                    {
                        uno::Reference < embed::XEmbedPersist > xPers( xObj, uno::UNO_QUERY );
                        if ( xPers.is() )
                            xPers->storeOwn();
                        else {
                            DBG_ERROR("Modified object without persistance in cache!");
                        }
                    }

                    // setting object to loaded state will remove it from cache
                    xObj->changeState( embed::EmbedStates::LOADED );
                }
                catch ( uno::Exception& )
                {
                    bRet = sal_False;
                }
			}
			else
				bRet = sal_False;
		}
	}

	return bRet;
}

String SwOLEObj::GetDescription()
{
    String aResult;
	uno::Reference< embed::XEmbeddedObject > xEmbObj = GetOleRef();
    if ( xEmbObj.is() )
    {
		SvGlobalName aClassID( xEmbObj->getClassID() );
        if ( SotExchange::IsMath( aClassID ) )
            aResult = SW_RES(STR_MATH_FORMULA);
        else if ( SotExchange::IsChart( aClassID ) )
            aResult = SW_RES(STR_CHART);
        else
            aResult = SW_RES(STR_OLE);
    }

    return aResult;
}


SwOLELRUCache::SwOLELRUCache()
	: SvPtrarr( 64, 16 ),
	utl::ConfigItem( OUString::createFromAscii( "Office.Common/Cache" )),
    nLRU_InitSize( 20 ),
    bInUnload( sal_False )
{
	EnableNotification( GetPropertyNames() );
	Load();
}

uno::Sequence< rtl::OUString > SwOLELRUCache::GetPropertyNames()
{
	Sequence< OUString > aNames( 1 );
	OUString* pNames = aNames.getArray();
	pNames[0] = OUString::createFromAscii( "Writer/OLE_Objects" );
	return aNames;
}

void SwOLELRUCache::Notify( const uno::Sequence< rtl::OUString>&  )
{
	Load();
}

void SwOLELRUCache::Commit()
{
}

void SwOLELRUCache::Load()
{
	Sequence< OUString > aNames( GetPropertyNames() );
	Sequence< Any > aValues = GetProperties( aNames );
	const Any* pValues = aValues.getConstArray();
	DBG_ASSERT( aValues.getLength() == aNames.getLength(), "GetProperties failed" );
    if( aValues.getLength() == aNames.getLength() && pValues->hasValue() )
	{
		sal_Int32 nVal = 0;
		*pValues >>= nVal;
        //if( 20 > nVal )
        //    nVal = 20;

		{
            if( nVal < nLRU_InitSize )
			{
                // size of cache has been changed
                sal_uInt16 nCount = SvPtrarr::Count();
                sal_uInt16 nPos = nCount;

                // try to remove the last entries until new maximum size is reached
                while( nCount > nVal )
				{
					SwOLEObj* pObj = (SwOLEObj*) SvPtrarr::GetObject( --nPos );
                    if ( pObj->UnloadObject() )
                        nCount--;
                    if ( !nPos )
                        break;
				}
			}
		}

		nLRU_InitSize = (sal_uInt16)nVal;
	}
}

void SwOLELRUCache::InsertObj( SwOLEObj& rObj )
{
    SwOLEObj* pObj = &rObj;
    sal_uInt16 nPos = SvPtrarr::GetPos( pObj );
    if( nPos )
    {
        // object is currently not the first in cache
        if( USHRT_MAX != nPos )
            SvPtrarr::Remove( nPos );

        SvPtrarr::Insert( pObj, 0 );

        // try to remove objects if necessary (of course not the freshly inserted one at nPos=0)
        sal_uInt16 nCount = SvPtrarr::Count();
        nPos = nCount-1;
        while( nPos && nCount > nLRU_InitSize )
        {
            pObj = (SwOLEObj*) SvPtrarr::GetObject( nPos-- );
            if ( pObj->UnloadObject() )
                nCount--;
        }
    }
}

void SwOLELRUCache::RemoveObj( SwOLEObj& rObj )
{
    sal_uInt16 nPos = SvPtrarr::GetPos( &rObj );
    if ( nPos != 0xFFFF )
        SvPtrarr::Remove( nPos );
    if( !Count() )
        DELETEZ( pOLELRU_Cache );
}

