Files
libreoffice/comphelper/source/container/embeddedobjectcontainer.cxx
Stephan Bergmann 33ecd0d5c4 Change OUStringLiteral from char[] to char16_t[]
This is a prerequisite for making conversion from OUStringLiteral to OUString
more efficient at least for C++20 (by replacing its internals with a constexpr-
generated sal_uString-compatible layout with a SAL_STRING_STATIC_FLAG refCount,
conditionally for C++20 for now).

For a configure-wise bare-bones build on Linux, size reported by `du -bs
instdir` grew by 118792 bytes from 1155636636 to 1155755428.

In most places just a u"..." string literal prefix had to be added.  In some
places

  char const a[] = "...";

variables have been changed to char16_t, and a few places required even further
changes to code (which prompted the addition of include/o3tl/string_view.hxx
helper function o3tl::equalsIgnoreAsciiCase and the additional
OUString::createFromAscii overload).

For all uses of macros expanding to string literals, the relevant uses have been
rewritten as

  u"" MACRO

instead of changing the macro definitions.  It should be possible to change at
least some of those macro definitions (and drop the u"" from their call sites)
in follow-up commits.

Change-Id: Iec4ef1a057d412d22443312d40c6a8a290dc6144
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/101483
Tested-by: Jenkins
Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
2020-08-28 08:07:09 +02:00

