Simplify a bit
[LibreOffice.git] / comphelper / source / container / embeddedobjectcontainer.cxx
blob56b50c9648236d2b364f0429ac265751796b22ca
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <com/sun/star/container/XChild.hpp>
21 #include <com/sun/star/container/XNameAccess.hpp>
22 #include <com/sun/star/embed/EmbeddedObjectCreator.hpp>
23 #include <com/sun/star/embed/WrongStateException.hpp>
24 #include <com/sun/star/embed/XEmbeddedObject.hpp>
25 #include <com/sun/star/embed/XEmbedPersist.hpp>
26 #include <com/sun/star/embed/XLinkageSupport.hpp>
27 #include <com/sun/star/embed/XTransactedObject.hpp>
28 #include <com/sun/star/embed/XOptimizedStorage.hpp>
29 #include <com/sun/star/embed/EntryInitModes.hpp>
30 #include <com/sun/star/io/IOException.hpp>
31 #include <com/sun/star/util/XCloseable.hpp>
32 #include <com/sun/star/util/XModifiable.hpp>
33 #include <com/sun/star/embed/EmbedStates.hpp>
34 #include <com/sun/star/beans/XPropertySetInfo.hpp>
35 #include <com/sun/star/beans/XPropertySet.hpp>
36 #include <com/sun/star/embed/Aspects.hpp>
37 #include <com/sun/star/embed/EmbedMisc.hpp>
39 #include <comphelper/seqstream.hxx>
40 #include <comphelper/processfactory.hxx>
41 #include <comphelper/storagehelper.hxx>
42 #include <comphelper/embeddedobjectcontainer.hxx>
43 #include <comphelper/sequence.hxx>
44 #include <comphelper/propertysequence.hxx>
45 #include <comphelper/propertyvalue.hxx>
46 #include <cppuhelper/weakref.hxx>
47 #include <sal/log.hxx>
49 #include <officecfg/Office/Common.hxx>
51 #include <algorithm>
52 #include <unordered_map>
55 using namespace ::com::sun::star;
57 namespace comphelper {
59 typedef std::unordered_map<OUString, uno::Reference<embed::XEmbeddedObject>> EmbeddedObjectContainerNameMap;
60 struct EmbedImpl
62 // TODO/LATER: remove objects from temp. Container storage when object is disposed
63 EmbeddedObjectContainerNameMap maNameToObjectMap;
64 // to speed up lookup by Reference
65 std::unordered_map<uno::Reference<embed::XEmbeddedObject>, OUString> maObjectToNameMap;
66 uno::Reference < embed::XStorage > mxStorage;
67 EmbeddedObjectContainer* mpTempObjectContainer;
68 uno::Reference < embed::XStorage > mxImageStorage;
69 uno::WeakReference < uno::XInterface > m_xModel;
71 bool mbOwnsStorage : 1;
72 bool mbUserAllowsLinkUpdate : 1;
74 const uno::Reference < embed::XStorage >& GetReplacements();
77 const uno::Reference < embed::XStorage >& EmbedImpl::GetReplacements()
79 if ( !mxImageStorage.is() )
81 try
83 mxImageStorage = mxStorage->openStorageElement(
84 "ObjectReplacements", embed::ElementModes::READWRITE );
86 catch (const uno::Exception&)
88 mxImageStorage = mxStorage->openStorageElement(
89 "ObjectReplacements", embed::ElementModes::READ );
93 if ( !mxImageStorage.is() )
94 throw io::IOException("No ObjectReplacements");
96 return mxImageStorage;
99 EmbeddedObjectContainer::EmbeddedObjectContainer()
100 : pImpl(new EmbedImpl)
102 pImpl->mxStorage = ::comphelper::OStorageHelper::GetTemporaryStorage();
103 pImpl->mbOwnsStorage = true;
104 pImpl->mbUserAllowsLinkUpdate = true;
105 pImpl->mpTempObjectContainer = nullptr;
108 EmbeddedObjectContainer::EmbeddedObjectContainer( const uno::Reference < embed::XStorage >& rStor )
109 : pImpl(new EmbedImpl)
111 pImpl->mxStorage = rStor;
112 pImpl->mbOwnsStorage = false;
113 pImpl->mbUserAllowsLinkUpdate = true;
114 pImpl->mpTempObjectContainer = nullptr;
117 EmbeddedObjectContainer::EmbeddedObjectContainer( const uno::Reference < embed::XStorage >& rStor, const uno::Reference < uno::XInterface >& xModel )
118 : pImpl(new EmbedImpl)
120 pImpl->mxStorage = rStor;
121 pImpl->mbOwnsStorage = false;
122 pImpl->mbUserAllowsLinkUpdate = true;
123 pImpl->mpTempObjectContainer = nullptr;
124 pImpl->m_xModel = xModel;
127 void EmbeddedObjectContainer::SwitchPersistence( const uno::Reference < embed::XStorage >& rStor )
129 ReleaseImageSubStorage();
131 if ( pImpl->mbOwnsStorage )
132 pImpl->mxStorage->dispose();
134 pImpl->mxStorage = rStor;
135 pImpl->mbOwnsStorage = false;
138 bool EmbeddedObjectContainer::CommitImageSubStorage()
140 if ( !pImpl->mxImageStorage )
141 return true;
145 bool bReadOnlyMode = true;
146 uno::Reference < beans::XPropertySet > xSet(pImpl->mxImageStorage,uno::UNO_QUERY);
147 if ( xSet.is() )
149 // get the open mode from the parent storage
150 sal_Int32 nMode = 0;
151 uno::Any aAny = xSet->getPropertyValue("OpenMode");
152 if ( aAny >>= nMode )
153 bReadOnlyMode = !(nMode & embed::ElementModes::WRITE );
154 } // if ( xSet.is() )
155 if ( !bReadOnlyMode )
157 uno::Reference< embed::XTransactedObject > xTransact( pImpl->mxImageStorage, uno::UNO_QUERY_THROW );
158 xTransact->commit();
161 catch (const uno::Exception&)
163 return false;
166 return true;
169 void EmbeddedObjectContainer::ReleaseImageSubStorage()
171 CommitImageSubStorage();
173 if ( pImpl->mxImageStorage.is() )
177 pImpl->mxImageStorage->dispose();
178 pImpl->mxImageStorage.clear();
180 catch (const uno::Exception&)
182 SAL_WARN( "comphelper.container", "Problems releasing image substorage!" );
187 EmbeddedObjectContainer::~EmbeddedObjectContainer()
189 ReleaseImageSubStorage();
191 if ( pImpl->mbOwnsStorage )
192 pImpl->mxStorage->dispose();
194 delete pImpl->mpTempObjectContainer;
197 void EmbeddedObjectContainer::CloseEmbeddedObjects()
199 for( const auto& rObj : pImpl->maNameToObjectMap )
201 uno::Reference < util::XCloseable > const & xClose = rObj.second;
202 if( xClose.is() )
206 xClose->close( true );
208 catch (const uno::Exception&)
215 OUString EmbeddedObjectContainer::CreateUniqueObjectName()
217 OUString aStr;
218 sal_Int32 i=1;
221 aStr = "Object " + OUString::number( i++ );
223 while( HasEmbeddedObject( aStr ) );
224 // TODO/LATER: should we consider deleted objects?
226 return aStr;
229 uno::Sequence < OUString > EmbeddedObjectContainer::GetObjectNames() const
231 return comphelper::mapKeysToSequence(pImpl->maNameToObjectMap);
234 bool EmbeddedObjectContainer::HasEmbeddedObjects() const
236 return !pImpl->maNameToObjectMap.empty();
239 bool EmbeddedObjectContainer::HasEmbeddedObject( const OUString& rName )
241 if (pImpl->maNameToObjectMap.contains(rName))
242 return true;
243 if (!pImpl->mxStorage.is())
244 return false;
245 return pImpl->mxStorage->hasByName(rName);
248 bool EmbeddedObjectContainer::HasEmbeddedObject( const uno::Reference < embed::XEmbeddedObject >& xObj ) const
250 return pImpl->maObjectToNameMap.contains(xObj);
253 bool EmbeddedObjectContainer::HasInstantiatedEmbeddedObject( const OUString& rName )
255 // allows to detect whether the object was already instantiated
256 // currently the filter instantiate it on loading, so this method allows
257 // to avoid objects pointing to the same persistence
258 return pImpl->maNameToObjectMap.contains(rName);
261 OUString EmbeddedObjectContainer::GetEmbeddedObjectName( const css::uno::Reference < css::embed::XEmbeddedObject >& xObj ) const
263 auto it = pImpl->maObjectToNameMap.find(xObj);
264 if (it == pImpl->maObjectToNameMap.end())
266 SAL_WARN( "comphelper.container", "Unknown object!" );
267 return OUString();
269 return it->second;
272 uno::Reference< embed::XEmbeddedObject>
273 EmbeddedObjectContainer::GetEmbeddedObject(
274 const OUString& rName, OUString const*const pBaseURL)
276 SAL_WARN_IF( rName.isEmpty(), "comphelper.container", "Empty object name!");
278 uno::Reference < embed::XEmbeddedObject > xObj;
279 auto aIt = pImpl->maNameToObjectMap.find( rName );
281 #if OSL_DEBUG_LEVEL > 1
282 uno::Reference < container::XNameAccess > xAccess( pImpl->mxStorage, uno::UNO_QUERY );
283 uno::Sequence< OUString> aSeq = xAccess->getElementNames();
284 const OUString* pIter = aSeq.getConstArray();
285 const OUString* pEnd = pIter + aSeq.getLength();
286 for(;pIter != pEnd;++pIter)
288 (void)*pIter;
290 OSL_ENSURE( aIt != pImpl->maNameToObjectMap.end() || xAccess->hasByName(rName), "Could not return object!" );
291 #endif
293 // check if object was already created
294 if ( aIt != pImpl->maNameToObjectMap.end() )
295 xObj = (*aIt).second;
296 else
297 xObj = Get_Impl(rName, uno::Reference<embed::XEmbeddedObject>(), pBaseURL);
299 return xObj;
302 uno::Reference<embed::XEmbeddedObject> EmbeddedObjectContainer::Get_Impl(
303 const OUString& rName,
304 const uno::Reference<embed::XEmbeddedObject>& xCopy,
305 OUString const*const pBaseURL)
307 uno::Reference < embed::XEmbeddedObject > xObj;
310 // create the object from the storage
311 uno::Reference < beans::XPropertySet > xSet( pImpl->mxStorage, uno::UNO_QUERY );
312 bool bReadOnlyMode = true;
313 if ( xSet.is() )
315 // get the open mode from the parent storage
316 sal_Int32 nMode = 0;
317 uno::Any aAny = xSet->getPropertyValue("OpenMode");
318 if ( aAny >>= nMode )
319 bReadOnlyMode = !(nMode & embed::ElementModes::WRITE );
322 // object was not added until now - should happen only by calling this method from "inside"
323 //TODO/LATER: it would be good to detect an error when an object should be created already, but isn't (not an "inside" call)
324 uno::Reference < embed::XEmbeddedObjectCreator > xFactory = embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() );
325 uno::Sequence< beans::PropertyValue > aObjDescr(1 + (xCopy.is() ? 1 : 0) + (pBaseURL ? 1 : 0));
326 auto itObjDescr = aObjDescr.getArray();
327 itObjDescr->Name = "Parent";
328 itObjDescr->Value <<= pImpl->m_xModel.get();
329 if (pBaseURL)
331 ++itObjDescr;
332 itObjDescr->Name = "DefaultParentBaseURL";
333 itObjDescr->Value <<= *pBaseURL;
335 if ( xCopy.is() )
337 ++itObjDescr;
338 itObjDescr->Name = "CloneFrom";
339 itObjDescr->Value <<= xCopy;
342 uno::Sequence< beans::PropertyValue > aMediaDescr{ comphelper::makePropertyValue(
343 "ReadOnly", bReadOnlyMode) };
344 xObj.set( xFactory->createInstanceInitFromEntry(
345 pImpl->mxStorage, rName,
346 aMediaDescr, aObjDescr ), uno::UNO_QUERY );
348 // insert object into my list
349 AddEmbeddedObject( xObj, rName );
351 catch (uno::Exception const& e)
353 SAL_WARN("comphelper.container", "EmbeddedObjectContainer::Get_Impl: exception caught: " << e);
356 return xObj;
359 uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::CreateEmbeddedObject( const uno::Sequence < sal_Int8 >& rClassId,
360 const uno::Sequence < beans::PropertyValue >& rArgs, OUString& rNewName, OUString const* pBaseURL )
362 if ( rNewName.isEmpty() )
363 rNewName = CreateUniqueObjectName();
365 SAL_WARN_IF( HasEmbeddedObject(rNewName), "comphelper.container", "Object to create already exists!");
367 // create object from classid by inserting it into storage
368 uno::Reference < embed::XEmbeddedObject > xObj;
371 uno::Reference < embed::XEmbeddedObjectCreator > xFactory = embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() );
373 const size_t nExtraArgs = pBaseURL ? 2 : 1;
374 uno::Sequence< beans::PropertyValue > aObjDescr( rArgs.getLength() + nExtraArgs );
375 auto pObjDescr = aObjDescr.getArray();
376 pObjDescr[0].Name = "Parent";
377 pObjDescr[0].Value <<= pImpl->m_xModel.get();
378 if (pBaseURL)
380 pObjDescr[1].Name = "DefaultParentBaseURL";
381 pObjDescr[1].Value <<= *pBaseURL;
383 std::copy( rArgs.begin(), rArgs.end(), pObjDescr + nExtraArgs );
384 xObj.set( xFactory->createInstanceInitNew(
385 rClassId, OUString(), pImpl->mxStorage, rNewName,
386 aObjDescr ), uno::UNO_QUERY );
388 AddEmbeddedObject( xObj, rNewName );
390 OSL_ENSURE( !xObj.is() || xObj->getCurrentState() != embed::EmbedStates::LOADED,
391 "A freshly create object should be running always!" );
393 catch (uno::Exception const& e)
395 SAL_WARN("comphelper.container", "EmbeddedObjectContainer::CreateEmbeddedObject: exception caught: " << e);
398 return xObj;
401 uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::CreateEmbeddedObject( const uno::Sequence < sal_Int8 >& rClassId, OUString& rNewName, OUString const* pBaseURL )
403 return CreateEmbeddedObject( rClassId, uno::Sequence < beans::PropertyValue >(), rNewName, pBaseURL );
406 void EmbeddedObjectContainer::AddEmbeddedObject( const css::uno::Reference < css::embed::XEmbeddedObject >& xObj, const OUString& rName )
408 #if OSL_DEBUG_LEVEL > 1
409 SAL_WARN_IF( rName.isEmpty(), "comphelper.container", "Added object doesn't have a name!");
410 uno::Reference < container::XNameAccess > xAccess( pImpl->mxStorage, uno::UNO_QUERY );
411 uno::Reference < embed::XEmbedPersist > xEmb( xObj, uno::UNO_QUERY );
412 uno::Reference < embed::XLinkageSupport > xLink( xEmb, uno::UNO_QUERY );
413 // if the object has a persistence and the object is not a link than it must have persistence entry in the storage
414 OSL_ENSURE( !( xEmb.is() && ( !xLink.is() || !xLink->isLink() ) ) || xAccess->hasByName(rName),
415 "Added element not in storage!" );
416 #endif
418 // remember object - it needs to be in storage already
419 auto aIt = pImpl->maNameToObjectMap.find( rName );
420 OSL_ENSURE( aIt == pImpl->maNameToObjectMap.end(), "Element already inserted!" );
421 pImpl->maNameToObjectMap[ rName ] = xObj;
422 pImpl->maObjectToNameMap[ xObj ] = rName;
423 uno::Reference < container::XChild > xChild( xObj, uno::UNO_QUERY );
424 if ( xChild.is() && xChild->getParent() != pImpl->m_xModel.get() )
425 xChild->setParent( pImpl->m_xModel.get() );
427 // look for object in temporary container
428 if ( !pImpl->mpTempObjectContainer )
429 return;
431 auto& rObjectContainer = pImpl->mpTempObjectContainer->pImpl->maNameToObjectMap;
432 auto aIter = std::find_if(rObjectContainer.begin(), rObjectContainer.end(),
433 [&xObj](const EmbeddedObjectContainerNameMap::value_type& rEntry) { return rEntry.second == xObj; });
434 if (aIter == rObjectContainer.end())
435 return;
437 // copy replacement image from temporary container (if there is any)
438 OUString aTempName = aIter->first;
439 OUString aMediaType;
440 uno::Reference < io::XInputStream > xStream = pImpl->mpTempObjectContainer->GetGraphicStream( xObj, &aMediaType );
441 if ( xStream.is() )
443 InsertGraphicStream( xStream, rName, aMediaType );
444 xStream = nullptr;
445 pImpl->mpTempObjectContainer->RemoveGraphicStream( aTempName );
448 // remove object from storage of temporary container
449 uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
450 if ( xPersist.is() )
454 pImpl->mpTempObjectContainer->pImpl->mxStorage->removeElement( aTempName );
456 catch (const uno::Exception&)
461 // temp. container needs to forget the object
462 pImpl->mpTempObjectContainer->pImpl->maObjectToNameMap.erase( aIter->second );
463 pImpl->mpTempObjectContainer->pImpl->maNameToObjectMap.erase( aIter );
466 bool EmbeddedObjectContainer::StoreEmbeddedObject(
467 const uno::Reference < embed::XEmbeddedObject >& xObj, OUString& rName, bool bCopy,
468 const OUString& rSrcShellID, const OUString& rDestShellID )
470 uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
471 if ( rName.isEmpty() )
472 rName = CreateUniqueObjectName();
474 #if OSL_DEBUG_LEVEL > 1
475 uno::Reference < container::XNameAccess > xAccess( pImpl->mxStorage, uno::UNO_QUERY );
476 OSL_ENSURE( !xPersist.is() || !xAccess->hasByName(rName), "Inserting element already present in storage!" );
477 OSL_ENSURE( xPersist.is() || xObj->getCurrentState() == embed::EmbedStates::RUNNING, "Non persistent object inserted!");
478 #endif
480 // insert objects' storage into the container storage (if object has one)
483 if ( xPersist.is() )
485 uno::Sequence < beans::PropertyValue > aSeq;
486 if ( bCopy )
488 auto aObjArgs(::comphelper::InitPropertySequence({
489 { "SourceShellID", uno::Any(rSrcShellID) },
490 { "DestinationShellID", uno::Any(rDestShellID) }
491 }));
492 xPersist->storeToEntry(pImpl->mxStorage, rName, aSeq, aObjArgs);
494 else
496 //TODO/LATER: possible optimization, don't store immediately
497 //xPersist->setPersistentEntry( pImpl->mxStorage, rName, embed::EntryInitModes::ENTRY_NO_INIT, aSeq, aSeq );
498 xPersist->storeAsEntry( pImpl->mxStorage, rName, aSeq, aSeq );
499 xPersist->saveCompleted( true );
503 catch (uno::Exception const& e)
505 SAL_WARN("comphelper.container", "EmbeddedObjectContainer::StoreEmbeddedObject: exception caught: " << e);
506 // TODO/LATER: better error recovery should keep storage intact
507 return false;
510 return true;
513 bool EmbeddedObjectContainer::InsertEmbeddedObject( const uno::Reference < embed::XEmbeddedObject >& xObj, OUString& rName )
515 // store it into the container storage
516 if (StoreEmbeddedObject(xObj, rName, false, OUString(), OUString()))
518 // remember object
519 AddEmbeddedObject( xObj, rName );
520 return true;
522 else
523 return false;
526 uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::InsertEmbeddedObject( const uno::Reference < io::XInputStream >& xStm, OUString& rNewName )
528 if ( rNewName.isEmpty() )
529 rNewName = CreateUniqueObjectName();
531 // store it into the container storage
532 bool bIsStorage = false;
535 // first try storage persistence
536 uno::Reference < embed::XStorage > xStore = ::comphelper::OStorageHelper::GetStorageFromInputStream( xStm );
538 // storage was created from stream successfully
539 bIsStorage = true;
541 uno::Reference < embed::XStorage > xNewStore = pImpl->mxStorage->openStorageElement( rNewName, embed::ElementModes::READWRITE );
542 xStore->copyToStorage( xNewStore );
544 catch (const uno::Exception&)
546 if ( bIsStorage )
547 // it is storage persistence, but opening of new substorage or copying to it failed
548 return uno::Reference < embed::XEmbeddedObject >();
550 // stream didn't contain a storage, now try stream persistence
553 uno::Reference < io::XStream > xNewStream = pImpl->mxStorage->openStreamElement( rNewName, embed::ElementModes::READWRITE );
554 ::comphelper::OStorageHelper::CopyInputToOutput( xStm, xNewStream->getOutputStream() );
556 // No mediatype is provided so the default for OLE objects value is used
557 // it is correct so for now, but what if somebody introduces a new stream based embedded object?
558 // Probably introducing of such an object must be restricted ( a storage must be used! ).
559 uno::Reference< beans::XPropertySet > xProps( xNewStream, uno::UNO_QUERY_THROW );
560 xProps->setPropertyValue("MediaType",
561 uno::Any( OUString( "application/vnd.sun.star.oleobject" ) ) );
563 catch (uno::Exception const& e)
565 // complete disaster!
566 SAL_WARN("comphelper.container", "EmbeddedObjectContainer::InsertEmbeddedObject: exception caught: " << e);
567 return uno::Reference < embed::XEmbeddedObject >();
571 // stream was copied into the container storage in either way, now try to open something form it
572 uno::Reference < embed::XEmbeddedObject > xRet = GetEmbeddedObject( rNewName );
575 if ( !xRet.is() )
576 // no object could be created, so withdraw insertion
577 pImpl->mxStorage->removeElement( rNewName );
579 catch (const uno::Exception&)
583 return xRet;
586 uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::InsertEmbeddedObject( const css::uno::Sequence < css::beans::PropertyValue >& aMedium, OUString& rNewName, OUString const* pBaseURL )
588 if ( rNewName.isEmpty() )
589 rNewName = CreateUniqueObjectName();
591 uno::Reference < embed::XEmbeddedObject > xObj;
594 uno::Reference < embed::XEmbeddedObjectCreator > xFactory = embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() );
595 uno::Sequence< beans::PropertyValue > aObjDescr(pBaseURL ? 2 : 1);
596 auto pObjDescr = aObjDescr.getArray();
597 pObjDescr[0].Name = "Parent";
598 pObjDescr[0].Value <<= pImpl->m_xModel.get();
599 if (pBaseURL)
601 pObjDescr[1].Name = "DefaultParentBaseURL";
602 pObjDescr[1].Value <<= *pBaseURL;
604 xObj.set( xFactory->createInstanceInitFromMediaDescriptor(
605 pImpl->mxStorage, rNewName, aMedium, aObjDescr ), uno::UNO_QUERY );
606 uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
608 OSL_ENSURE( !xObj.is() || xObj->getCurrentState() != embed::EmbedStates::LOADED,
609 "A freshly create object should be running always!" );
611 // possible optimization: store later!
612 if ( xPersist.is())
613 xPersist->storeOwn();
615 AddEmbeddedObject( xObj, rNewName );
617 catch (const uno::Exception&)
621 return xObj;
624 uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::InsertEmbeddedLink( const css::uno::Sequence < css::beans::PropertyValue >& aMedium, OUString& rNewName )
626 if ( rNewName.isEmpty() )
627 rNewName = CreateUniqueObjectName();
629 uno::Reference < embed::XEmbeddedObject > xObj;
632 uno::Reference < embed::XEmbeddedObjectCreator > xFactory = embed::EmbeddedObjectCreator::create(::comphelper::getProcessComponentContext());
633 uno::Sequence< beans::PropertyValue > aObjDescr{ comphelper::makePropertyValue(
634 "Parent", pImpl->m_xModel.get()) };
635 xObj.set( xFactory->createInstanceLink( pImpl->mxStorage, rNewName, aMedium, aObjDescr ), uno::UNO_QUERY );
637 uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
639 OSL_ENSURE( !xObj.is() || xObj->getCurrentState() != embed::EmbedStates::LOADED,
640 "A freshly create object should be running always!" );
642 // possible optimization: store later!
643 if ( xPersist.is())
644 xPersist->storeOwn();
646 AddEmbeddedObject( xObj, rNewName );
648 catch (uno::Exception const& e)
650 SAL_WARN("comphelper.container", "EmbeddedObjectContainer::InsertEmbeddedLink: "
651 "exception caught: " << e);
654 return xObj;
657 bool EmbeddedObjectContainer::TryToCopyGraphReplacement( EmbeddedObjectContainer& rSrc,
658 const OUString& aOrigName,
659 const OUString& aTargetName )
661 bool bResult = false;
663 if ( ( &rSrc != this || aOrigName != aTargetName ) && !aOrigName.isEmpty() && !aTargetName.isEmpty() )
665 OUString aMediaType;
666 uno::Reference < io::XInputStream > xGrStream = rSrc.GetGraphicStream( aOrigName, &aMediaType );
667 if ( xGrStream.is() )
668 bResult = InsertGraphicStream( xGrStream, aTargetName, aMediaType );
671 return bResult;
674 uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::CopyAndGetEmbeddedObject(
675 EmbeddedObjectContainer& rSrc, const uno::Reference <embed::XEmbeddedObject>& xObj, OUString& rName,
676 const OUString& rSrcShellID, const OUString& rDestShellID )
678 uno::Reference< embed::XEmbeddedObject > xResult;
680 // TODO/LATER: For now only objects that implement XEmbedPersist have a replacement image, it might change in future
681 // do an incompatible change so that object name is provided in all the move and copy methods
682 OUString aOrigName;
685 uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY_THROW );
686 aOrigName = xPersist->getEntryName();
688 catch (const uno::Exception&)
692 if ( rName.isEmpty() )
693 rName = CreateUniqueObjectName();
695 // objects without persistence are not really stored by the method
696 if (xObj.is() && StoreEmbeddedObject(xObj, rName, true, rSrcShellID, rDestShellID))
698 SAL_INFO_IF(rDestShellID.isEmpty(), "comphelper.container",
699 "SfxObjectShell with no base URL?"); // every shell has a base URL, except the clipboard SwDocShell
700 xResult = Get_Impl(rName, xObj, &rDestShellID);
701 if ( !xResult.is() )
703 // this is a case when object has no real persistence
704 // in such cases a new object should be explicitly created and initialized with the data of the old one
707 uno::Reference< embed::XLinkageSupport > xOrigLinkage( xObj, uno::UNO_QUERY );
708 if ( xOrigLinkage.is() && xOrigLinkage->isLink() )
710 // this is an OOo link, it has no persistence
711 OUString aURL = xOrigLinkage->getLinkURL();
712 if ( aURL.isEmpty() )
713 throw uno::RuntimeException();
715 // create new linked object from the URL the link is based on
716 uno::Reference < embed::XEmbeddedObjectCreator > xCreator =
717 embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() );
719 uno::Sequence< beans::PropertyValue > aMediaDescr{ comphelper::makePropertyValue(
720 "URL", aURL) };
721 uno::Sequence< beans::PropertyValue > aObjDescr{ comphelper::makePropertyValue(
722 "Parent", pImpl->m_xModel.get()) };
723 xResult.set(xCreator->createInstanceLink(
724 pImpl->mxStorage,
725 rName,
726 aMediaDescr,
727 aObjDescr ),
728 uno::UNO_QUERY_THROW );
730 else
732 // the component is required for copying of this object
733 if ( xObj->getCurrentState() == embed::EmbedStates::LOADED )
734 xObj->changeState( embed::EmbedStates::RUNNING );
736 // this must be an object based on properties, otherwise we can not copy it currently
737 uno::Reference< beans::XPropertySet > xOrigProps( xObj->getComponent(), uno::UNO_QUERY_THROW );
739 // use object class ID to create a new one and transfer all the properties
740 uno::Reference < embed::XEmbeddedObjectCreator > xCreator =
741 embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() );
743 uno::Sequence< beans::PropertyValue > aObjDescr{ comphelper::makePropertyValue(
744 "Parent", pImpl->m_xModel.get()) };
745 xResult.set(xCreator->createInstanceInitNew(
746 xObj->getClassID(),
747 xObj->getClassName(),
748 pImpl->mxStorage,
749 rName,
750 aObjDescr ),
751 uno::UNO_QUERY_THROW );
753 if ( xResult->getCurrentState() == embed::EmbedStates::LOADED )
754 xResult->changeState( embed::EmbedStates::RUNNING );
756 uno::Reference< beans::XPropertySet > xTargetProps( xResult->getComponent(), uno::UNO_QUERY_THROW );
758 // copy all the properties from xOrigProps to xTargetProps
759 uno::Reference< beans::XPropertySetInfo > xOrigInfo = xOrigProps->getPropertySetInfo();
760 if ( !xOrigInfo.is() )
761 throw uno::RuntimeException();
763 const uno::Sequence< beans::Property > aPropertiesList = xOrigInfo->getProperties();
764 for ( const auto & p : aPropertiesList )
768 xTargetProps->setPropertyValue(
769 p.Name,
770 xOrigProps->getPropertyValue( p.Name ) );
772 catch (const beans::PropertyVetoException&)
774 // impossibility to copy readonly property is not treated as an error for now
775 // but the assertion is helpful to detect such scenarios and review them
776 SAL_WARN( "comphelper.container", "Could not copy readonly property!" );
781 if ( xResult.is() )
782 AddEmbeddedObject( xResult, rName );
784 catch (const uno::Exception&)
786 if ( xResult.is() )
790 xResult->close( true );
792 catch (const uno::Exception&)
795 xResult.clear();
801 SAL_WARN_IF( !xResult.is(), "comphelper.container", "Can not copy embedded object that has no persistence!" );
803 if ( xResult.is() )
805 // the object is successfully copied, try to copy graphical replacement
806 if ( !aOrigName.isEmpty() )
807 TryToCopyGraphReplacement( rSrc, aOrigName, rName );
809 // the object might need the size to be set
812 if ( xResult->getStatus( embed::Aspects::MSOLE_CONTENT ) & embed::EmbedMisc::EMBED_NEEDSSIZEONLOAD )
813 xResult->setVisualAreaSize( embed::Aspects::MSOLE_CONTENT,
814 xObj->getVisualAreaSize( embed::Aspects::MSOLE_CONTENT ) );
816 catch (const uno::Exception&)
821 return xResult;
824 // #i119941, bKeepToTempStorage: use to specify whether store the removed object to temporary storage+
825 void EmbeddedObjectContainer::RemoveEmbeddedObject( const OUString& rName, bool bKeepToTempStorage )
827 uno::Reference < embed::XEmbeddedObject > xObj = GetEmbeddedObject( rName );
828 if ( xObj.is() )
829 RemoveEmbeddedObject( xObj, bKeepToTempStorage );
832 bool EmbeddedObjectContainer::MoveEmbeddedObject( const OUString& rName, EmbeddedObjectContainer& rCnt )
834 // find object entry
835 auto aIt2 = rCnt.pImpl->maNameToObjectMap.find( rName );
836 OSL_ENSURE( aIt2 == rCnt.pImpl->maNameToObjectMap.end(), "Object does already exist in target container!" );
838 if ( aIt2 != rCnt.pImpl->maNameToObjectMap.end() )
839 return false;
841 uno::Reference < embed::XEmbeddedObject > xObj;
842 auto aIt = pImpl->maNameToObjectMap.find( rName );
843 if ( aIt != pImpl->maNameToObjectMap.end() )
845 xObj = (*aIt).second;
848 if ( xObj.is() )
850 // move object
851 OUString aName( rName );
852 rCnt.InsertEmbeddedObject( xObj, aName );
853 pImpl->maObjectToNameMap.erase( aIt->second );
854 pImpl->maNameToObjectMap.erase( aIt );
855 uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
856 if ( xPersist.is() )
857 pImpl->mxStorage->removeElement( rName );
859 else
861 // copy storages; object *must* have persistence!
862 uno::Reference < embed::XStorage > xOld = pImpl->mxStorage->openStorageElement( rName, embed::ElementModes::READ );
863 uno::Reference < embed::XStorage > xNew = rCnt.pImpl->mxStorage->openStorageElement( rName, embed::ElementModes::READWRITE );
864 xOld->copyToStorage( xNew );
867 rCnt.TryToCopyGraphReplacement( *this, rName, rName );
868 // RemoveGraphicStream( rName );
870 return true;
872 catch (const uno::Exception&)
874 SAL_WARN( "comphelper.container", "Could not move object!");
875 return false;
879 else
880 SAL_WARN( "comphelper.container", "Unknown object!");
881 return false;
884 // #i119941, bKeepToTempStorage: use to specify whether store the removed object to temporary storage+
885 bool EmbeddedObjectContainer::RemoveEmbeddedObject( const uno::Reference < embed::XEmbeddedObject >& xObj, bool bKeepToTempStorage )
887 uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
888 OUString aName;
889 if ( xPersist.is() )
890 aName = xPersist->getEntryName();
892 #if OSL_DEBUG_LEVEL > 1
893 uno::Reference < container::XNameAccess > xAccess( pImpl->mxStorage, uno::UNO_QUERY );
894 uno::Reference < embed::XLinkageSupport > xLink( xPersist, uno::UNO_QUERY );
895 sal_Bool bIsNotEmbedded = !xPersist.is() || ( xLink.is() && xLink->isLink() );
897 // if the object has a persistence and the object is not a link than it must have persistence entry in the storage
898 OSL_ENSURE( bIsNotEmbedded || xAccess->hasByName(aName), "Removing element not present in storage!" );
899 #endif
901 // somebody still needs the object, so we must assign a temporary persistence
904 if ( xPersist.is() && bKeepToTempStorage ) // #i119941
907 if ( !pImpl->mpTempObjectContainer )
909 pImpl->mpTempObjectContainer = new EmbeddedObjectContainer();
912 // TODO/LATER: in future probably the temporary container will have two storages ( of two formats )
913 // the media type will be provided with object insertion
914 OUString aOrigStorMediaType;
915 uno::Reference< beans::XPropertySet > xStorProps( pImpl->mxStorage, uno::UNO_QUERY_THROW );
916 static constexpr OUString s_sMediaType(u"MediaType"_ustr);
917 xStorProps->getPropertyValue( s_sMediaType ) >>= aOrigStorMediaType;
919 SAL_WARN_IF( aOrigStorMediaType.isEmpty(), "comphelper.container", "No valuable media type in the storage!" );
921 uno::Reference< beans::XPropertySet > xTargetStorProps(
922 pImpl->mpTempObjectContainer->pImpl->mxStorage,
923 uno::UNO_QUERY_THROW );
924 xTargetStorProps->setPropertyValue( s_sMediaType,uno::Any( aOrigStorMediaType ) );
926 catch (const uno::Exception&)
928 SAL_WARN( "comphelper.container", "Can not set the new media type to a storage!" );
932 OUString aTempName, aMediaType;
933 /* Do not create a new name for a removed object, in the pImpl->mpTempObjectContainer,
934 because the original m_aEntryName of xObj will be overwritten by InsertEmbeddedObject(),
935 so uno::Reference < embed::XEmbeddedObject >& xObj will misbehave in
936 EmbeddedObjectContainer::StoreAsChildren and SfxObjectShell::SaveCompletedChildren
937 and will throw an exception because of objects with the same names! */
938 if( !pImpl->mpTempObjectContainer->HasEmbeddedObject(aName) )
939 aTempName = aName;
941 pImpl->mpTempObjectContainer->InsertEmbeddedObject( xObj, aTempName );
943 uno::Reference < io::XInputStream > xStream = GetGraphicStream( xObj, &aMediaType );
944 if ( xStream.is() )
945 pImpl->mpTempObjectContainer->InsertGraphicStream( xStream, aTempName, aMediaType );
947 // object is stored, so at least it can be set to loaded state
948 xObj->changeState( embed::EmbedStates::LOADED );
950 else
951 // objects without persistence need to stay in running state if they shall not be closed
952 xObj->changeState( embed::EmbedStates::RUNNING );
954 catch (const uno::Exception&)
956 return false;
959 auto aIter = std::find_if(pImpl->maNameToObjectMap.begin(), pImpl->maNameToObjectMap.end(),
960 [&xObj](const EmbeddedObjectContainerNameMap::value_type& rEntry) { return rEntry.second == xObj; });
961 if (aIter != pImpl->maNameToObjectMap.end())
963 pImpl->maObjectToNameMap.erase( aIter->second );
964 pImpl->maNameToObjectMap.erase( aIter );
965 uno::Reference < container::XChild > xChild( xObj, uno::UNO_QUERY );
966 if ( xChild.is() )
967 xChild->setParent( uno::Reference < uno::XInterface >() );
969 else
970 SAL_WARN( "comphelper.container", "Object not found for removal!" );
972 if ( !xPersist || !bKeepToTempStorage ) // #i119941#
973 return true;
975 // remove replacement image (if there is one)
976 RemoveGraphicStream( aName );
978 // now it's time to remove the storage from the container storage
981 #if OSL_DEBUG_LEVEL > 1
982 // if the object has a persistence and the object is not a link than it must have persistence entry in storage
983 OSL_ENSURE( bIsNotEmbedded || pImpl->mxStorage->hasByName( aName ), "The object has no persistence entry in the storage!" );
984 #endif
985 if ( xPersist.is() && pImpl->mxStorage->hasByName( aName ) )
986 pImpl->mxStorage->removeElement( aName );
988 catch (const uno::Exception&)
990 SAL_WARN( "comphelper.container", "Failed to remove object from storage!" );
991 return false;
994 return true;
997 void EmbeddedObjectContainer::CloseEmbeddedObject( const uno::Reference < embed::XEmbeddedObject >& xObj )
999 // disconnect the object from the container and close it if possible
1001 auto aIter = std::find_if(pImpl->maNameToObjectMap.begin(), pImpl->maNameToObjectMap.end(),
1002 [&xObj](const EmbeddedObjectContainerNameMap::value_type& rEntry) { return rEntry.second == xObj; });
1003 if (aIter == pImpl->maNameToObjectMap.end())
1004 return;
1006 pImpl->maObjectToNameMap.erase( aIter->second );
1007 pImpl->maNameToObjectMap.erase( aIter );
1011 xObj->close( true );
1013 catch (const uno::Exception&)
1015 // it is no problem if the object is already closed
1016 // TODO/LATER: what if the object can not be closed?
1020 uno::Reference < io::XInputStream > EmbeddedObjectContainer::GetGraphicStream( const OUString& aName, OUString* pMediaType )
1022 uno::Reference < io::XInputStream > xStream;
1024 SAL_WARN_IF( aName.isEmpty(), "comphelper.container", "Retrieving graphic for unknown object!" );
1025 if ( !aName.isEmpty() )
1029 uno::Reference < embed::XStorage > xReplacements = pImpl->GetReplacements();
1030 uno::Reference < io::XStream > xGraphicStream = xReplacements->openStreamElement( aName, embed::ElementModes::READ );
1031 xStream = xGraphicStream->getInputStream();
1032 if ( pMediaType )
1034 uno::Reference < beans::XPropertySet > xSet( xStream, uno::UNO_QUERY );
1035 if ( xSet.is() )
1037 uno::Any aAny = xSet->getPropertyValue("MediaType");
1038 aAny >>= *pMediaType;
1042 catch (uno::Exception const& e)
1044 SAL_INFO("comphelper.container",
1045 "EmbeddedObjectContainer::GetGraphicStream(): " << e);
1049 return xStream;
1052 uno::Reference < io::XInputStream > EmbeddedObjectContainer::GetGraphicStream( const css::uno::Reference < css::embed::XEmbeddedObject >& xObj, OUString* pMediaType )
1054 // try to load it from the container storage
1055 return GetGraphicStream( GetEmbeddedObjectName( xObj ), pMediaType );
1058 bool EmbeddedObjectContainer::InsertGraphicStream( const css::uno::Reference < css::io::XInputStream >& rStream, const OUString& rObjectName, const OUString& rMediaType )
1062 uno::Reference < embed::XStorage > xReplacements = pImpl->GetReplacements();
1064 // store it into the subfolder
1065 uno::Reference < io::XOutputStream > xOutStream;
1066 uno::Reference < io::XStream > xGraphicStream = xReplacements->openStreamElement( rObjectName,
1067 embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE );
1068 xOutStream = xGraphicStream->getOutputStream();
1069 ::comphelper::OStorageHelper::CopyInputToOutput( rStream, xOutStream );
1070 xOutStream->flush();
1072 uno::Reference< beans::XPropertySet > xPropSet( xGraphicStream, uno::UNO_QUERY_THROW );
1074 xPropSet->setPropertyValue("UseCommonStoragePasswordEncryption",
1075 uno::Any( true ) );
1076 xPropSet->setPropertyValue("MediaType", uno::Any(rMediaType) );
1078 xPropSet->setPropertyValue("Compressed",
1079 uno::Any( true ) );
1081 catch (const uno::Exception&)
1083 return false;
1086 return true;
1089 bool EmbeddedObjectContainer::InsertGraphicStreamDirectly( const css::uno::Reference < css::io::XInputStream >& rStream, const OUString& rObjectName, const OUString& rMediaType )
1093 uno::Reference < embed::XStorage > xReplacement = pImpl->GetReplacements();
1094 uno::Reference < embed::XOptimizedStorage > xOptRepl( xReplacement, uno::UNO_QUERY_THROW );
1096 // store it into the subfolder
1097 uno::Sequence< beans::PropertyValue > aProps{
1098 comphelper::makePropertyValue("MediaType", rMediaType),
1099 comphelper::makePropertyValue("UseCommonStoragePasswordEncryption", true),
1100 comphelper::makePropertyValue("Compressed", true)
1103 if ( xReplacement->hasByName( rObjectName ) )
1104 xReplacement->removeElement( rObjectName );
1106 xOptRepl->insertStreamElementDirect( rObjectName, rStream, aProps );
1108 catch (const uno::Exception&)
1110 return false;
1113 return true;
1117 void EmbeddedObjectContainer::RemoveGraphicStream( const OUString& rObjectName )
1121 uno::Reference < embed::XStorage > xReplacements = pImpl->GetReplacements();
1122 xReplacements->removeElement( rObjectName );
1124 catch (const uno::Exception&)
1128 namespace {
1129 void InsertStreamIntoPicturesStorage_Impl( const uno::Reference< embed::XStorage >& xDocStor,
1130 const uno::Reference< io::XInputStream >& xInStream,
1131 const OUString& aStreamName )
1133 OSL_ENSURE( !aStreamName.isEmpty() && xInStream.is() && xDocStor.is(), "Misuse of the method!" );
1137 uno::Reference< embed::XStorage > xPictures = xDocStor->openStorageElement(
1138 "Pictures",
1139 embed::ElementModes::READWRITE );
1140 uno::Reference< io::XStream > xObjReplStr = xPictures->openStreamElement(
1141 aStreamName,
1142 embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE );
1143 uno::Reference< io::XOutputStream > xOutStream(
1144 xObjReplStr->getInputStream(), uno::UNO_QUERY_THROW );
1146 ::comphelper::OStorageHelper::CopyInputToOutput( xInStream, xOutStream );
1147 xOutStream->closeOutput();
1149 uno::Reference< embed::XTransactedObject > xTransact( xPictures, uno::UNO_QUERY );
1150 if ( xTransact.is() )
1151 xTransact->commit();
1153 catch (const uno::Exception&)
1155 SAL_WARN( "comphelper.container", "The images storage is not available!" );
1161 bool EmbeddedObjectContainer::StoreAsChildren(bool _bOasisFormat,bool _bCreateEmbedded, bool _bAutoSaveEvent,
1162 const uno::Reference < embed::XStorage >& _xStorage)
1164 bool bResult = false;
1167 comphelper::EmbeddedObjectContainer aCnt( _xStorage );
1168 for (auto& name : GetObjectNames())
1170 uno::Reference<embed::XEmbeddedObject> xObj = GetEmbeddedObject(name);
1171 SAL_WARN_IF( !xObj.is(), "comphelper.container", "An empty entry in the embedded objects list!" );
1172 if ( xObj.is() )
1174 bool bSwitchBackToLoaded = false;
1175 uno::Reference< embed::XLinkageSupport > xLink( xObj, uno::UNO_QUERY );
1177 uno::Reference < io::XInputStream > xStream;
1178 OUString aMediaType;
1180 sal_Int32 nCurState = xObj->getCurrentState();
1181 if ( nCurState == embed::EmbedStates::LOADED || nCurState == embed::EmbedStates::RUNNING )
1183 // means that the object is not active
1184 // copy replacement image from old to new container
1185 xStream = GetGraphicStream( xObj, &aMediaType );
1188 if ( !xStream.is() && getUserAllowsLinkUpdate() )
1190 // the image must be regenerated
1191 // TODO/LATER: another aspect could be used
1192 if ( xObj->getCurrentState() == embed::EmbedStates::LOADED )
1193 bSwitchBackToLoaded = true;
1195 xStream = GetGraphicReplacementStream(
1196 embed::Aspects::MSOLE_CONTENT,
1197 xObj,
1198 &aMediaType );
1201 if ( _bOasisFormat || (xLink.is() && xLink->isLink()) )
1203 if ( xStream.is() )
1205 if ( _bOasisFormat )
1207 // if it is an embedded object or the optimized inserting fails the normal inserting should be done
1208 if ( _bCreateEmbedded
1209 || !aCnt.InsertGraphicStreamDirectly(xStream, name, aMediaType))
1210 aCnt.InsertGraphicStream(xStream, name, aMediaType);
1212 else
1214 // it is a linked object exported into SO7 format
1215 InsertStreamIntoPicturesStorage_Impl(_xStorage, xStream, name);
1220 uno::Reference< embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
1221 if ( xPersist.is() )
1223 uno::Sequence< beans::PropertyValue > aArgs( _bOasisFormat ? 3 : 4 );
1224 auto pArgs = aArgs.getArray();
1225 pArgs[0].Name = "StoreVisualReplacement";
1226 pArgs[0].Value <<= !_bOasisFormat;
1228 // if it is an embedded object or the optimized inserting fails the normal inserting should be done
1229 pArgs[1].Name = "CanTryOptimization";
1230 pArgs[1].Value <<= !_bCreateEmbedded;
1232 pArgs[2].Name = "AutoSaveEvent";
1233 pArgs[2].Value <<= _bAutoSaveEvent;
1235 if ( !_bOasisFormat )
1237 // if object has no cached replacement it will use this one
1238 pArgs[3].Name = "VisualReplacement";
1239 pArgs[3].Value <<= xStream;
1244 xPersist->storeAsEntry( _xStorage, xPersist->getEntryName(), uno::Sequence< beans::PropertyValue >(), aArgs );
1246 catch (const embed::WrongStateException&)
1248 SAL_WARN("comphelper.container", "failed to store '" << name << "'");
1252 if ( bSwitchBackToLoaded )
1253 // switch back to loaded state; that way we have a minimum cache confusion
1254 xObj->changeState( embed::EmbedStates::LOADED );
1258 bResult = aCnt.CommitImageSubStorage();
1261 catch (const uno::Exception& e)
1263 // TODO/LATER: error handling
1264 bResult = false;
1265 SAL_WARN("comphelper.container", "failed. Message: " << e);
1268 // the old SO6 format does not store graphical replacements
1269 if ( !_bOasisFormat && bResult )
1273 // the substorage still can not be locked by the embedded object container
1274 OUString aObjReplElement( "ObjectReplacements" );
1275 if ( _xStorage->hasByName( aObjReplElement ) && _xStorage->isStorageElement( aObjReplElement ) )
1276 _xStorage->removeElement( aObjReplElement );
1278 catch (const uno::Exception&)
1280 // TODO/LATER: error handling;
1281 bResult = false;
1284 return bResult;
1287 bool EmbeddedObjectContainer::StoreChildren(bool _bOasisFormat,bool _bObjectsOnly)
1289 bool bResult = true;
1290 for (auto& name : GetObjectNames())
1294 uno::Reference<embed::XEmbeddedObject> xObj = GetEmbeddedObject(name);
1295 SAL_WARN_IF( !xObj.is(), "comphelper.container", "An empty entry in the embedded objects list!" );
1296 if ( xObj.is() )
1298 sal_Int32 nCurState = xObj->getCurrentState();
1299 if ( _bOasisFormat && nCurState != embed::EmbedStates::LOADED && nCurState != embed::EmbedStates::RUNNING )
1301 // means that the object is active
1302 // the image must be regenerated
1303 OUString aMediaType;
1305 // TODO/LATER: another aspect could be used
1306 uno::Reference < io::XInputStream > xStream =
1307 GetGraphicReplacementStream(
1308 embed::Aspects::MSOLE_CONTENT,
1309 xObj,
1310 &aMediaType );
1311 if ( xStream.is() )
1313 if (!InsertGraphicStreamDirectly(xStream, name, aMediaType))
1314 InsertGraphicStream(xStream, name, aMediaType);
1318 // TODO/LATER: currently the object by default does not cache replacement image
1319 // that means that if somebody loads SO7 document and store its objects using
1320 // this method the images might be lost.
1321 // Currently this method is only used on storing to alien formats, that means
1322 // that SO7 documents storing does not use it, and all other filters are
1323 // based on OASIS format. But if it changes the method must be fixed. The fix
1324 // must be done only on demand since it can affect performance.
1326 uno::Reference< embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
1327 if ( xPersist.is() )
1331 //TODO/LATER: only storing if changed!
1332 //xPersist->storeOwn(); //commented, i120168
1334 // begin:all charts will be persisted as xml format on disk when saving, which is time consuming.
1335 // '_bObjectsOnly' mean we are storing to alien formats.
1336 // 'isStorageElement' mean current object is NOT a MS OLE format. (may also include in future), i120168
1337 if (_bObjectsOnly && (nCurState == embed::EmbedStates::LOADED || nCurState == embed::EmbedStates::RUNNING)
1338 && (pImpl->mxStorage->isStorageElement(name)))
1340 uno::Reference< util::XModifiable > xModifiable( xObj->getComponent(), uno::UNO_QUERY );
1341 if ( xModifiable.is() && xModifiable->isModified())
1343 xPersist->storeOwn();
1345 else
1347 //do nothing. Embedded model is not modified, no need to persist.
1350 else //the embedded object is in active status, always store back it.
1352 xPersist->storeOwn();
1354 //end i120168
1356 catch (const uno::Exception&)
1358 // TODO/LATER: error handling
1359 bResult = false;
1360 break;
1364 if ( !_bOasisFormat && !_bObjectsOnly )
1366 // copy replacement images for linked objects
1369 uno::Reference< embed::XLinkageSupport > xLink( xObj, uno::UNO_QUERY );
1370 if ( xLink.is() && xLink->isLink() )
1372 OUString aMediaType;
1373 uno::Reference < io::XInputStream > xInStream = GetGraphicStream( xObj, &aMediaType );
1374 if ( xInStream.is() )
1375 InsertStreamIntoPicturesStorage_Impl( pImpl->mxStorage, xInStream, name );
1378 catch (const uno::Exception&)
1384 catch (const uno::Exception&)
1386 // TODO/LATER: error handling
1390 if ( bResult && _bOasisFormat )
1391 bResult = CommitImageSubStorage();
1393 if ( bResult && !_bObjectsOnly )
1397 ReleaseImageSubStorage();
1398 OUString aObjReplElement( "ObjectReplacements" );
1399 if ( !_bOasisFormat && pImpl->mxStorage->hasByName( aObjReplElement ) && pImpl->mxStorage->isStorageElement( aObjReplElement ) )
1400 pImpl->mxStorage->removeElement( aObjReplElement );
1402 catch (const uno::Exception&)
1404 // TODO/LATER: error handling
1405 bResult = false;
1408 return bResult;
1411 uno::Reference< io::XInputStream > EmbeddedObjectContainer::GetGraphicReplacementStream(
1412 sal_Int64 nViewAspect,
1413 const uno::Reference< embed::XEmbeddedObject >& xObj,
1414 OUString* pMediaType )
1416 uno::Reference< io::XInputStream > xInStream;
1417 if ( xObj.is() )
1421 // retrieving of the visual representation can switch object to running state
1422 embed::VisualRepresentation aRep = xObj->getPreferredVisualRepresentation( nViewAspect );
1423 if ( pMediaType )
1424 *pMediaType = aRep.Flavor.MimeType;
1426 uno::Sequence < sal_Int8 > aSeq;
1427 aRep.Data >>= aSeq;
1428 xInStream = new ::comphelper::SequenceInputStream( aSeq );
1430 catch (const uno::Exception&)
1435 return xInStream;
1438 bool EmbeddedObjectContainer::SetPersistentEntries(const uno::Reference< embed::XStorage >& _xStorage,bool _bClearModifiedFlag)
1440 bool bError = false;
1441 for (auto& name : GetObjectNames())
1443 uno::Reference<embed::XEmbeddedObject> xObj = GetEmbeddedObject(name);
1444 SAL_WARN_IF( !xObj.is(), "comphelper.container", "An empty entry in the embedded objects list!" );
1445 if ( xObj.is() )
1447 uno::Reference< embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
1448 if ( xPersist.is() )
1452 xPersist->setPersistentEntry( _xStorage,
1453 name,
1454 embed::EntryInitModes::NO_INIT,
1455 uno::Sequence< beans::PropertyValue >(),
1456 uno::Sequence< beans::PropertyValue >() );
1459 catch (const uno::Exception&)
1461 // TODO/LATER: error handling
1462 bError = true;
1463 break;
1466 if ( _bClearModifiedFlag )
1468 // if this method is used as part of SaveCompleted the object must stay unmodified after execution
1471 uno::Reference< util::XModifiable > xModif( xObj->getComponent(), uno::UNO_QUERY_THROW );
1472 if ( xModif->isModified() )
1473 xModif->setModified( false );
1475 catch (const uno::Exception&)
1481 return bError;
1484 bool EmbeddedObjectContainer::getUserAllowsLinkUpdate() const
1486 if (officecfg::Office::Common::Security::Scripting::DisableActiveContent::get())
1487 return false;
1488 return pImpl->mbUserAllowsLinkUpdate;
1491 void EmbeddedObjectContainer::setUserAllowsLinkUpdate(bool bNew)
1493 if(pImpl->mbUserAllowsLinkUpdate != bNew)
1495 pImpl->mbUserAllowsLinkUpdate = bNew;
1501 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */