/* -*- 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_comphelper.hxx"
#include <com/sun/star/container/XChild.hpp>
#include <com/sun/star/container/XNameAccess.hpp>
#include <com/sun/star/embed/XEmbedObjectCreator.hpp>
#include <com/sun/star/embed/XLinkCreator.hpp>
#include <com/sun/star/embed/XEmbedPersist.hpp>
#include <com/sun/star/embed/XLinkageSupport.hpp>
#include <com/sun/star/embed/XTransactedObject.hpp>
#include <com/sun/star/embed/XOptimizedStorage.hpp>
#include <com/sun/star/embed/EntryInitModes.hpp>
#include <com/sun/star/util/XCloseable.hpp>
#include <com/sun/star/util/XModifiable.hpp>
#include <com/sun/star/embed/EmbedStates.hpp>
#include <com/sun/star/datatransfer/XTransferable.hpp>
#include <com/sun/star/beans/XPropertySetInfo.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/embed/Aspects.hpp>
#include <com/sun/star/embed/EmbedMisc.hpp>

#include <comphelper/seqstream.hxx>
#include <comphelper/processfactory.hxx>
#include <comphelper/storagehelper.hxx>
#include <comphelper/embeddedobjectcontainer.hxx>
#include <comphelper/sequence.hxx>
#include <cppuhelper/weakref.hxx>
#include <boost/unordered_map.hpp>
#include <algorithm>

#include <rtl/logfile.hxx>

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

namespace comphelper
{

struct hashObjectName_Impl
{
    size_t operator()(const ::rtl::OUString Str) const
    {
        return (size_t)Str.hashCode();
    }
};

struct eqObjectName_Impl
{
    sal_Bool operator()(const ::rtl::OUString Str1, const ::rtl::OUString Str2) const
    {
        return ( Str1 == Str2 );
    }
};

typedef boost::unordered_map
<
    ::rtl::OUString,
    ::com::sun::star::uno::Reference < com::sun::star::embed::XEmbeddedObject >,
    hashObjectName_Impl,
    eqObjectName_Impl
>
EmbeddedObjectContainerNameMap;

struct EmbedImpl
{
    // TODO/LATER: remove objects from temp. Container storage when object is disposed
    EmbeddedObjectContainerNameMap maObjectContainer;
    uno::Reference < embed::XStorage > mxStorage;
    EmbeddedObjectContainer* mpTempObjectContainer;
    uno::Reference < embed::XStorage > mxImageStorage;
    uno::WeakReference < uno::XInterface > m_xModel;
    //EmbeddedObjectContainerNameMap maTempObjectContainer;
    //uno::Reference < embed::XStorage > mxTempStorage;
    sal_Bool bOwnsStorage;

    const uno::Reference < embed::XStorage >& GetReplacements();
};

const uno::Reference < embed::XStorage >& EmbedImpl::GetReplacements()
{
    if ( !mxImageStorage.is() )
    {
        try
        {
            mxImageStorage = mxStorage->openStorageElement(
                ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("ObjectReplacements")), embed::ElementModes::READWRITE );
        }
        catch ( uno::Exception& )
        {
            mxImageStorage = mxStorage->openStorageElement(
                ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("ObjectReplacements")), embed::ElementModes::READ );
        }
    }

    if ( !mxImageStorage.is() )
        throw io::IOException();

    return mxImageStorage;
}

EmbeddedObjectContainer::EmbeddedObjectContainer()
{
    pImpl = new EmbedImpl;
    pImpl->mxStorage = ::comphelper::OStorageHelper::GetTemporaryStorage();
    pImpl->bOwnsStorage = sal_True;
    pImpl->mpTempObjectContainer = 0;
}

EmbeddedObjectContainer::EmbeddedObjectContainer( const uno::Reference < embed::XStorage >& rStor )
{
    pImpl = new EmbedImpl;
    pImpl->mxStorage = rStor;
    pImpl->bOwnsStorage = sal_False;
    pImpl->mpTempObjectContainer = 0;
}

EmbeddedObjectContainer::EmbeddedObjectContainer( const uno::Reference < embed::XStorage >& rStor, const uno::Reference < uno::XInterface >& xModel )
{
    pImpl = new EmbedImpl;
    pImpl->mxStorage = rStor;
    pImpl->bOwnsStorage = sal_False;
    pImpl->mpTempObjectContainer = 0;
    pImpl->m_xModel = xModel;
}

void EmbeddedObjectContainer::SwitchPersistence( const uno::Reference < embed::XStorage >& rStor )
{
    ReleaseImageSubStorage();

    if ( pImpl->bOwnsStorage )
        pImpl->mxStorage->dispose();

    pImpl->mxStorage = rStor;
    pImpl->bOwnsStorage = sal_False;
}

sal_Bool EmbeddedObjectContainer::CommitImageSubStorage()
{
    if ( pImpl->mxImageStorage.is() )
    {
        try
        {
            sal_Bool bReadOnlyMode = sal_True;
            uno::Reference < beans::XPropertySet > xSet(pImpl->mxImageStorage,uno::UNO_QUERY);
            if ( xSet.is() )
            {
                // get the open mode from the parent storage
                sal_Int32 nMode = 0;
                uno::Any aAny = xSet->getPropertyValue( ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("OpenMode")) );
                if ( aAny >>= nMode )
                    bReadOnlyMode = !(nMode & embed::ElementModes::WRITE );
            } // if ( xSet.is() )
            if ( !bReadOnlyMode )
            {
                uno::Reference< embed::XTransactedObject > xTransact( pImpl->mxImageStorage, uno::UNO_QUERY_THROW );
                xTransact->commit();
            }
        }
        catch( uno::Exception& )
        {
            return sal_False;
        }
    }

    return sal_True;
}

void EmbeddedObjectContainer::ReleaseImageSubStorage()
{
    CommitImageSubStorage();

    if ( pImpl->mxImageStorage.is() )
    {
        try
        {
            pImpl->mxImageStorage->dispose();
            pImpl->mxImageStorage = uno::Reference< embed::XStorage >();
        }
        catch( uno::Exception& )
        {
            OSL_FAIL( "Problems releasing image substorage!\n" );
        }
    }
}

EmbeddedObjectContainer::~EmbeddedObjectContainer()
{
    ReleaseImageSubStorage();

    if ( pImpl->bOwnsStorage )
        pImpl->mxStorage->dispose();

    delete pImpl->mpTempObjectContainer;
    delete pImpl;
}

void EmbeddedObjectContainer::CloseEmbeddedObjects()
{
    EmbeddedObjectContainerNameMap::iterator aIt = pImpl->maObjectContainer.begin();
    while ( aIt != pImpl->maObjectContainer.end() )
    {
        uno::Reference < util::XCloseable > xClose( (*aIt).second, uno::UNO_QUERY );
        if ( xClose.is() )
        {
            try
            {
                xClose->close( sal_True );
            }
            catch ( uno::Exception& )
            {
            }
        }

        aIt++;
    }
}

::rtl::OUString EmbeddedObjectContainer::CreateUniqueObjectName()
{
    ::rtl::OUString aPersistName = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("Object ") );
    ::rtl::OUString aStr;
    sal_Int32 i=1;
    do
    {
        aStr = aPersistName;
        aStr += ::rtl::OUString::valueOf( i++ );
    }
    while( HasEmbeddedObject( aStr ) );
    // TODO/LATER: should we consider deleted objects?

    return aStr;
}

uno::Sequence < ::rtl::OUString > EmbeddedObjectContainer::GetObjectNames()
{
    uno::Sequence < ::rtl::OUString > aSeq( pImpl->maObjectContainer.size() );
    EmbeddedObjectContainerNameMap::iterator aIt = pImpl->maObjectContainer.begin();
    sal_Int32 nIdx=0;
    while ( aIt != pImpl->maObjectContainer.end() )
        aSeq[nIdx++] = (*aIt++).first;
    return aSeq;
}

sal_Bool EmbeddedObjectContainer::HasEmbeddedObjects()
{
    return pImpl->maObjectContainer.size() != 0;
}

sal_Bool EmbeddedObjectContainer::HasEmbeddedObject( const ::rtl::OUString& rName )
{
    EmbeddedObjectContainerNameMap::iterator aIt = pImpl->maObjectContainer.find( rName );
    if ( aIt == pImpl->maObjectContainer.end() )
    {
        uno::Reference < container::XNameAccess > xAccess( pImpl->mxStorage, uno::UNO_QUERY );
        return xAccess->hasByName(rName);
    }
    else
        return sal_True;
}

sal_Bool EmbeddedObjectContainer::HasEmbeddedObject( const uno::Reference < embed::XEmbeddedObject >& xObj )
{
    EmbeddedObjectContainerNameMap::iterator aIt = pImpl->maObjectContainer.begin();
    while ( aIt != pImpl->maObjectContainer.end() )
    {
        if ( (*aIt).second == xObj )
            return sal_True;
        else
            aIt++;
    }

    return sal_False;
}

sal_Bool EmbeddedObjectContainer::HasInstantiatedEmbeddedObject( const ::rtl::OUString& rName )
{
    // allows to detect whether the object was already instantiated
    // currently the filter instantiate it on loading, so this method allows
    // to avoid objects pointing to the same persistence
    EmbeddedObjectContainerNameMap::iterator aIt = pImpl->maObjectContainer.find( rName );
    return ( aIt != pImpl->maObjectContainer.end() );
}

::rtl::OUString EmbeddedObjectContainer::GetEmbeddedObjectName( const ::com::sun::star::uno::Reference < ::com::sun::star::embed::XEmbeddedObject >& xObj )
{
    EmbeddedObjectContainerNameMap::iterator aIt = pImpl->maObjectContainer.begin();
    while ( aIt != pImpl->maObjectContainer.end() )
    {
        if ( (*aIt).second == xObj )
            return (*aIt).first;
        else
            aIt++;
    }

    OSL_FAIL( "Unknown object!" );
    return ::rtl::OUString();
}

uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::GetEmbeddedObject( const ::rtl::OUString& rName )
{
    RTL_LOGFILE_CONTEXT( aLog, "comphelper (mv76033) comphelper::EmbeddedObjectContainer::GetEmbeddedObject" );

    OSL_ENSURE( rName.getLength(), "Empty object name!");

    uno::Reference < embed::XEmbeddedObject > xObj;
    EmbeddedObjectContainerNameMap::iterator aIt = pImpl->maObjectContainer.find( rName );

#if OSL_DEBUG_LEVEL > 1
    uno::Reference < container::XNameAccess > xAccess( pImpl->mxStorage, uno::UNO_QUERY );
    uno::Sequence< ::rtl::OUString> aSeq = xAccess->getElementNames();
    const ::rtl::OUString* pIter = aSeq.getConstArray();
    const ::rtl::OUString* pEnd	  = pIter + aSeq.getLength();
    for(;pIter != pEnd;++pIter)
    {
        (void)*pIter;
    }
    OSL_ENSURE( aIt != pImpl->maObjectContainer.end() || xAccess->hasByName(rName), "Could not return object!" );
#endif

    // check if object was already created
    if ( aIt != pImpl->maObjectContainer.end() )
        xObj = (*aIt).second;
    else
        xObj = Get_Impl( rName, uno::Reference < embed::XEmbeddedObject >() );

    return xObj;
}

uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::Get_Impl( const ::rtl::OUString& rName, const uno::Reference < embed::XEmbeddedObject >& xCopy )
{
    uno::Reference < embed::XEmbeddedObject > xObj;
    try
    {
        // create the object from the storage
        uno::Reference < beans::XPropertySet > xSet( pImpl->mxStorage, uno::UNO_QUERY );
        sal_Bool bReadOnlyMode = sal_True;
        if ( xSet.is() )
        {
            // get the open mode from the parent storage
            sal_Int32 nMode = 0;
            uno::Any aAny = xSet->getPropertyValue( ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("OpenMode")) );
            if ( aAny >>= nMode )
                bReadOnlyMode = !(nMode & embed::ElementModes::WRITE );
        }

        // object was not added until now - should happen only by calling this method from "inside"
        //TODO/LATER: it would be good to detect an error when an object should be created already, but isn't (not an "inside" call)
        uno::Reference < embed::XEmbedObjectCreator > xFactory( ::comphelper::getProcessServiceFactory()->createInstance(
                ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.embed.EmbeddedObjectCreator")) ), uno::UNO_QUERY );
        uno::Sequence< beans::PropertyValue > aObjDescr( xCopy.is() ? 2 : 1 );
        aObjDescr[0].Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Parent" ) );
        aObjDescr[0].Value <<= pImpl->m_xModel.get();
        if ( xCopy.is() )
        {
            aObjDescr[1].Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "CloneFrom" ) );
            aObjDescr[1].Value <<= xCopy;
        }

        uno::Sequence< beans::PropertyValue > aMediaDescr( 1 );
        aMediaDescr[0].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("ReadOnly"));
        aMediaDescr[0].Value <<= bReadOnlyMode;
        xObj = uno::Reference < embed::XEmbeddedObject >( xFactory->createInstanceInitFromEntry(
                pImpl->mxStorage, rName,
                aMediaDescr, aObjDescr ), uno::UNO_QUERY );

        // insert object into my list
        AddEmbeddedObject( xObj, rName );
    }
    catch ( uno::Exception& )
    {
    }

    return xObj;
}

uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::CreateEmbeddedObject( const uno::Sequence < sal_Int8 >& rClassId,
            const uno::Sequence < beans::PropertyValue >& rArgs, ::rtl::OUString& rNewName )
{
    RTL_LOGFILE_CONTEXT( aLog, "comphelper (mv76033) comphelper::EmbeddedObjectContainer::CreateEmbeddedObject" );

    if ( !rNewName.getLength() )
        rNewName = CreateUniqueObjectName();

    OSL_ENSURE( !HasEmbeddedObject(rNewName), "Object to create already exists!");

    // create object from classid by inserting it into storage
    uno::Reference < embed::XEmbeddedObject > xObj;
    try
    {
        uno::Reference < embed::XEmbedObjectCreator > xFactory( ::comphelper::getProcessServiceFactory()->createInstance(
                ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.embed.EmbeddedObjectCreator")) ), uno::UNO_QUERY );

        uno::Sequence< beans::PropertyValue > aObjDescr( rArgs.getLength() + 1 );
        aObjDescr[0].Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Parent" ) );
        aObjDescr[0].Value <<= pImpl->m_xModel.get();
        ::std::copy( rArgs.getConstArray(), rArgs.getConstArray() + rArgs.getLength(), aObjDescr.getArray() + 1 );
        xObj = uno::Reference < embed::XEmbeddedObject >( xFactory->createInstanceInitNew(
                    rClassId, ::rtl::OUString(), pImpl->mxStorage, rNewName,
                    aObjDescr ), uno::UNO_QUERY );

        AddEmbeddedObject( xObj, rNewName );

        OSL_ENSURE( !xObj.is() || xObj->getCurrentState() != embed::EmbedStates::LOADED,
                    "A freshly create object should be running always!\n" );
    }
    catch ( uno::Exception& )
    {
    }

    return xObj;
}

uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::CreateEmbeddedObject( const uno::Sequence < sal_Int8 >& rClassId, ::rtl::OUString& rNewName )
{
    return CreateEmbeddedObject( rClassId, uno::Sequence < beans::PropertyValue >(), rNewName );
}

void EmbeddedObjectContainer::AddEmbeddedObject( const ::com::sun::star::uno::Reference < ::com::sun::star::embed::XEmbeddedObject >& xObj, const ::rtl::OUString& rName )
{
    RTL_LOGFILE_CONTEXT( aLog, "comphelper (mv76033) comphelper::EmbeddedObjectContainer::AddEmbeddedObject" );

#if OSL_DEBUG_LEVEL > 1
    OSL_ENSURE( rName.getLength(), "Added object doesn't have a name!");
    uno::Reference < container::XNameAccess > xAccess( pImpl->mxStorage, uno::UNO_QUERY );
    uno::Reference < embed::XEmbedPersist > xEmb( xObj, uno::UNO_QUERY );
    uno::Reference < embed::XLinkageSupport > xLink( xEmb, uno::UNO_QUERY );
    // if the object has a persistance and the object is not a link than it must have persistence entry in the storage
    OSL_ENSURE( !( xEmb.is() && ( !xLink.is() || !xLink->isLink() ) ) || xAccess->hasByName(rName),
                    "Added element not in storage!" );
#endif

    // remember object - it needs to be in storage already
    EmbeddedObjectContainerNameMap::iterator aIt = pImpl->maObjectContainer.find( rName );
    OSL_ENSURE( aIt == pImpl->maObjectContainer.end(), "Element already inserted!" );
    pImpl->maObjectContainer[ rName ] = xObj;
    uno::Reference < container::XChild > xChild( xObj, uno::UNO_QUERY );
    if ( xChild.is() && xChild->getParent() != pImpl->m_xModel.get() )
        xChild->setParent( pImpl->m_xModel.get() );

    // look for object in temorary container
    if ( pImpl->mpTempObjectContainer )
    {
        aIt = pImpl->mpTempObjectContainer->pImpl->maObjectContainer.begin();
        while ( aIt != pImpl->mpTempObjectContainer->pImpl->maObjectContainer.end() )
        {
            if ( (*aIt).second == xObj )
            {
                // copy replacement image from temporary container (if there is any)
                ::rtl::OUString aTempName = (*aIt).first;
                ::rtl::OUString aMediaType;
                uno::Reference < io::XInputStream > xStream = pImpl->mpTempObjectContainer->GetGraphicStream( xObj, &aMediaType );
                if ( xStream.is() )
                {
                    InsertGraphicStream( xStream, rName, aMediaType );
                    xStream = 0;
                    pImpl->mpTempObjectContainer->RemoveGraphicStream( aTempName );
                }

                // remove object from storage of temporary container
                uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
                if ( xPersist.is() )
                {
                    try
                    {
                        pImpl->mpTempObjectContainer->pImpl->mxStorage->removeElement( aTempName );
                    }
                    catch ( uno::Exception& )
                    {
                    }
                }

                // temp. container needs to forget the object
                pImpl->mpTempObjectContainer->pImpl->maObjectContainer.erase( aIt );
                break;
            }
            else
                aIt++;
        }
    }
}

sal_Bool EmbeddedObjectContainer::StoreEmbeddedObject( const uno::Reference < embed::XEmbeddedObject >& xObj, ::rtl::OUString& rName, sal_Bool bCopy )
{
    RTL_LOGFILE_CONTEXT( aLog, "comphelper (mv76033) comphelper::EmbeddedObjectContainer::StoreEmbeddedObject" );

    uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
    if ( !rName.getLength() )
        rName = CreateUniqueObjectName();

#if OSL_DEBUG_LEVEL > 1
    uno::Reference < container::XNameAccess > xAccess( pImpl->mxStorage, uno::UNO_QUERY );
    OSL_ENSURE( !xPersist.is() || !xAccess->hasByName(rName), "Inserting element already present in storage!" );
    OSL_ENSURE( xPersist.is() || xObj->getCurrentState() == embed::EmbedStates::RUNNING, "Non persistent object inserted!");
#endif

    // insert objects' storage into the container storage (if object has one)
    try
    {
        if ( xPersist.is() )
        {
            uno::Sequence < beans::PropertyValue > aSeq;
            if ( bCopy )
                xPersist->storeToEntry( pImpl->mxStorage, rName, aSeq, aSeq );
            else
            {
                //TODO/LATER: possible optimisation, don't store immediately
                //xPersist->setPersistentEntry( pImpl->mxStorage, rName, embed::EntryInitModes::ENTRY_NO_INIT, aSeq, aSeq );
                xPersist->storeAsEntry( pImpl->mxStorage, rName, aSeq, aSeq );
                xPersist->saveCompleted( sal_True );
            }
        }
    }
    catch ( uno::Exception& )
    {
        // TODO/LATER: better error recovery should keep storage intact
        return sal_False;
    }

    return sal_True;
}

sal_Bool EmbeddedObjectContainer::InsertEmbeddedObject( const uno::Reference < embed::XEmbeddedObject >& xObj, ::rtl::OUString& rName )
{
    RTL_LOGFILE_CONTEXT( aLog, "comphelper (mv76033) comphelper::EmbeddedObjectContainer::InsertEmbeddedObject( Object )" );
    // store it into the container storage
    if ( StoreEmbeddedObject( xObj, rName, sal_False ) )
    {
        // remember object
        AddEmbeddedObject( xObj, rName );
        return sal_True;
    }
    else
        return sal_False;
}

uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::InsertEmbeddedObject( const uno::Reference < io::XInputStream >& xStm, ::rtl::OUString& rNewName )
{
    RTL_LOGFILE_CONTEXT( aLog, "comphelper (mv76033) comphelper::EmbeddedObjectContainer::InsertEmbeddedObject( InputStream )" );

    if ( !rNewName.getLength() )
        rNewName = CreateUniqueObjectName();

    // store it into the container storage
    sal_Bool bIsStorage = sal_False;
    try
    {
        // first try storage persistence
        uno::Reference < embed::XStorage > xStore = ::comphelper::OStorageHelper::GetStorageFromInputStream( xStm );

        // storage was created from stream successfully
        bIsStorage = sal_True;

        uno::Reference < embed::XStorage > xNewStore = pImpl->mxStorage->openStorageElement( rNewName, embed::ElementModes::READWRITE );
        xStore->copyToStorage( xNewStore );
    }
    catch ( uno::Exception& )
    {
        if ( bIsStorage )
            // it is storage persistence, but opening of new substorage or copying to it failed
            return uno::Reference < embed::XEmbeddedObject >();

        // stream didn't contain a storage, now try stream persistence
        try
        {
            uno::Reference < io::XStream > xNewStream = pImpl->mxStorage->openStreamElement( rNewName, embed::ElementModes::READWRITE );
            ::comphelper::OStorageHelper::CopyInputToOutput( xStm, xNewStream->getOutputStream() );

            // No mediatype is provided so the default for OLE objects value is used
            // it is correct so for now, but what if somebody introduces a new stream based embedded object?
            // Probably introducing of such an object must be restricted ( a storage must be used! ).
            uno::Reference< beans::XPropertySet > xProps( xNewStream, uno::UNO_QUERY_THROW );
            xProps->setPropertyValue(
                    ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "MediaType" ) ),
                    uno::makeAny( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "application/vnd.sun.star.oleobject" ) ) ) );
        }
        catch ( uno::Exception& )
        {
            // complete disaster!
            return uno::Reference < embed::XEmbeddedObject >();
        }
    }

    // stream was copied into the container storage in either way, now try to open something form it
    uno::Reference < embed::XEmbeddedObject > xRet = GetEmbeddedObject( rNewName );
    try
    {
        if ( !xRet.is() )
            // no object could be created, so withdraw insertion
            pImpl->mxStorage->removeElement( rNewName );
    }
    catch ( uno::Exception& )
    {
    }

    return xRet;
}

uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::InsertEmbeddedObject( const ::com::sun::star::uno::Sequence < ::com::sun::star::beans::PropertyValue >& aMedium, ::rtl::OUString& rNewName )
{
    RTL_LOGFILE_CONTEXT( aLog, "comphelper (mv76033) comphelper::EmbeddedObjectContainer::InsertEmbeddedObject( MediaDescriptor )" );

    if ( !rNewName.getLength() )
        rNewName = CreateUniqueObjectName();

    uno::Reference < embed::XEmbeddedObject > xObj;
    try
    {
        uno::Reference < embed::XEmbedObjectCreator > xFactory( ::comphelper::getProcessServiceFactory()->createInstance(
                ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.embed.EmbeddedObjectCreator")) ), uno::UNO_QUERY );
        uno::Sequence< beans::PropertyValue > aObjDescr( 1 );
        aObjDescr[0].Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Parent" ) );
        aObjDescr[0].Value <<= pImpl->m_xModel.get();
        xObj = uno::Reference < embed::XEmbeddedObject >( xFactory->createInstanceInitFromMediaDescriptor(
                pImpl->mxStorage, rNewName, aMedium, aObjDescr ), uno::UNO_QUERY );
        uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );

           OSL_ENSURE( !xObj.is() || xObj->getCurrentState() != embed::EmbedStates::LOADED,
                    "A freshly create object should be running always!\n" );

        // possible optimization: store later!
        if ( xPersist.is())
            xPersist->storeOwn();

        AddEmbeddedObject( xObj, rNewName );
    }
    catch ( uno::Exception& )
    {
    }

    return xObj;
}

uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::InsertEmbeddedLink( const ::com::sun::star::uno::Sequence < ::com::sun::star::beans::PropertyValue >& aMedium, ::rtl::OUString& rNewName )
{
    RTL_LOGFILE_CONTEXT( aLog, "comphelper (mv76033) comphelper::EmbeddedObjectContainer::InsertEmbeddedLink" );

    if ( !rNewName.getLength() )
        rNewName = CreateUniqueObjectName();

    uno::Reference < embed::XEmbeddedObject > xObj;
    try
    {
        uno::Reference < embed::XLinkCreator > xFactory( ::comphelper::getProcessServiceFactory()->createInstance(
                ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.embed.EmbeddedObjectCreator")) ), uno::UNO_QUERY );
        uno::Sequence< beans::PropertyValue > aObjDescr( 1 );
        aObjDescr[0].Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Parent" ) );
        aObjDescr[0].Value <<= pImpl->m_xModel.get();
        xObj = uno::Reference < embed::XEmbeddedObject >( xFactory->createInstanceLink(
                pImpl->mxStorage, rNewName, aMedium, aObjDescr ), uno::UNO_QUERY );

        uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );

           OSL_ENSURE( !xObj.is() || xObj->getCurrentState() != embed::EmbedStates::LOADED,
                    "A freshly create object should be running always!\n" );

        // possible optimization: store later!
        if ( xPersist.is())
            xPersist->storeOwn();

        AddEmbeddedObject( xObj, rNewName );
    }
    catch ( uno::Exception& )
    {
    }

    return xObj;
}

sal_Bool EmbeddedObjectContainer::TryToCopyGraphReplacement( EmbeddedObjectContainer& rSrc,
                                                            const ::rtl::OUString& aOrigName,
                                                            const ::rtl::OUString& aTargetName )
{
    RTL_LOGFILE_CONTEXT( aLog, "comphelper (mv76033) comphelper::EmbeddedObjectContainer::TryToCopyGraphReplacement" );

    sal_Bool bResult = sal_False;

    if ( ( &rSrc != this || !aOrigName.equals( aTargetName ) ) && aOrigName.getLength() && aTargetName.getLength() )
    {
        ::rtl::OUString aMediaType;
        uno::Reference < io::XInputStream > xGrStream = rSrc.GetGraphicStream( aOrigName, &aMediaType );
        if ( xGrStream.is() )
            bResult = InsertGraphicStream( xGrStream, aTargetName, aMediaType );
    }

    return bResult;
}

sal_Bool EmbeddedObjectContainer::CopyEmbeddedObject( EmbeddedObjectContainer& rSrc, const uno::Reference < embed::XEmbeddedObject >& xObj, ::rtl::OUString& rName )
{
    RTL_LOGFILE_CONTEXT( aLog, "comphelper (mv76033) comphelper::EmbeddedObjectContainer::CopyEmbeddedObject" );

    OSL_FAIL( "This method is depricated! Use EmbeddedObjectContainer::CopyAndGetEmbeddedObject() to copy object!\n" );

    // get the object name before(!) it is assigned to a new storage
    ::rtl::OUString aOrigName;
    uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
    if ( xPersist.is() )
        aOrigName = xPersist->getEntryName();

    if ( !rName.getLength() )
        rName = CreateUniqueObjectName();

    if ( StoreEmbeddedObject( xObj, rName, sal_True ) )
    {
        TryToCopyGraphReplacement( rSrc, aOrigName, rName );
        return sal_True;
    }

    return sal_False;
}

uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::CopyAndGetEmbeddedObject( EmbeddedObjectContainer& rSrc, const uno::Reference < embed::XEmbeddedObject >& xObj, ::rtl::OUString& rName )
{
    RTL_LOGFILE_CONTEXT( aLog, "comphelper (mv76033) comphelper::EmbeddedObjectContainer::CopyAndGetEmbeddedObject" );

    uno::Reference< embed::XEmbeddedObject > xResult;

    // TODO/LATER: For now only objects that implement XEmbedPersist have a replacement image, it might change in future
    // do an incompatible change so that object name is provided in all the move and copy methods
    ::rtl::OUString aOrigName;
    try
    {
        uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY_THROW );
        aOrigName = xPersist->getEntryName();
    }
    catch( uno::Exception& )
    {}

    if ( !rName.getLength() )
        rName = CreateUniqueObjectName();

    // objects without persistance are not really stored by the method
       if ( xObj.is() && StoreEmbeddedObject( xObj, rName, sal_True ) )
    {
        xResult = Get_Impl( rName, xObj);
        if ( !xResult.is() )
        {
            // this is a case when object has no real persistence
            // in such cases a new object should be explicitly created and initialized with the data of the old one
            try
            {
                uno::Reference< embed::XLinkageSupport > xOrigLinkage( xObj, uno::UNO_QUERY );
                if ( xOrigLinkage.is() && xOrigLinkage->isLink() )
                {
                    // this is a OOo link, it has no persistence
                    ::rtl::OUString aURL = xOrigLinkage->getLinkURL();
                    if ( !aURL.getLength() )
                        throw uno::RuntimeException();

                    // create new linked object from the URL the link is based on
                    uno::Reference < embed::XLinkCreator > xCreator(
                        ::comphelper::getProcessServiceFactory()->createInstance(
                            ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.embed.EmbeddedObjectCreator") ) ),
                        uno::UNO_QUERY_THROW );

                    uno::Sequence< beans::PropertyValue > aMediaDescr( 1 );
                    aMediaDescr[0].Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "URL" ) );
                    aMediaDescr[0].Value <<= aURL;
                    uno::Sequence< beans::PropertyValue > aObjDescr( 1 );
                    aObjDescr[0].Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Parent" ) );
                    aObjDescr[0].Value <<= pImpl->m_xModel.get();
                    xResult = uno::Reference < embed::XEmbeddedObject >(
                                xCreator->createInstanceLink(
                                    pImpl->mxStorage,
                                    rName,
                                    aMediaDescr,
                                    aObjDescr ),
                                uno::UNO_QUERY_THROW );
                }
                else
                {
                    // the component is required for copying of this object
                    if ( xObj->getCurrentState() == embed::EmbedStates::LOADED )
                        xObj->changeState( embed::EmbedStates::RUNNING );

                    // this must be an object based on properties, otherwise we can not copy it currently
                    uno::Reference< beans::XPropertySet > xOrigProps( xObj->getComponent(), uno::UNO_QUERY_THROW );

                    // use object class ID to create a new one and tranfer all the properties
                    uno::Reference < embed::XEmbedObjectCreator > xCreator(
                        ::comphelper::getProcessServiceFactory()->createInstance(
                            ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.embed.EmbeddedObjectCreator") ) ),
                        uno::UNO_QUERY_THROW );

                    uno::Sequence< beans::PropertyValue > aObjDescr( 1 );
                    aObjDescr[0].Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Parent" ) );
                    aObjDescr[0].Value <<= pImpl->m_xModel.get();
                    xResult = uno::Reference < embed::XEmbeddedObject >(
                                xCreator->createInstanceInitNew(
                                    xObj->getClassID(),
                                    xObj->getClassName(),
                                    pImpl->mxStorage,
                                    rName,
                                    aObjDescr ),
                                uno::UNO_QUERY_THROW );

                    if ( xResult->getCurrentState() == embed::EmbedStates::LOADED )
                        xResult->changeState( embed::EmbedStates::RUNNING );

                    uno::Reference< beans::XPropertySet > xTargetProps( xResult->getComponent(), uno::UNO_QUERY_THROW );

                    // copy all the properties from xOrigProps to xTargetProps
                    uno::Reference< beans::XPropertySetInfo > xOrigInfo = xOrigProps->getPropertySetInfo();
                    if ( !xOrigInfo.is() )
                        throw uno::RuntimeException();

                    uno::Sequence< beans::Property > aPropertiesList = xOrigInfo->getProperties();
                    for ( sal_Int32 nInd = 0; nInd < aPropertiesList.getLength(); nInd++ )
                    {
                        try
                        {
                            xTargetProps->setPropertyValue(
                                aPropertiesList[nInd].Name,
                                xOrigProps->getPropertyValue( aPropertiesList[nInd].Name ) );
                        }
                        catch( beans::PropertyVetoException& )
                        {
                            // impossibility to copy readonly property is not treated as an error for now
                            // but the assertion is helpful to detect such scenarios and review them
                            OSL_FAIL( "Could not copy readonly property!\n" );
                        }
                    }
                }

                   if ( xResult.is() )
                    AddEmbeddedObject( xResult, rName );
            }
            catch( uno::Exception& )
            {
                if ( xResult.is() )
                {
                    try
                    {
                        xResult->close( sal_True );
                    }
                    catch( uno::Exception& )
                    {}
                    xResult = uno::Reference< embed::XEmbeddedObject >();
                }
            }
        }
    }

    OSL_ENSURE( xResult.is(), "Can not copy embedded object that has no persistance!\n" );

    if ( xResult.is() )
    {
        // the object is successfully copied, try to copy graphical replacement
        if ( aOrigName.getLength() )
            TryToCopyGraphReplacement( rSrc, aOrigName, rName );

        // the object might need the size to be set
        try
        {
            if ( xResult->getStatus( embed::Aspects::MSOLE_CONTENT ) & embed::EmbedMisc::EMBED_NEEDSSIZEONLOAD )
                xResult->setVisualAreaSize( embed::Aspects::MSOLE_CONTENT,
                                            xObj->getVisualAreaSize( embed::Aspects::MSOLE_CONTENT ) );
        }
        catch( uno::Exception& )
        {}
    }

    return xResult;
}

sal_Bool EmbeddedObjectContainer::MoveEmbeddedObject( EmbeddedObjectContainer& rSrc, const uno::Reference < embed::XEmbeddedObject >& xObj, ::rtl::OUString& rName )
{
    RTL_LOGFILE_CONTEXT( aLog, "comphelper (mv76033) comphelper::EmbeddedObjectContainer::MoveEmbeddedObject( Object )" );

    // get the object name before(!) it is assigned to a new storage
    uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
    ::rtl::OUString aName;
    if ( xPersist.is() )
        aName = xPersist->getEntryName();

    // now move the object to the new container; the returned name is the new persist name in this container
    sal_Bool bRet;

    try
    {
        bRet = InsertEmbeddedObject( xObj, rName );
        if ( bRet )
            TryToCopyGraphReplacement( rSrc, aName, rName );
    }
    catch ( uno::Exception& e )
    {
        (void)e;
        OSL_FAIL( "Failed to insert embedded object into storage!" );
        bRet = sal_False;
    }

    if ( bRet )
    {
        // now remove the object from the former container
        bRet = sal_False;
        EmbeddedObjectContainerNameMap::iterator aIt = rSrc.pImpl->maObjectContainer.begin();
        while ( aIt != rSrc.pImpl->maObjectContainer.end() )
        {
            if ( (*aIt).second == xObj )
            {
                rSrc.pImpl->maObjectContainer.erase( aIt );
                bRet = sal_True;
                break;
            }

            aIt++;
        }

        OSL_ENSURE( bRet, "Object not found for removal!" );
        if ( xPersist.is() )
        {
            // now it's time to remove the storage from the container storage
            try
            {
                if ( xPersist.is() )
                    rSrc.pImpl->mxStorage->removeElement( aName );
            }
            catch ( uno::Exception& )
            {
                OSL_FAIL( "Failed to remove object from storage!" );
                bRet = sal_False;
            }
        }

        // rSrc.RemoveGraphicStream( aName );
    }

    return bRet;
}

sal_Bool EmbeddedObjectContainer::RemoveEmbeddedObject( const ::rtl::OUString& rName, sal_Bool bClose )
{
    RTL_LOGFILE_CONTEXT( aLog, "comphelper (mv76033) comphelper::EmbeddedObjectContainer::RemoveEmbeddedObject( Name )" );

    uno::Reference < embed::XEmbeddedObject > xObj = GetEmbeddedObject( rName );
    if ( xObj.is() )
        return RemoveEmbeddedObject( xObj, bClose );
    else
        return sal_False;
}

sal_Bool EmbeddedObjectContainer::MoveEmbeddedObject( const ::rtl::OUString& rName, EmbeddedObjectContainer& rCnt )
{
    RTL_LOGFILE_CONTEXT( aLog, "comphelper (mv76033) comphelper::EmbeddedObjectContainer::MoveEmbeddedObject( Name )" );

    // find object entry
    EmbeddedObjectContainerNameMap::iterator aIt2 = rCnt.pImpl->maObjectContainer.find( rName );
    OSL_ENSURE( aIt2 == rCnt.pImpl->maObjectContainer.end(), "Object does already exist in target container!" );

    if ( aIt2 != rCnt.pImpl->maObjectContainer.end() )
        return sal_False;

    uno::Reference < embed::XEmbeddedObject > xObj;
    EmbeddedObjectContainerNameMap::iterator aIt = pImpl->maObjectContainer.find( rName );
    if ( aIt != pImpl->maObjectContainer.end() )
    {
        xObj = (*aIt).second;
        try
        {
            if ( xObj.is() )
            {
                // move object
                ::rtl::OUString aName( rName );
                rCnt.InsertEmbeddedObject( xObj, aName );
                pImpl->maObjectContainer.erase( aIt );
                uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
                if ( xPersist.is() )
                    pImpl->mxStorage->removeElement( rName );
            }
            else
            {
                // copy storages; object *must* have persistence!
                uno::Reference < embed::XStorage > xOld = pImpl->mxStorage->openStorageElement( rName, embed::ElementModes::READ );
                uno::Reference < embed::XStorage > xNew = rCnt.pImpl->mxStorage->openStorageElement( rName, embed::ElementModes::READWRITE );
                xOld->copyToStorage( xNew );
            }

            rCnt.TryToCopyGraphReplacement( *this, rName, rName );
            // RemoveGraphicStream( rName );

            return sal_True;
        }
        catch ( uno::Exception& )
        {
            OSL_FAIL("Could not move object!");
            return sal_False;
        }

    }
    else
        OSL_FAIL("Unknown object!");
    return sal_False;
}

sal_Bool EmbeddedObjectContainer::RemoveEmbeddedObject( const uno::Reference < embed::XEmbeddedObject >& xObj, sal_Bool bClose )
{
    RTL_LOGFILE_CONTEXT( aLog, "comphelper (mv76033) comphelper::EmbeddedObjectContainer::RemoveEmbeddedObject( Object )" );

    uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
    ::rtl::OUString aName;
    if ( xPersist.is() )
        aName = xPersist->getEntryName();

#if OSL_DEBUG_LEVEL > 1
    uno::Reference < container::XNameAccess > xAccess( pImpl->mxStorage, uno::UNO_QUERY );
    uno::Reference < embed::XLinkageSupport > xLink( xPersist, uno::UNO_QUERY );
    sal_Bool bIsNotEmbedded = !xPersist.is() || ( xLink.is() && xLink->isLink() );

    // if the object has a persistance and the object is not a link than it must have persistence entry in the storage
    OSL_ENSURE( bIsNotEmbedded || xAccess->hasByName(aName), "Removing element not present in storage!" );
#endif

    // try to close it if permitted
    if ( bClose )
    {
        uno::Reference < ::util::XCloseable > xClose( xObj, uno::UNO_QUERY );
        try
        {
            xClose->close( sal_True );
        }
        catch ( util::CloseVetoException& )
        {
            bClose = sal_False;
        }
    }

    if ( !bClose )
    {
        // somebody still needs the object, so we must assign a temporary persistence
        try
        {
            if ( xPersist.is() )
            {
                /*
                //TODO/LATER: needs storage handling!  Why not letting the object do it?!
                if ( !pImpl->mxTempStorage.is() )
                    pImpl->mxTempStorage = ::comphelper::OStorageHelper::GetTemporaryStorage();
                uno::Sequence < beans::PropertyValue > aSeq;

                ::rtl::OUString aTmpPersistName = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("Object ") );
                aTmpPersistName += ::rtl::OUString::valueOf( (sal_Int32) pImpl->maTempObjectContainer.size() );

                xPersist->storeAsEntry( pImpl->mxTempStorage, aTmpPersistName, aSeq, aSeq );
                xPersist->saveCompleted( sal_True );

                pImpl->maTempObjectContainer[ aTmpPersistName ] = uno::Reference < embed::XEmbeddedObject >();
                */

                if ( !pImpl->mpTempObjectContainer )
                {
                    pImpl->mpTempObjectContainer = new EmbeddedObjectContainer();
                    try
                    {
                        // TODO/LATER: in future probably the temporary container will have two storages ( of two formats )
                        //             the media type will be provided with object insertion
                        ::rtl::OUString aOrigStorMediaType;
                        uno::Reference< beans::XPropertySet > xStorProps( pImpl->mxStorage, uno::UNO_QUERY_THROW );
                        static const ::rtl::OUString s_sMediaType(RTL_CONSTASCII_USTRINGPARAM("MediaType"));
                        xStorProps->getPropertyValue( s_sMediaType ) >>= aOrigStorMediaType;

                        OSL_ENSURE( aOrigStorMediaType.getLength(), "No valuable media type in the storage!\n" );

                        uno::Reference< beans::XPropertySet > xTargetStorProps(
                                                                    pImpl->mpTempObjectContainer->pImpl->mxStorage,
                                                                    uno::UNO_QUERY_THROW );
                        xTargetStorProps->setPropertyValue( s_sMediaType,uno::makeAny( aOrigStorMediaType ) );
                    }
                    catch( uno::Exception& )
                    {
                        OSL_FAIL( "Can not set the new media type to a storage!\n" );
                    }
                }

                ::rtl::OUString aTempName, aMediaType;
                pImpl->mpTempObjectContainer->InsertEmbeddedObject( xObj, aTempName );

                uno::Reference < io::XInputStream > xStream = GetGraphicStream( xObj, &aMediaType );
                if ( xStream.is() )
                    pImpl->mpTempObjectContainer->InsertGraphicStream( xStream, aTempName, aMediaType );

                // object is stored, so at least it can be set to loaded state
                xObj->changeState( embed::EmbedStates::LOADED );
            }
            else
                // objects without persistence need to stay in running state if they shall not be closed
                xObj->changeState( embed::EmbedStates::RUNNING );
        }
        catch ( uno::Exception& )
        {
            return sal_False;
        }
    }

    sal_Bool bFound = sal_False;
    EmbeddedObjectContainerNameMap::iterator aIt = pImpl->maObjectContainer.begin();
    while ( aIt != pImpl->maObjectContainer.end() )
    {
        if ( (*aIt).second == xObj )
        {
            pImpl->maObjectContainer.erase( aIt );
            bFound = sal_True;
            uno::Reference < container::XChild > xChild( xObj, uno::UNO_QUERY );
            if ( xChild.is() )
                xChild->setParent( uno::Reference < uno::XInterface >() );
            break;
        }

        aIt++;
    }

    OSL_ENSURE( bFound, "Object not found for removal!" );
    (void)bFound;
    if ( xPersist.is() )
    {
        // remove replacement image (if there is one)
        RemoveGraphicStream( aName );

        // now it's time to remove the storage from the container storage
        try
        {
#if OSL_DEBUG_LEVEL > 1
            // if the object has a persistance and the object is not a link than it must have persistence entry in storage
            OSL_ENSURE( bIsNotEmbedded || pImpl->mxStorage->hasByName( aName ), "The object has no persistence entry in the storage!" );
#endif
            if ( xPersist.is() && pImpl->mxStorage->hasByName( aName ) )
                pImpl->mxStorage->removeElement( aName );
        }
        catch ( uno::Exception& )
        {
            OSL_FAIL( "Failed to remove object from storage!" );
            return sal_False;
        }
    }

    return sal_True;
}

sal_Bool EmbeddedObjectContainer::CloseEmbeddedObject( const uno::Reference < embed::XEmbeddedObject >& xObj )
{
    RTL_LOGFILE_CONTEXT( aLog, "comphelper (mv76033) comphelper::EmbeddedObjectContainer::CloseEmbeddedObject" );

    // disconnect the object from the container and close it if possible

    sal_Bool bFound = sal_False;
    EmbeddedObjectContainerNameMap::iterator aIt = pImpl->maObjectContainer.begin();
    while ( aIt != pImpl->maObjectContainer.end() )
    {
        if ( (*aIt).second == xObj )
        {
            pImpl->maObjectContainer.erase( aIt );
            bFound = sal_True;
            break;
        }

        aIt++;
    }

    if ( bFound )
    {
        uno::Reference < ::util::XCloseable > xClose( xObj, uno::UNO_QUERY );
        try
        {
            xClose->close( sal_True );
        }
        catch ( uno::Exception& )
        {
            // it is no problem if the object is already closed
            // TODO/LATER: what if the object can not be closed?
        }
    }

    return bFound;
}

uno::Reference < io::XInputStream > EmbeddedObjectContainer::GetGraphicStream( const ::rtl::OUString& aName, rtl::OUString* pMediaType )
{
    RTL_LOGFILE_CONTEXT( aLog, "comphelper (mv76033) comphelper::EmbeddedObjectContainer::GetGraphicStream( Name )" );

    uno::Reference < io::XInputStream > xStream;

    OSL_ENSURE( aName.getLength(), "Retrieving graphic for unknown object!" );
    if ( aName.getLength() )
    {
        try
        {
            uno::Reference < embed::XStorage > xReplacements = pImpl->GetReplacements();
            uno::Reference < io::XStream > xGraphicStream = xReplacements->openStreamElement( aName, embed::ElementModes::READ );
            xStream = xGraphicStream->getInputStream();
            if ( pMediaType )
            {
                uno::Reference < beans::XPropertySet > xSet( xStream, uno::UNO_QUERY );
                if ( xSet.is() )
                {
                    uno::Any aAny = xSet->getPropertyValue( ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("MediaType")) );
                    aAny >>= *pMediaType;
                }
            }
        }
        catch ( uno::Exception& )
        {
        }
    }

    return xStream;
}

uno::Reference < io::XInputStream > EmbeddedObjectContainer::GetGraphicStream( const ::com::sun::star::uno::Reference < ::com::sun::star::embed::XEmbeddedObject >& xObj, rtl::OUString* pMediaType )
{
    RTL_LOGFILE_CONTEXT( aLog, "comphelper (mv76033) comphelper::EmbeddedObjectContainer::GetGraphicStream( Object )" );

    // get the object name
    ::rtl::OUString aName;
    EmbeddedObjectContainerNameMap::iterator aIt = pImpl->maObjectContainer.begin();
    while ( aIt != pImpl->maObjectContainer.end() )
    {
        if ( (*aIt).second == xObj )
        {
            aName = (*aIt).first;
            break;
        }

        aIt++;
    }

    // try to load it from the container storage
    return GetGraphicStream( aName, pMediaType );
}

sal_Bool EmbeddedObjectContainer::InsertGraphicStream( const com::sun::star::uno::Reference < com::sun::star::io::XInputStream >& rStream, const ::rtl::OUString& rObjectName, const rtl::OUString& rMediaType )
{
    RTL_LOGFILE_CONTEXT( aLog, "comphelper (mv76033) comphelper::EmbeddedObjectContainer::InsertGraphicStream" );

    try
    {
        uno::Reference < embed::XStorage > xReplacements = pImpl->GetReplacements();

        // store it into the subfolder
        uno::Reference < io::XOutputStream > xOutStream;
        uno::Reference < io::XStream > xGraphicStream = xReplacements->openStreamElement( rObjectName,
                embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE );
        xOutStream = xGraphicStream->getOutputStream();
        ::comphelper::OStorageHelper::CopyInputToOutput( rStream, xOutStream );
        xOutStream->flush();

        uno::Reference< beans::XPropertySet > xPropSet( xGraphicStream, uno::UNO_QUERY );
        if ( !xPropSet.is() )
            throw uno::RuntimeException();

        xPropSet->setPropertyValue( ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("UseCommonStoragePasswordEncryption")),
                                    uno::makeAny( (sal_Bool)sal_True ) );
        uno::Any aAny;
        aAny <<= rMediaType;
        xPropSet->setPropertyValue( ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("MediaType")), aAny );

        xPropSet->setPropertyValue( ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Compressed")),
                                    uno::makeAny( (sal_Bool)sal_True ) );
    }
    catch( uno::Exception& )
    {
        return sal_False;
    }

    return sal_True;
}

sal_Bool EmbeddedObjectContainer::InsertGraphicStreamDirectly( const com::sun::star::uno::Reference < com::sun::star::io::XInputStream >& rStream, const ::rtl::OUString& rObjectName, const rtl::OUString& rMediaType )
{
    RTL_LOGFILE_CONTEXT( aLog, "comphelper (mv76033) comphelper::EmbeddedObjectContainer::InsertGraphicStreamDirectly" );

    try
    {
        uno::Reference < embed::XStorage > xReplacement = pImpl->GetReplacements();
        uno::Reference < embed::XOptimizedStorage > xOptRepl( xReplacement, uno::UNO_QUERY_THROW );

        // store it into the subfolder
        uno::Sequence< beans::PropertyValue > aProps( 3 );
        aProps[0].Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "MediaType" ) );
        aProps[0].Value <<= rMediaType;
        aProps[1].Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "UseCommonStoragePasswordEncryption" ) );
        aProps[1].Value <<= (sal_Bool)sal_True;
        aProps[2].Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Compressed" ) );
        aProps[2].Value <<= (sal_Bool)sal_True;

        if ( xReplacement->hasByName( rObjectName ) )
            xReplacement->removeElement( rObjectName );

        xOptRepl->insertStreamElementDirect( rObjectName, rStream, aProps );
    }
    catch( uno::Exception& )
    {
        return sal_False;
    }

    return sal_True;
}


sal_Bool EmbeddedObjectContainer::RemoveGraphicStream( const ::rtl::OUString& rObjectName )
{
    RTL_LOGFILE_CONTEXT( aLog, "comphelper (mv76033) comphelper::EmbeddedObjectContainer::RemoveGraphicStream" );

    try
    {
        uno::Reference < embed::XStorage > xReplacements = pImpl->GetReplacements();
        xReplacements->removeElement( rObjectName );
    }
    catch( uno::Exception& )
    {
        return sal_False;
    }

    return sal_True;
}
namespace {
    void InsertStreamIntoPicturesStorage_Impl( const uno::Reference< embed::XStorage >& xDocStor,
                                            const uno::Reference< io::XInputStream >& xInStream,
                                            const ::rtl::OUString& aStreamName )
    {
        OSL_ENSURE( aStreamName.getLength() && xInStream.is() && xDocStor.is(), "Misuse of the method!\n" );

        try
        {
            uno::Reference< embed::XStorage > xPictures = xDocStor->openStorageElement(
                                        ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Pictures" ) ),
                                        embed::ElementModes::READWRITE );
            uno::Reference< io::XStream > xObjReplStr = xPictures->openStreamElement(
                                        aStreamName,
                                        embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE );
            uno::Reference< io::XOutputStream > xOutStream(
                                        xObjReplStr->getInputStream(), uno::UNO_QUERY_THROW );

            ::comphelper::OStorageHelper::CopyInputToOutput( xInStream, xOutStream );
            xOutStream->closeOutput();

            uno::Reference< embed::XTransactedObject > xTransact( xPictures, uno::UNO_QUERY );
            if ( xTransact.is() )
                xTransact->commit();
        }
        catch( uno::Exception& )
        {
            OSL_FAIL( "The pictures storage is not available!\n" );
        }
    }

}
// -----------------------------------------------------------------------------
sal_Bool EmbeddedObjectContainer::StoreAsChildren(sal_Bool _bOasisFormat,sal_Bool _bCreateEmbedded,const uno::Reference < embed::XStorage >& _xStorage)
{
    sal_Bool bResult = sal_False;
    try
    {
        comphelper::EmbeddedObjectContainer aCnt( _xStorage );
        const uno::Sequence < ::rtl::OUString > aNames = GetObjectNames();
        const ::rtl::OUString* pIter = aNames.getConstArray();
        const ::rtl::OUString* pEnd	  = pIter + aNames.getLength();
        for(;pIter != pEnd;++pIter)
        {
            uno::Reference < embed::XEmbeddedObject > xObj = GetEmbeddedObject( *pIter );
            OSL_ENSURE( xObj.is(), "An empty entry in the embedded objects list!\n" );
            if ( xObj.is() )
            {
                sal_Bool bSwitchBackToLoaded = sal_False;
                uno::Reference< embed::XLinkageSupport > xLink( xObj, uno::UNO_QUERY );

                uno::Reference < io::XInputStream > xStream;
                ::rtl::OUString aMediaType;

                sal_Int32 nCurState = xObj->getCurrentState();
                if ( nCurState == embed::EmbedStates::LOADED || nCurState == embed::EmbedStates::RUNNING )
                {
                    // means that the object is not active
                    // copy replacement image from old to new container
                    xStream = GetGraphicStream( xObj, &aMediaType );
                }

                if ( !xStream.is() )
                {
                    // the image must be regenerated
                    // TODO/LATER: another aspect could be used
                    if ( xObj->getCurrentState() == embed::EmbedStates::LOADED )
                            bSwitchBackToLoaded = sal_True;

                    xStream = GetGraphicReplacementStream(
                                                            embed::Aspects::MSOLE_CONTENT,
                                                            xObj,
                                                            &aMediaType );
                }

                if ( _bOasisFormat || (xLink.is() && xLink->isLink()) )
                {
                    if ( xStream.is() )
                    {
                        if ( _bOasisFormat )
                        {
                            // if it is an embedded object or the optimized inserting fails the normal inserting should be done
                            if ( _bCreateEmbedded
                                || !aCnt.InsertGraphicStreamDirectly( xStream, *pIter, aMediaType ) )
                                aCnt.InsertGraphicStream( xStream, *pIter, aMediaType );
                        }
                        else
                        {
                            // it is a linked object exported into SO7 format
                            InsertStreamIntoPicturesStorage_Impl( _xStorage, xStream, *pIter );
                        }
                    }
                }

                uno::Reference< embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
                if ( xPersist.is() )
                {
                    uno::Sequence< beans::PropertyValue > aArgs( _bOasisFormat ? 2 : 3 );
                    aArgs[0].Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "StoreVisualReplacement" ) );
                    aArgs[0].Value <<= (sal_Bool)( !_bOasisFormat );

                    // if it is an embedded object or the optimized inserting fails the normal inserting should be done
                    aArgs[1].Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "CanTryOptimization" ) );
                    aArgs[1].Value <<= !_bCreateEmbedded;
                    if ( !_bOasisFormat )
                    {
                        // if object has no cached replacement it will use this one
                        aArgs[2].Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "VisualReplacement" ) );
                        aArgs[2].Value <<= xStream;
                    }

                    xPersist->storeAsEntry( _xStorage,
                                            xPersist->getEntryName(),
                                            uno::Sequence< beans::PropertyValue >(),
                                            aArgs );
                }

                if ( bSwitchBackToLoaded )
                    // switch back to loaded state; that way we have a minimum cache confusion
                    xObj->changeState( embed::EmbedStates::LOADED );
            }
        }

        bResult = aCnt.CommitImageSubStorage();

    }
    catch ( uno::Exception& )
    {
        // TODO/LATER: error handling
        bResult = sal_False;
    }

    // the old SO6 format does not store graphical replacements
    if ( !_bOasisFormat && bResult )
    {
        try
        {
            // the substorage still can not be locked by the embedded object conteiner
            ::rtl::OUString aObjReplElement( RTL_CONSTASCII_USTRINGPARAM( "ObjectReplacements" ) );
            if ( _xStorage->hasByName( aObjReplElement ) && _xStorage->isStorageElement( aObjReplElement ) )
                _xStorage->removeElement( aObjReplElement );
        }
        catch ( uno::Exception& )
        {
            // TODO/LATER: error handling;
            bResult = sal_False;
        }
    }
    return bResult;
}
// -----------------------------------------------------------------------------
sal_Bool EmbeddedObjectContainer::StoreChildren(sal_Bool _bOasisFormat,sal_Bool _bObjectsOnly)
{
    sal_Bool bResult = sal_True;
    const uno::Sequence < ::rtl::OUString > aNames = GetObjectNames();
    const ::rtl::OUString* pIter = aNames.getConstArray();
    const ::rtl::OUString* pEnd	  = pIter + aNames.getLength();
    for(;pIter != pEnd;++pIter)
    {
        uno::Reference < embed::XEmbeddedObject > xObj = GetEmbeddedObject( *pIter );
        OSL_ENSURE( xObj.is(), "An empty entry in the embedded objects list!\n" );
        if ( xObj.is() )
        {
            sal_Int32 nCurState = xObj->getCurrentState();
            if ( _bOasisFormat && nCurState != embed::EmbedStates::LOADED && nCurState != embed::EmbedStates::RUNNING )
            {
                // means that the object is active
                // the image must be regenerated
                ::rtl::OUString aMediaType;

                // TODO/LATER: another aspect could be used
                uno::Reference < io::XInputStream > xStream =
                            GetGraphicReplacementStream(
                                                        embed::Aspects::MSOLE_CONTENT,
                                                        xObj,
                                                        &aMediaType );
                if ( xStream.is() )
                {
                    if ( !InsertGraphicStreamDirectly( xStream, *pIter, aMediaType ) )
                        InsertGraphicStream( xStream, *pIter, aMediaType );
                }
            }

            // TODO/LATER: currently the object by default does not cache replacement image
            // that means that if somebody loads SO7 document and store its objects using
            // this method the images might be lost.
            // Currently this method is only used on storing to alien formats, that means
            // that SO7 documents storing does not use it, and all other filters are
            // based on OASIS format. But if it changes the method must be fixed. The fix
            // must be done only on demand since it can affect performance.

            uno::Reference< embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
            if ( xPersist.is() )
            {
                try
                {
                    //TODO/LATER: only storing if changed!
                    xPersist->storeOwn();
                }
                catch( uno::Exception& )
                {
                    // TODO/LATER: error handling
                    bResult = sal_False;
                    break;
                }
            }

            if ( !_bOasisFormat && !_bObjectsOnly )
            {
                // copy replacement images for linked objects
                try
                {
                    uno::Reference< embed::XLinkageSupport > xLink( xObj, uno::UNO_QUERY );
                    if ( xLink.is() && xLink->isLink() )
                    {
                        ::rtl::OUString aMediaType;
                        uno::Reference < io::XInputStream > xInStream = GetGraphicStream( xObj, &aMediaType );
                        if ( xInStream.is() )
                            InsertStreamIntoPicturesStorage_Impl( pImpl->mxStorage, xInStream, *pIter );
                    }
                }
                catch( uno::Exception& )
                {
                }
            }
        }
    }

    if ( bResult && _bOasisFormat )
        bResult = CommitImageSubStorage();

    if ( bResult && !_bObjectsOnly )
    {
        try
        {
            ReleaseImageSubStorage();
            ::rtl::OUString aObjReplElement( RTL_CONSTASCII_USTRINGPARAM( "ObjectReplacements" ) );
            if ( !_bOasisFormat && pImpl->mxStorage->hasByName( aObjReplElement ) && pImpl->mxStorage->isStorageElement( aObjReplElement ) )
                pImpl->mxStorage->removeElement( aObjReplElement );
        }
        catch( uno::Exception& )
        {
            // TODO/LATER: error handling
            bResult = sal_False;
        }
    }
    return bResult;
}
// -----------------------------------------------------------------------------
uno::Reference< io::XInputStream > EmbeddedObjectContainer::GetGraphicReplacementStream(
                                                                sal_Int64 nViewAspect,
                                                                const uno::Reference< embed::XEmbeddedObject >& xObj,
                                                                ::rtl::OUString* pMediaType )
{
    uno::Reference< io::XInputStream > xInStream;
    if ( xObj.is() )
    {
        try
        {
            // retrieving of the visual representation can switch object to running state
            embed::VisualRepresentation aRep = xObj->getPreferredVisualRepresentation( nViewAspect );
            if ( pMediaType )
                *pMediaType = aRep.Flavor.MimeType;

            uno::Sequence < sal_Int8 > aSeq;
            aRep.Data >>= aSeq;
            xInStream = new ::comphelper::SequenceInputStream( aSeq );
        }
        catch ( uno::Exception& )
        {
        }
    }

    return xInStream;
}
// -----------------------------------------------------------------------------
sal_Bool EmbeddedObjectContainer::SetPersistentEntries(const uno::Reference< embed::XStorage >& _xStorage,bool _bClearModifedFlag)
{
    sal_Bool bError = sal_False;
    const uno::Sequence < ::rtl::OUString > aNames = GetObjectNames();
    const ::rtl::OUString* pIter = aNames.getConstArray();
    const ::rtl::OUString* pEnd	  = pIter + aNames.getLength();
    for(;pIter != pEnd;++pIter)
    {
        uno::Reference < embed::XEmbeddedObject > xObj = GetEmbeddedObject( *pIter );
        OSL_ENSURE( xObj.is(), "An empty entry in the embedded objects list!\n" );
        if ( xObj.is() )
        {
            uno::Reference< embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
            if ( xPersist.is() )
            {
                try
                {
                    xPersist->setPersistentEntry( _xStorage,
                                                *pIter,
                                                embed::EntryInitModes::NO_INIT,
                                                uno::Sequence< beans::PropertyValue >(),
                                                uno::Sequence< beans::PropertyValue >() );

                }
                catch( uno::Exception& )
                {
                    // TODO/LATER: error handling
                    bError = sal_True;
                    break;
                }
            }
            if ( _bClearModifedFlag )
            {
                // if this method is used as part of SaveCompleted the object must stay unmodified after execution
                try
                {
                    uno::Reference< util::XModifiable > xModif( xObj->getComponent(), uno::UNO_QUERY_THROW );
                    if ( xModif->isModified() )
                        xModif->setModified( sal_False );
                }
                catch( uno::Exception& )
                {
                }
            }
        }
    }
    return bError;
}
}

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