1498 lines
58 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <com/sun/star/container/XChild.hpp>
#include <com/sun/star/container/XNameAccess.hpp>
#include <com/sun/star/embed/EmbeddedObjectCreator.hpp>
#include <com/sun/star/embed/WrongStateException.hpp>
#include <com/sun/star/embed/XEmbeddedObject.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/io/IOException.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/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 <comphelper/propertysequence.hxx>
#include <cppuhelper/weakref.hxx>
#include <sal/log.hxx>
#include <algorithm>
#include <unordered_map>
using namespace ::com::sun::star;
namespace comphelper {
typedef std::unordered_map<OUString, uno::Reference<embed::XEmbeddedObject>> EmbeddedObjectContainerNameMap;
struct EmbedImpl
{
// TODO/LATER: remove objects from temp. Container storage when object is disposed
EmbeddedObjectContainerNameMap maNameToObjectMap;
// to speed up lookup by Reference
std::unordered_map<uno::Reference<embed::XEmbeddedObject>, OUString> maObjectToNameMap;
uno::Reference < embed::XStorage > mxStorage;
EmbeddedObjectContainer* mpTempObjectContainer;
uno::Reference < embed::XStorage > mxImageStorage;
uno::WeakReference < uno::XInterface > m_xModel;
bool mbOwnsStorage : 1;
bool mbUserAllowsLinkUpdate : 1;
const uno::Reference < embed::XStorage >& GetReplacements();
};
const uno::Reference < embed::XStorage >& EmbedImpl::GetReplacements()
{
if ( !mxImageStorage.is() )
{
try
{
mxImageStorage = mxStorage->openStorageElement(
"ObjectReplacements", embed::ElementModes::READWRITE );
}
catch (const uno::Exception&)
{
mxImageStorage = mxStorage->openStorageElement(
"ObjectReplacements", embed::ElementModes::READ );
}
}
if ( !mxImageStorage.is() )
throw io::IOException();
return mxImageStorage;
}
EmbeddedObjectContainer::EmbeddedObjectContainer()
: pImpl(new EmbedImpl)
{
pImpl->mxStorage = ::comphelper::OStorageHelper::GetTemporaryStorage();
pImpl->mbOwnsStorage = true;
pImpl->mbUserAllowsLinkUpdate = true;
pImpl->mpTempObjectContainer = nullptr;
}
EmbeddedObjectContainer::EmbeddedObjectContainer( const uno::Reference < embed::XStorage >& rStor )
: pImpl(new EmbedImpl)
{
pImpl->mxStorage = rStor;
pImpl->mbOwnsStorage = false;
pImpl->mbUserAllowsLinkUpdate = true;
pImpl->mpTempObjectContainer = nullptr;
}
EmbeddedObjectContainer::EmbeddedObjectContainer( const uno::Reference < embed::XStorage >& rStor, const uno::Reference < uno::XInterface >& xModel )
: pImpl(new EmbedImpl)
{
pImpl->mxStorage = rStor;
pImpl->mbOwnsStorage = false;
pImpl->mbUserAllowsLinkUpdate = true;
pImpl->mpTempObjectContainer = nullptr;
pImpl->m_xModel = xModel;
}
void EmbeddedObjectContainer::SwitchPersistence( const uno::Reference < embed::XStorage >& rStor )
{
ReleaseImageSubStorage();
if ( pImpl->mbOwnsStorage )
pImpl->mxStorage->dispose();
pImpl->mxStorage = rStor;
pImpl->mbOwnsStorage = false;
}
bool EmbeddedObjectContainer::CommitImageSubStorage()
{
if ( pImpl->mxImageStorage.is() )
{
try
{
bool bReadOnlyMode = 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("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 (const uno::Exception&)
{
return false;
}
}
return true;
}
void EmbeddedObjectContainer::ReleaseImageSubStorage()
{
CommitImageSubStorage();
if ( pImpl->mxImageStorage.is() )
{
try
{
pImpl->mxImageStorage->dispose();
pImpl->mxImageStorage.clear();
}
catch (const uno::Exception&)
{
SAL_WARN( "comphelper.container", "Problems releasing image substorage!" );
}
}
}
EmbeddedObjectContainer::~EmbeddedObjectContainer()
{
ReleaseImageSubStorage();
if ( pImpl->mbOwnsStorage )
pImpl->mxStorage->dispose();
delete pImpl->mpTempObjectContainer;
}
void EmbeddedObjectContainer::CloseEmbeddedObjects()
{
for( const auto& rObj : pImpl->maNameToObjectMap )
{
uno::Reference < util::XCloseable > const & xClose = rObj.second;
if( xClose.is() )
{
try
{
xClose->close( true );
}
catch (const uno::Exception&)
{
}
}
}
}
OUString EmbeddedObjectContainer::CreateUniqueObjectName()
{
OUString aStr;
sal_Int32 i=1;
do
{
aStr = "Object " + OUString::number( i++ );
}
while( HasEmbeddedObject( aStr ) );
// TODO/LATER: should we consider deleted objects?
return aStr;
}
uno::Sequence < OUString > EmbeddedObjectContainer::GetObjectNames() const
{
return comphelper::mapKeysToSequence(pImpl->maNameToObjectMap);
}
bool EmbeddedObjectContainer::HasEmbeddedObjects() const
{
return !pImpl->maNameToObjectMap.empty();
}
bool EmbeddedObjectContainer::HasEmbeddedObject( const OUString& rName )
{
auto aIt = pImpl->maNameToObjectMap.find( rName );
if (aIt != pImpl->maNameToObjectMap.end())
return true;
if (!pImpl->mxStorage.is())
return false;
return pImpl->mxStorage->hasByName(rName);
}
bool EmbeddedObjectContainer::HasEmbeddedObject( const uno::Reference < embed::XEmbeddedObject >& xObj ) const
{
return pImpl->maObjectToNameMap.find(xObj) != pImpl->maObjectToNameMap.end();
}
bool EmbeddedObjectContainer::HasInstantiatedEmbeddedObject( const 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
auto aIt = pImpl->maNameToObjectMap.find( rName );
return ( aIt != pImpl->maNameToObjectMap.end() );
}
OUString EmbeddedObjectContainer::GetEmbeddedObjectName( const css::uno::Reference < css::embed::XEmbeddedObject >& xObj ) const
{
auto it = pImpl->maObjectToNameMap.find(xObj);
if (it == pImpl->maObjectToNameMap.end())
{
SAL_WARN( "comphelper.container", "Unknown object!" );
return OUString();
}
return it->second;
}
uno::Reference< embed::XEmbeddedObject>
EmbeddedObjectContainer::GetEmbeddedObject(
const OUString& rName, OUString const*const pBaseURL)
{
SAL_WARN_IF( rName.isEmpty(), "comphelper.container", "Empty object name!");
uno::Reference < embed::XEmbeddedObject > xObj;
auto aIt = pImpl->maNameToObjectMap.find( rName );
#if OSL_DEBUG_LEVEL > 1
uno::Reference < container::XNameAccess > xAccess( pImpl->mxStorage, uno::UNO_QUERY );
uno::Sequence< OUString> aSeq = xAccess->getElementNames();
const OUString* pIter = aSeq.getConstArray();
const OUString* pEnd = pIter + aSeq.getLength();
for(;pIter != pEnd;++pIter)
{
(void)*pIter;
}
OSL_ENSURE( aIt != pImpl->maNameToObjectMap.end() || xAccess->hasByName(rName), "Could not return object!" );
#endif
// check if object was already created
if ( aIt != pImpl->maNameToObjectMap.end() )
xObj = (*aIt).second;
else
xObj = Get_Impl(rName, uno::Reference<embed::XEmbeddedObject>(), pBaseURL);
return xObj;
}
uno::Reference<embed::XEmbeddedObject> EmbeddedObjectContainer::Get_Impl(
const OUString& rName,
const uno::Reference<embed::XEmbeddedObject>& xCopy,
OUString const*const pBaseURL)
{
uno::Reference < embed::XEmbeddedObject > xObj;
try
{
// create the object from the storage
uno::Reference < beans::XPropertySet > xSet( pImpl->mxStorage, uno::UNO_QUERY );
bool bReadOnlyMode = true;
if ( xSet.is() )
{
// get the open mode from the parent storage
sal_Int32 nMode = 0;
uno::Any aAny = xSet->getPropertyValue("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::XEmbeddedObjectCreator > xFactory = embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() );
uno::Sequence< beans::PropertyValue > aObjDescr(1 + (xCopy.is() ? 1 : 0) + (pBaseURL ? 1 : 0));
aObjDescr[0].Name = "Parent";
aObjDescr[0].Value <<= pImpl->m_xModel.get();
sal_Int32 i = 1;
if (pBaseURL)
{
aObjDescr[i].Name = "DefaultParentBaseURL";
aObjDescr[i].Value <<= *pBaseURL;
++i;
}
if ( xCopy.is() )
{
aObjDescr[i].Name = "CloneFrom";
aObjDescr[i].Value <<= xCopy;
}
uno::Sequence< beans::PropertyValue > aMediaDescr( 1 );
aMediaDescr[0].Name = "ReadOnly";
aMediaDescr[0].Value <<= bReadOnlyMode;
xObj.set( xFactory->createInstanceInitFromEntry(
pImpl->mxStorage, rName,
aMediaDescr, aObjDescr ), uno::UNO_QUERY );
// insert object into my list
AddEmbeddedObject( xObj, rName );
}
catch (uno::Exception const& e)
{
SAL_WARN("comphelper.container", "EmbeddedObjectContainer::Get_Impl: exception caught: " << e);
}
return xObj;
}
uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::CreateEmbeddedObject( const uno::Sequence < sal_Int8 >& rClassId,
const uno::Sequence < beans::PropertyValue >& rArgs, OUString& rNewName, OUString const* pBaseURL )
{
if ( rNewName.isEmpty() )
rNewName = CreateUniqueObjectName();
SAL_WARN_IF( HasEmbeddedObject(rNewName), "comphelper.container", "Object to create already exists!");
// create object from classid by inserting it into storage
uno::Reference < embed::XEmbeddedObject > xObj;
try
{
uno::Reference < embed::XEmbeddedObjectCreator > xFactory = embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() );
const size_t nExtraArgs = pBaseURL ? 2 : 1;
uno::Sequence< beans::PropertyValue > aObjDescr( rArgs.getLength() + nExtraArgs );
aObjDescr[0].Name = "Parent";
aObjDescr[0].Value <<= pImpl->m_xModel.get();
if (pBaseURL)
{
aObjDescr[1].Name = "DefaultParentBaseURL";
aObjDescr[1].Value <<= *pBaseURL;
}
std::copy( rArgs.begin(), rArgs.end(), aObjDescr.getArray() + nExtraArgs );
xObj.set( xFactory->createInstanceInitNew(
rClassId, 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!" );
}
catch (uno::Exception const& e)
{
SAL_WARN("comphelper.container", "EmbeddedObjectContainer::CreateEmbeddedObject: exception caught: " << e);
}
return xObj;
}
uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::CreateEmbeddedObject( const uno::Sequence < sal_Int8 >& rClassId, OUString& rNewName, OUString const* pBaseURL )
{
return CreateEmbeddedObject( rClassId, uno::Sequence < beans::PropertyValue >(), rNewName, pBaseURL );
}
void EmbeddedObjectContainer::AddEmbeddedObject( const css::uno::Reference < css::embed::XEmbeddedObject >& xObj, const OUString& rName )
{
#if OSL_DEBUG_LEVEL > 1
SAL_WARN_IF( rName.isEmpty(), "comphelper.container", "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 persistence 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
auto aIt = pImpl->maNameToObjectMap.find( rName );
OSL_ENSURE( aIt == pImpl->maNameToObjectMap.end(), "Element already inserted!" );
pImpl->maNameToObjectMap[ rName ] = xObj;
pImpl->maObjectToNameMap[ xObj ] = rName;
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 temporary container
if ( !pImpl->mpTempObjectContainer )
return;
auto& rObjectContainer = pImpl->mpTempObjectContainer->pImpl->maNameToObjectMap;
auto aIter = std::find_if(rObjectContainer.begin(), rObjectContainer.end(),
[&xObj](const EmbeddedObjectContainerNameMap::value_type& rEntry) { return rEntry.second == xObj; });
if (aIter == rObjectContainer.end())
return;
// copy replacement image from temporary container (if there is any)
OUString aTempName = aIter->first;
OUString aMediaType;
uno::Reference < io::XInputStream > xStream = pImpl->mpTempObjectContainer->GetGraphicStream( xObj, &aMediaType );
if ( xStream.is() )
{
InsertGraphicStream( xStream, rName, aMediaType );
xStream = nullptr;
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 (const uno::Exception&)
{
}
}
// temp. container needs to forget the object
pImpl->mpTempObjectContainer->pImpl->maObjectToNameMap.erase( aIter->second );
pImpl->mpTempObjectContainer->pImpl->maNameToObjectMap.erase( aIter );
}
bool EmbeddedObjectContainer::StoreEmbeddedObject(
const uno::Reference < embed::XEmbeddedObject >& xObj, OUString& rName, bool bCopy,
const OUString& rSrcShellID, const OUString& rDestShellID )
{
uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
if ( rName.isEmpty() )
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 )
{
auto aObjArgs(::comphelper::InitPropertySequence({
{ "SourceShellID", uno::Any(rSrcShellID) },
{ "DestinationShellID", uno::Any(rDestShellID) }
}));
xPersist->storeToEntry(pImpl->mxStorage, rName, aSeq, aObjArgs);
}
else
{
//TODO/LATER: possible optimization, 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( true );
}
}
}
catch (uno::Exception const& e)
{
SAL_WARN("comphelper.container", "EmbeddedObjectContainer::StoreEmbeddedObject: exception caught: " << e);
// TODO/LATER: better error recovery should keep storage intact
return false;
}
return true;
}
bool EmbeddedObjectContainer::InsertEmbeddedObject( const uno::Reference < embed::XEmbeddedObject >& xObj, OUString& rName )
{
// store it into the container storage
if (StoreEmbeddedObject(xObj, rName, false, OUString(), OUString()))
{
// remember object
AddEmbeddedObject( xObj, rName );
return true;
}
else
return false;
}
uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::InsertEmbeddedObject( const uno::Reference < io::XInputStream >& xStm, OUString& rNewName )
{
if ( rNewName.isEmpty() )
rNewName = CreateUniqueObjectName();
// store it into the container storage
bool bIsStorage = false;
try
{
// first try storage persistence
uno::Reference < embed::XStorage > xStore = ::comphelper::OStorageHelper::GetStorageFromInputStream( xStm );
// storage was created from stream successfully
bIsStorage = true;
uno::Reference < embed::XStorage > xNewStore = pImpl->mxStorage->openStorageElement( rNewName, embed::ElementModes::READWRITE );
xStore->copyToStorage( xNewStore );
}
catch (const 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("MediaType",
uno::Any( OUString( "application/vnd.sun.star.oleobject" ) ) );
}
catch (uno::Exception const& e)
{
// complete disaster!
SAL_WARN("comphelper.container", "EmbeddedObjectContainer::InsertEmbeddedObject: exception caught: " << e);
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 (const uno::Exception&)
{
}
return xRet;
}
uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::InsertEmbeddedObject( const css::uno::Sequence < css::beans::PropertyValue >& aMedium, OUString& rNewName, OUString const* pBaseURL )
{
if ( rNewName.isEmpty() )
rNewName = CreateUniqueObjectName();
uno::Reference < embed::XEmbeddedObject > xObj;
try
{
uno::Reference < embed::XEmbeddedObjectCreator > xFactory = embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() );
uno::Sequence< beans::PropertyValue > aObjDescr(pBaseURL ? 2 : 1);
aObjDescr[0].Name = "Parent";
aObjDescr[0].Value <<= pImpl->m_xModel.get();
if (pBaseURL)
{
aObjDescr[1].Name = "DefaultParentBaseURL";
aObjDescr[1].Value <<= *pBaseURL;
}
xObj.set( 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!" );
// possible optimization: store later!
if ( xPersist.is())
xPersist->storeOwn();
AddEmbeddedObject( xObj, rNewName );
}
catch (const uno::Exception&)
{
}
return xObj;
}
uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::InsertEmbeddedLink( const css::uno::Sequence < css::beans::PropertyValue >& aMedium, OUString& rNewName )
{
if ( rNewName.isEmpty() )
rNewName = CreateUniqueObjectName();
uno::Reference < embed::XEmbeddedObject > xObj;
try
{
uno::Reference < embed::XEmbeddedObjectCreator > xFactory = embed::EmbeddedObjectCreator::create(::comphelper::getProcessComponentContext());
uno::Sequence< beans::PropertyValue > aObjDescr( 1 );
aObjDescr[0].Name = "Parent";
aObjDescr[0].Value <<= pImpl->m_xModel.get();
xObj.set( 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!" );
// possible optimization: store later!
if ( xPersist.is())
xPersist->storeOwn();
AddEmbeddedObject( xObj, rNewName );
}
catch (uno::Exception const& e)
{
SAL_WARN("comphelper.container", "EmbeddedObjectContainer::InsertEmbeddedLink: "
"exception caught: " << e);
}
return xObj;
}
bool EmbeddedObjectContainer::TryToCopyGraphReplacement( EmbeddedObjectContainer& rSrc,
const OUString& aOrigName,
const OUString& aTargetName )
{
bool bResult = false;
if ( ( &rSrc != this || aOrigName != aTargetName ) && !aOrigName.isEmpty() && !aTargetName.isEmpty() )
{
OUString aMediaType;
uno::Reference < io::XInputStream > xGrStream = rSrc.GetGraphicStream( aOrigName, &aMediaType );
if ( xGrStream.is() )
bResult = InsertGraphicStream( xGrStream, aTargetName, aMediaType );
}
return bResult;
}
uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::CopyAndGetEmbeddedObject(
EmbeddedObjectContainer& rSrc, const uno::Reference <embed::XEmbeddedObject>& xObj, OUString& rName,
const OUString& rSrcShellID, const OUString& rDestShellID )
{
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
OUString aOrigName;
try
{
uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY_THROW );
aOrigName = xPersist->getEntryName();
}
catch (const uno::Exception&)
{
}
if ( rName.isEmpty() )
rName = CreateUniqueObjectName();
// objects without persistence are not really stored by the method
if (xObj.is() && StoreEmbeddedObject(xObj, rName, true, rSrcShellID, rDestShellID))
{
SAL_INFO_IF(rDestShellID.isEmpty(), "comphelper.container",
"SfxObjectShell with no base URL?"); // every shell has a base URL, except the clipboard SwDocShell
xResult = Get_Impl(rName, xObj, &rDestShellID);
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 an OOo link, it has no persistence
OUString aURL = xOrigLinkage->getLinkURL();
if ( aURL.isEmpty() )
throw uno::RuntimeException();
// create new linked object from the URL the link is based on
uno::Reference < embed::XEmbeddedObjectCreator > xCreator =
embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() );
uno::Sequence< beans::PropertyValue > aMediaDescr( 1 );
aMediaDescr[0].Name = "URL";
aMediaDescr[0].Value <<= aURL;
uno::Sequence< beans::PropertyValue > aObjDescr( 1 );
aObjDescr[0].Name = "Parent";
aObjDescr[0].Value <<= pImpl->m_xModel.get();
xResult.set(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 transfer all the properties
uno::Reference < embed::XEmbeddedObjectCreator > xCreator =
embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() );
uno::Sequence< beans::PropertyValue > aObjDescr( 1 );
aObjDescr[0].Name = "Parent";
aObjDescr[0].Value <<= pImpl->m_xModel.get();
xResult.set(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();
const uno::Sequence< beans::Property > aPropertiesList = xOrigInfo->getProperties();
for ( const auto & p : aPropertiesList )
{
try
{
xTargetProps->setPropertyValue(
p.Name,
xOrigProps->getPropertyValue( p.Name ) );
}
catch (const 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
SAL_WARN( "comphelper.container", "Could not copy readonly property!" );
}
}
}
if ( xResult.is() )
AddEmbeddedObject( xResult, rName );
}
catch (const uno::Exception&)
{
if ( xResult.is() )
{
try
{
xResult->close( true );
}
catch (const uno::Exception&)
{
}
xResult.clear();
}
}
}
}
SAL_WARN_IF( !xResult.is(), "comphelper.container", "Can not copy embedded object that has no persistence!" );
if ( xResult.is() )
{
// the object is successfully copied, try to copy graphical replacement
if ( !aOrigName.isEmpty() )
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 (const uno::Exception&)
{
}
}
return xResult;
}
// #i119941, bKeepToTempStorage: use to specify whether store the removed object to temporary storage+
void EmbeddedObjectContainer::RemoveEmbeddedObject( const OUString& rName, bool bKeepToTempStorage )
{
uno::Reference < embed::XEmbeddedObject > xObj = GetEmbeddedObject( rName );
if ( xObj.is() )
RemoveEmbeddedObject( xObj, bKeepToTempStorage );
}
bool EmbeddedObjectContainer::MoveEmbeddedObject( const OUString& rName, EmbeddedObjectContainer& rCnt )
{
// find object entry
auto aIt2 = rCnt.pImpl->maNameToObjectMap.find( rName );
OSL_ENSURE( aIt2 == rCnt.pImpl->maNameToObjectMap.end(), "Object does already exist in target container!" );
if ( aIt2 != rCnt.pImpl->maNameToObjectMap.end() )
return false;
uno::Reference < embed::XEmbeddedObject > xObj;
auto aIt = pImpl->maNameToObjectMap.find( rName );
if ( aIt != pImpl->maNameToObjectMap.end() )
{
xObj = (*aIt).second;
try
{
if ( xObj.is() )
{
// move object
OUString aName( rName );
rCnt.InsertEmbeddedObject( xObj, aName );
pImpl->maObjectToNameMap.erase( aIt->second );
pImpl->maNameToObjectMap.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 true;
}
catch (const uno::Exception&)
{
SAL_WARN( "comphelper.container", "Could not move object!");
return false;
}
}
else
SAL_WARN( "comphelper.container", "Unknown object!");
return false;
}
// #i119941, bKeepToTempStorage: use to specify whether store the removed object to temporary storage+
bool EmbeddedObjectContainer::RemoveEmbeddedObject( const uno::Reference < embed::XEmbeddedObject >& xObj, bool bKeepToTempStorage )
{
uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
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 persistence 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
// somebody still needs the object, so we must assign a temporary persistence
try
{
if ( xPersist.is() && bKeepToTempStorage ) // #i119941
{
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
OUString aOrigStorMediaType;
uno::Reference< beans::XPropertySet > xStorProps( pImpl->mxStorage, uno::UNO_QUERY_THROW );
static const OUStringLiteral s_sMediaType(u"MediaType");
xStorProps->getPropertyValue( s_sMediaType ) >>= aOrigStorMediaType;
SAL_WARN_IF( aOrigStorMediaType.isEmpty(), "comphelper.container", "No valuable media type in the storage!" );
uno::Reference< beans::XPropertySet > xTargetStorProps(
pImpl->mpTempObjectContainer->pImpl->mxStorage,
uno::UNO_QUERY_THROW );
xTargetStorProps->setPropertyValue( s_sMediaType,uno::Any( aOrigStorMediaType ) );
}
catch (const uno::Exception&)
{
SAL_WARN( "comphelper.container", "Can not set the new media type to a storage!" );
}
}
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 (const uno::Exception&)
{
return false;
}
auto aIter = std::find_if(pImpl->maNameToObjectMap.begin(), pImpl->maNameToObjectMap.end(),
[&xObj](const EmbeddedObjectContainerNameMap::value_type& rEntry) { return rEntry.second == xObj; });
if (aIter != pImpl->maNameToObjectMap.end())
{
pImpl->maObjectToNameMap.erase( aIter->second );
pImpl->maNameToObjectMap.erase( aIter );
uno::Reference < container::XChild > xChild( xObj, uno::UNO_QUERY );
if ( xChild.is() )
xChild->setParent( uno::Reference < uno::XInterface >() );
}
else
SAL_WARN( "comphelper.container", "Object not found for removal!" );
if ( xPersist.is() && bKeepToTempStorage ) // #i119941#
{
// 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 persistence 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 (const uno::Exception&)
{
SAL_WARN( "comphelper.container", "Failed to remove object from storage!" );
return false;
}
}
return true;
}
void EmbeddedObjectContainer::CloseEmbeddedObject( const uno::Reference < embed::XEmbeddedObject >& xObj )
{
// disconnect the object from the container and close it if possible
auto aIter = std::find_if(pImpl->maNameToObjectMap.begin(), pImpl->maNameToObjectMap.end(),
[&xObj](const EmbeddedObjectContainerNameMap::value_type& rEntry) { return rEntry.second == xObj; });
if (aIter == pImpl->maNameToObjectMap.end())
return;
pImpl->maObjectToNameMap.erase( aIter->second );
pImpl->maNameToObjectMap.erase( aIter );
try
{
xObj->close( true );
}
catch (const uno::Exception&)
{
// it is no problem if the object is already closed
// TODO/LATER: what if the object can not be closed?
}
}
uno::Reference < io::XInputStream > EmbeddedObjectContainer::GetGraphicStream( const OUString& aName, OUString* pMediaType )
{
uno::Reference < io::XInputStream > xStream;
SAL_WARN_IF( aName.isEmpty(), "comphelper.container", "Retrieving graphic for unknown object!" );
if ( !aName.isEmpty() )
{
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("MediaType");
aAny >>= *pMediaType;
}
}
}
catch (uno::Exception const& e)
{
SAL_INFO("comphelper.container",
"EmbeddedObjectContainer::GetGraphicStream(): " << e);
}
}
return xStream;
}
uno::Reference < io::XInputStream > EmbeddedObjectContainer::GetGraphicStream( const css::uno::Reference < css::embed::XEmbeddedObject >& xObj, OUString* pMediaType )
{
// try to load it from the container storage
return GetGraphicStream( GetEmbeddedObjectName( xObj ), pMediaType );
}
bool EmbeddedObjectContainer::InsertGraphicStream( const css::uno::Reference < css::io::XInputStream >& rStream, const OUString& rObjectName, const OUString& rMediaType )
{
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_THROW );
xPropSet->setPropertyValue("UseCommonStoragePasswordEncryption",
uno::Any( true ) );
xPropSet->setPropertyValue("MediaType", uno::Any(rMediaType) );
xPropSet->setPropertyValue("Compressed",
uno::Any( true ) );
}
catch (const uno::Exception&)
{
return false;
}
return true;
}
bool EmbeddedObjectContainer::InsertGraphicStreamDirectly( const css::uno::Reference < css::io::XInputStream >& rStream, const OUString& rObjectName, const OUString& rMediaType )
{
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 = "MediaType";
aProps[0].Value <<= rMediaType;
aProps[1].Name = "UseCommonStoragePasswordEncryption";
aProps[1].Value <<= true;
aProps[2].Name = "Compressed";
aProps[2].Value <<= true;
if ( xReplacement->hasByName( rObjectName ) )
xReplacement->removeElement( rObjectName );
xOptRepl->insertStreamElementDirect( rObjectName, rStream, aProps );
}
catch (const uno::Exception&)
{
return false;
}
return true;
}
void EmbeddedObjectContainer::RemoveGraphicStream( const OUString& rObjectName )
{
try
{
uno::Reference < embed::XStorage > xReplacements = pImpl->GetReplacements();
xReplacements->removeElement( rObjectName );
}
catch (const uno::Exception&)
{
}
}
namespace {
void InsertStreamIntoPicturesStorage_Impl( const uno::Reference< embed::XStorage >& xDocStor,
const uno::Reference< io::XInputStream >& xInStream,
const OUString& aStreamName )
{
OSL_ENSURE( !aStreamName.isEmpty() && xInStream.is() && xDocStor.is(), "Misuse of the method!" );
try
{
uno::Reference< embed::XStorage > xPictures = xDocStor->openStorageElement(
"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 (const uno::Exception&)
{
SAL_WARN( "comphelper.container", "The images storage is not available!" );
}
}
}
bool EmbeddedObjectContainer::StoreAsChildren(bool _bOasisFormat,bool _bCreateEmbedded,const uno::Reference < embed::XStorage >& _xStorage)
{
bool bResult = false;
try
{
comphelper::EmbeddedObjectContainer aCnt( _xStorage );
const uno::Sequence < OUString > aNames = GetObjectNames();
const OUString* pIter = aNames.getConstArray();
const OUString* pEnd = pIter + aNames.getLength();
for(;pIter != pEnd;++pIter)
{
uno::Reference < embed::XEmbeddedObject > xObj = GetEmbeddedObject( *pIter );
SAL_WARN_IF( !xObj.is(), "comphelper.container", "An empty entry in the embedded objects list!" );
if ( xObj.is() )
{
bool bSwitchBackToLoaded = false;
uno::Reference< embed::XLinkageSupport > xLink( xObj, uno::UNO_QUERY );
uno::Reference < io::XInputStream > xStream;
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() && getUserAllowsLinkUpdate() )
{
// the image must be regenerated
// TODO/LATER: another aspect could be used
if ( xObj->getCurrentState() == embed::EmbedStates::LOADED )
bSwitchBackToLoaded = 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 = "StoreVisualReplacement";
aArgs[0].Value <<= !_bOasisFormat;
// if it is an embedded object or the optimized inserting fails the normal inserting should be done
aArgs[1].Name = "CanTryOptimization";
aArgs[1].Value <<= !_bCreateEmbedded;
if ( !_bOasisFormat )
{
// if object has no cached replacement it will use this one
aArgs[2].Name = "VisualReplacement";
aArgs[2].Value <<= xStream;
}
try
{
xPersist->storeAsEntry( _xStorage, xPersist->getEntryName(), uno::Sequence< beans::PropertyValue >(), aArgs );
}
catch (const embed::WrongStateException&)
{
SAL_WARN("comphelper.container", "failed to store '" << *pIter << "'");
}
}
if ( bSwitchBackToLoaded )
// switch back to loaded state; that way we have a minimum cache confusion
xObj->changeState( embed::EmbedStates::LOADED );
}
}
bResult = aCnt.CommitImageSubStorage();
}
catch (const uno::Exception& e)
{
// TODO/LATER: error handling
bResult = false;
SAL_WARN("comphelper.container", "failed. Message: " << e);
}
// the old SO6 format does not store graphical replacements
if ( !_bOasisFormat && bResult )
{
try
{
// the substorage still can not be locked by the embedded object container
OUString aObjReplElement( "ObjectReplacements" );
if ( _xStorage->hasByName( aObjReplElement ) && _xStorage->isStorageElement( aObjReplElement ) )
_xStorage->removeElement( aObjReplElement );
}
catch (const uno::Exception&)
{
// TODO/LATER: error handling;
bResult = false;
}
}
return bResult;
}
bool EmbeddedObjectContainer::StoreChildren(bool _bOasisFormat,bool _bObjectsOnly)
{
bool bResult = true;
const uno::Sequence < OUString > aNames = GetObjectNames();
const OUString* pIter = aNames.getConstArray();
const OUString* pEnd = pIter + aNames.getLength();
for(;pIter != pEnd;++pIter)
{
try
{
uno::Reference < embed::XEmbeddedObject > xObj = GetEmbeddedObject( *pIter );
SAL_WARN_IF( !xObj.is(), "comphelper.container", "An empty entry in the embedded objects list!" );
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
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(); //commented, i120168
// begin:all charts will be persisted as xml format on disk when saving, which is time consuming.
// '_bObjectsOnly' mean we are storing to alien formats.
// 'isStorageElement' mean current object is NOT a MS OLE format. (may also include in future), i120168
if (_bObjectsOnly && (nCurState == embed::EmbedStates::LOADED || nCurState == embed::EmbedStates::RUNNING)
&& (pImpl->mxStorage->isStorageElement( *pIter ) ))
{
uno::Reference< util::XModifiable > xModifiable( xObj->getComponent(), uno::UNO_QUERY );
if ( xModifiable.is() && xModifiable->isModified())
{
xPersist->storeOwn();
}
else
{
//do nothing. Embedded model is not modified, no need to persist.
}
}
else //the embedded object is in active status, always store back it.
{
xPersist->storeOwn();
}
//end i120168
}
catch (const uno::Exception&)
{
// TODO/LATER: error handling
bResult = 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() )
{
OUString aMediaType;
uno::Reference < io::XInputStream > xInStream = GetGraphicStream( xObj, &aMediaType );
if ( xInStream.is() )
InsertStreamIntoPicturesStorage_Impl( pImpl->mxStorage, xInStream, *pIter );
}
}
catch (const uno::Exception&)
{
}
}
}
}
catch (const uno::Exception&)
{
// TODO/LATER: error handling
}
}
if ( bResult && _bOasisFormat )
bResult = CommitImageSubStorage();
if ( bResult && !_bObjectsOnly )
{
try
{
ReleaseImageSubStorage();
OUString aObjReplElement( "ObjectReplacements" );
if ( !_bOasisFormat && pImpl->mxStorage->hasByName( aObjReplElement ) && pImpl->mxStorage->isStorageElement( aObjReplElement ) )
pImpl->mxStorage->removeElement( aObjReplElement );
}
catch (const uno::Exception&)
{
// TODO/LATER: error handling
bResult = false;
}
}
return bResult;
}
uno::Reference< io::XInputStream > EmbeddedObjectContainer::GetGraphicReplacementStream(
sal_Int64 nViewAspect,
const uno::Reference< embed::XEmbeddedObject >& xObj,
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 (const uno::Exception&)
{
}
}
return xInStream;
}
bool EmbeddedObjectContainer::SetPersistentEntries(const uno::Reference< embed::XStorage >& _xStorage,bool _bClearModifedFlag)
{
bool bError = false;
const uno::Sequence < OUString > aNames = GetObjectNames();
const OUString* pIter = aNames.getConstArray();
const OUString* pEnd = pIter + aNames.getLength();
for(;pIter != pEnd;++pIter)
{
uno::Reference < embed::XEmbeddedObject > xObj = GetEmbeddedObject( *pIter );
SAL_WARN_IF( !xObj.is(), "comphelper.container", "An empty entry in the embedded objects list!" );
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 (const uno::Exception&)
{
// TODO/LATER: error handling
bError = 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( false );
}
catch (const uno::Exception&)
{
}
}
}
}
return bError;
}
bool EmbeddedObjectContainer::getUserAllowsLinkUpdate() const
{
return pImpl->mbUserAllowsLinkUpdate;
}
void EmbeddedObjectContainer::setUserAllowsLinkUpdate(bool bNew)
{
if(pImpl->mbUserAllowsLinkUpdate != bNew)
{
pImpl->mbUserAllowsLinkUpdate = bNew;
}
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */