bump product version to 6.4.0.3
[LibreOffice.git] / comphelper / source / container / embeddedobjectcontainer.cxx
blobaa818d78ce5e2a7748dc64a099e87d945ce15ab9
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 <cppuhelper/weakref.hxx>
46 #include <sal/log.hxx>
48 #include <algorithm>
49 #include <unordered_map>
52 using namespace ::com::sun::star;
54 namespace comphelper {
56 typedef std::unordered_map<OUString, uno::Reference<embed::XEmbeddedObject>> EmbeddedObjectContainerNameMap;
57 struct EmbedImpl
59 // TODO/LATER: remove objects from temp. Container storage when object is disposed
60 EmbeddedObjectContainerNameMap maNameToObjectMap;
61 // to speed up lookup by Reference
62 std::unordered_map<uno::Reference<embed::XEmbeddedObject>, OUString> maObjectToNameMap;
63 uno::Reference < embed::XStorage > mxStorage;
64 EmbeddedObjectContainer* mpTempObjectContainer;
65 uno::Reference < embed::XStorage > mxImageStorage;
66 uno::WeakReference < uno::XInterface > m_xModel;
68 bool mbOwnsStorage : 1;
69 bool mbUserAllowsLinkUpdate : 1;
71 const uno::Reference < embed::XStorage >& GetReplacements();
74 const uno::Reference < embed::XStorage >& EmbedImpl::GetReplacements()
76 if ( !mxImageStorage.is() )
78 try
80 mxImageStorage = mxStorage->openStorageElement(
81 "ObjectReplacements", embed::ElementModes::READWRITE );
83 catch (const uno::Exception&)
85 mxImageStorage = mxStorage->openStorageElement(
86 "ObjectReplacements", embed::ElementModes::READ );
90 if ( !mxImageStorage.is() )
91 throw io::IOException();
93 return mxImageStorage;
96 EmbeddedObjectContainer::EmbeddedObjectContainer()
97 : pImpl(new EmbedImpl)
99 pImpl->mxStorage = ::comphelper::OStorageHelper::GetTemporaryStorage();
100 pImpl->mbOwnsStorage = true;
101 pImpl->mbUserAllowsLinkUpdate = true;
102 pImpl->mpTempObjectContainer = nullptr;
105 EmbeddedObjectContainer::EmbeddedObjectContainer( const uno::Reference < embed::XStorage >& rStor )
106 : pImpl(new EmbedImpl)
108 pImpl->mxStorage = rStor;
109 pImpl->mbOwnsStorage = false;
110 pImpl->mbUserAllowsLinkUpdate = true;
111 pImpl->mpTempObjectContainer = nullptr;
114 EmbeddedObjectContainer::EmbeddedObjectContainer( const uno::Reference < embed::XStorage >& rStor, const uno::Reference < uno::XInterface >& xModel )
115 : pImpl(new EmbedImpl)
117 pImpl->mxStorage = rStor;
118 pImpl->mbOwnsStorage = false;
119 pImpl->mbUserAllowsLinkUpdate = true;
120 pImpl->mpTempObjectContainer = nullptr;
121 pImpl->m_xModel = xModel;
124 void EmbeddedObjectContainer::SwitchPersistence( const uno::Reference < embed::XStorage >& rStor )
126 ReleaseImageSubStorage();
128 if ( pImpl->mbOwnsStorage )
129 pImpl->mxStorage->dispose();
131 pImpl->mxStorage = rStor;
132 pImpl->mbOwnsStorage = false;
135 bool EmbeddedObjectContainer::CommitImageSubStorage()
137 if ( pImpl->mxImageStorage.is() )
141 bool bReadOnlyMode = true;
142 uno::Reference < beans::XPropertySet > xSet(pImpl->mxImageStorage,uno::UNO_QUERY);
143 if ( xSet.is() )
145 // get the open mode from the parent storage
146 sal_Int32 nMode = 0;
147 uno::Any aAny = xSet->getPropertyValue("OpenMode");
148 if ( aAny >>= nMode )
149 bReadOnlyMode = !(nMode & embed::ElementModes::WRITE );
150 } // if ( xSet.is() )
151 if ( !bReadOnlyMode )
153 uno::Reference< embed::XTransactedObject > xTransact( pImpl->mxImageStorage, uno::UNO_QUERY_THROW );
154 xTransact->commit();
157 catch (const uno::Exception&)
159 return false;
163 return true;
166 void EmbeddedObjectContainer::ReleaseImageSubStorage()
168 CommitImageSubStorage();
170 if ( pImpl->mxImageStorage.is() )
174 pImpl->mxImageStorage->dispose();
175 pImpl->mxImageStorage.clear();
177 catch (const uno::Exception&)
179 SAL_WARN( "comphelper.container", "Problems releasing image substorage!" );
184 EmbeddedObjectContainer::~EmbeddedObjectContainer()
186 ReleaseImageSubStorage();
188 if ( pImpl->mbOwnsStorage )
189 pImpl->mxStorage->dispose();
191 delete pImpl->mpTempObjectContainer;
194 void EmbeddedObjectContainer::CloseEmbeddedObjects()
196 for( const auto& rObj : pImpl->maNameToObjectMap )
198 uno::Reference < util::XCloseable > xClose( rObj.second, uno::UNO_QUERY );
199 if( xClose.is() )
203 xClose->close( true );
205 catch (const uno::Exception&)
212 OUString EmbeddedObjectContainer::CreateUniqueObjectName()
214 OUString aStr;
215 sal_Int32 i=1;
218 aStr = "Object " + OUString::number( i++ );
220 while( HasEmbeddedObject( aStr ) );
221 // TODO/LATER: should we consider deleted objects?
223 return aStr;
226 uno::Sequence < OUString > EmbeddedObjectContainer::GetObjectNames() const
228 return comphelper::mapKeysToSequence(pImpl->maNameToObjectMap);
231 bool EmbeddedObjectContainer::HasEmbeddedObjects() const
233 return !pImpl->maNameToObjectMap.empty();
236 bool EmbeddedObjectContainer::HasEmbeddedObject( const OUString& rName )
238 auto aIt = pImpl->maNameToObjectMap.find( rName );
239 if (aIt != pImpl->maNameToObjectMap.end())
240 return true;
241 if (!pImpl->mxStorage.is())
242 return false;
243 return pImpl->mxStorage->hasByName(rName);
246 bool EmbeddedObjectContainer::HasEmbeddedObject( const uno::Reference < embed::XEmbeddedObject >& xObj ) const
248 return pImpl->maObjectToNameMap.find(xObj) != pImpl->maObjectToNameMap.end();
251 bool EmbeddedObjectContainer::HasInstantiatedEmbeddedObject( const OUString& rName )
253 // allows to detect whether the object was already instantiated
254 // currently the filter instantiate it on loading, so this method allows
255 // to avoid objects pointing to the same persistence
256 auto aIt = pImpl->maNameToObjectMap.find( rName );
257 return ( aIt != pImpl->maNameToObjectMap.end() );
260 OUString EmbeddedObjectContainer::GetEmbeddedObjectName( const css::uno::Reference < css::embed::XEmbeddedObject >& xObj ) const
262 auto it = pImpl->maObjectToNameMap.find(xObj);
263 if (it == pImpl->maObjectToNameMap.end())
265 SAL_WARN( "comphelper.container", "Unknown object!" );
266 return OUString();
268 return it->second;
271 uno::Reference< embed::XEmbeddedObject>
272 EmbeddedObjectContainer::GetEmbeddedObject(
273 const OUString& rName, OUString const*const pBaseURL)
275 SAL_WARN_IF( rName.isEmpty(), "comphelper.container", "Empty object name!");
277 uno::Reference < embed::XEmbeddedObject > xObj;
278 auto aIt = pImpl->maNameToObjectMap.find( rName );
280 #if OSL_DEBUG_LEVEL > 1
281 uno::Reference < container::XNameAccess > xAccess( pImpl->mxStorage, uno::UNO_QUERY );
282 uno::Sequence< OUString> aSeq = xAccess->getElementNames();
283 const OUString* pIter = aSeq.getConstArray();
284 const OUString* pEnd = pIter + aSeq.getLength();
285 for(;pIter != pEnd;++pIter)
287 (void)*pIter;
289 OSL_ENSURE( aIt != pImpl->maNameToObjectMap.end() || xAccess->hasByName(rName), "Could not return object!" );
290 #endif
292 // check if object was already created
293 if ( aIt != pImpl->maNameToObjectMap.end() )
294 xObj = (*aIt).second;
295 else
296 xObj = Get_Impl(rName, uno::Reference<embed::XEmbeddedObject>(), pBaseURL);
298 return xObj;
301 uno::Reference<embed::XEmbeddedObject> EmbeddedObjectContainer::Get_Impl(
302 const OUString& rName,
303 const uno::Reference<embed::XEmbeddedObject>& xCopy,
304 OUString const*const pBaseURL)
306 uno::Reference < embed::XEmbeddedObject > xObj;
309 // create the object from the storage
310 uno::Reference < beans::XPropertySet > xSet( pImpl->mxStorage, uno::UNO_QUERY );
311 bool bReadOnlyMode = true;
312 if ( xSet.is() )
314 // get the open mode from the parent storage
315 sal_Int32 nMode = 0;
316 uno::Any aAny = xSet->getPropertyValue("OpenMode");
317 if ( aAny >>= nMode )
318 bReadOnlyMode = !(nMode & embed::ElementModes::WRITE );
321 // object was not added until now - should happen only by calling this method from "inside"
322 //TODO/LATER: it would be good to detect an error when an object should be created already, but isn't (not an "inside" call)
323 uno::Reference < embed::XEmbeddedObjectCreator > xFactory = embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() );
324 uno::Sequence< beans::PropertyValue > aObjDescr(1 + (xCopy.is() ? 1 : 0) + (pBaseURL ? 1 : 0));
325 aObjDescr[0].Name = "Parent";
326 aObjDescr[0].Value <<= pImpl->m_xModel.get();
327 sal_Int32 i = 1;
328 if (pBaseURL)
330 aObjDescr[i].Name = "DefaultParentBaseURL";
331 aObjDescr[i].Value <<= *pBaseURL;
332 ++i;
334 if ( xCopy.is() )
336 aObjDescr[i].Name = "CloneFrom";
337 aObjDescr[i].Value <<= xCopy;
340 uno::Sequence< beans::PropertyValue > aMediaDescr( 1 );
341 aMediaDescr[0].Name = "ReadOnly";
342 aMediaDescr[0].Value <<= bReadOnlyMode;
343 xObj.set( xFactory->createInstanceInitFromEntry(
344 pImpl->mxStorage, rName,
345 aMediaDescr, aObjDescr ), uno::UNO_QUERY );
347 // insert object into my list
348 AddEmbeddedObject( xObj, rName );
350 catch (uno::Exception const& e)
352 SAL_WARN("comphelper.container", "EmbeddedObjectContainer::Get_Impl: exception caught: " << e);
355 return xObj;
358 uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::CreateEmbeddedObject( const uno::Sequence < sal_Int8 >& rClassId,
359 const uno::Sequence < beans::PropertyValue >& rArgs, OUString& rNewName, OUString const* pBaseURL )
361 if ( rNewName.isEmpty() )
362 rNewName = CreateUniqueObjectName();
364 SAL_WARN_IF( HasEmbeddedObject(rNewName), "comphelper.container", "Object to create already exists!");
366 // create object from classid by inserting it into storage
367 uno::Reference < embed::XEmbeddedObject > xObj;
370 uno::Reference < embed::XEmbeddedObjectCreator > xFactory = embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() );
372 const size_t nExtraArgs = pBaseURL ? 2 : 1;
373 uno::Sequence< beans::PropertyValue > aObjDescr( rArgs.getLength() + nExtraArgs );
374 aObjDescr[0].Name = "Parent";
375 aObjDescr[0].Value <<= pImpl->m_xModel.get();
376 if (pBaseURL)
378 aObjDescr[1].Name = "DefaultParentBaseURL";
379 aObjDescr[1].Value <<= *pBaseURL;
381 std::copy( rArgs.begin(), rArgs.end(), aObjDescr.getArray() + nExtraArgs );
382 xObj.set( xFactory->createInstanceInitNew(
383 rClassId, OUString(), pImpl->mxStorage, rNewName,
384 aObjDescr ), uno::UNO_QUERY );
386 AddEmbeddedObject( xObj, rNewName );
388 OSL_ENSURE( !xObj.is() || xObj->getCurrentState() != embed::EmbedStates::LOADED,
389 "A freshly create object should be running always!" );
391 catch (uno::Exception const& e)
393 SAL_WARN("comphelper.container", "EmbeddedObjectContainer::CreateEmbeddedObject: exception caught: " << e);
396 return xObj;
399 uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::CreateEmbeddedObject( const uno::Sequence < sal_Int8 >& rClassId, OUString& rNewName, OUString const* pBaseURL )
401 return CreateEmbeddedObject( rClassId, uno::Sequence < beans::PropertyValue >(), rNewName, pBaseURL );
404 void EmbeddedObjectContainer::AddEmbeddedObject( const css::uno::Reference < css::embed::XEmbeddedObject >& xObj, const OUString& rName )
406 #if OSL_DEBUG_LEVEL > 1
407 SAL_WARN_IF( rName.isEmpty(), "comphelper.container", "Added object doesn't have a name!");
408 uno::Reference < container::XNameAccess > xAccess( pImpl->mxStorage, uno::UNO_QUERY );
409 uno::Reference < embed::XEmbedPersist > xEmb( xObj, uno::UNO_QUERY );
410 uno::Reference < embed::XLinkageSupport > xLink( xEmb, uno::UNO_QUERY );
411 // if the object has a persistence and the object is not a link than it must have persistence entry in the storage
412 OSL_ENSURE( !( xEmb.is() && ( !xLink.is() || !xLink->isLink() ) ) || xAccess->hasByName(rName),
413 "Added element not in storage!" );
414 #endif
416 // remember object - it needs to be in storage already
417 auto aIt = pImpl->maNameToObjectMap.find( rName );
418 OSL_ENSURE( aIt == pImpl->maNameToObjectMap.end(), "Element already inserted!" );
419 pImpl->maNameToObjectMap[ rName ] = xObj;
420 pImpl->maObjectToNameMap[ xObj ] = rName;
421 uno::Reference < container::XChild > xChild( xObj, uno::UNO_QUERY );
422 if ( xChild.is() && xChild->getParent() != pImpl->m_xModel.get() )
423 xChild->setParent( pImpl->m_xModel.get() );
425 // look for object in temporary container
426 if ( pImpl->mpTempObjectContainer )
428 auto& rObjectContainer = pImpl->mpTempObjectContainer->pImpl->maNameToObjectMap;
429 auto aIter = std::find_if(rObjectContainer.begin(), rObjectContainer.end(),
430 [&xObj](const EmbeddedObjectContainerNameMap::value_type& rEntry) { return rEntry.second == xObj; });
431 if (aIter != rObjectContainer.end())
433 // copy replacement image from temporary container (if there is any)
434 OUString aTempName = aIter->first;
435 OUString aMediaType;
436 uno::Reference < io::XInputStream > xStream = pImpl->mpTempObjectContainer->GetGraphicStream( xObj, &aMediaType );
437 if ( xStream.is() )
439 InsertGraphicStream( xStream, rName, aMediaType );
440 xStream = nullptr;
441 pImpl->mpTempObjectContainer->RemoveGraphicStream( aTempName );
444 // remove object from storage of temporary container
445 uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
446 if ( xPersist.is() )
450 pImpl->mpTempObjectContainer->pImpl->mxStorage->removeElement( aTempName );
452 catch (const uno::Exception&)
457 // temp. container needs to forget the object
458 pImpl->mpTempObjectContainer->pImpl->maObjectToNameMap.erase( aIter->second );
459 pImpl->mpTempObjectContainer->pImpl->maNameToObjectMap.erase( aIter );
464 bool EmbeddedObjectContainer::StoreEmbeddedObject(
465 const uno::Reference < embed::XEmbeddedObject >& xObj, OUString& rName, bool bCopy,
466 const OUString& rSrcShellID, const OUString& rDestShellID )
468 uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
469 if ( rName.isEmpty() )
470 rName = CreateUniqueObjectName();
472 #if OSL_DEBUG_LEVEL > 1
473 uno::Reference < container::XNameAccess > xAccess( pImpl->mxStorage, uno::UNO_QUERY );
474 OSL_ENSURE( !xPersist.is() || !xAccess->hasByName(rName), "Inserting element already present in storage!" );
475 OSL_ENSURE( xPersist.is() || xObj->getCurrentState() == embed::EmbedStates::RUNNING, "Non persistent object inserted!");
476 #endif
478 // insert objects' storage into the container storage (if object has one)
481 if ( xPersist.is() )
483 uno::Sequence < beans::PropertyValue > aSeq;
484 if ( bCopy )
486 auto aObjArgs(::comphelper::InitPropertySequence({
487 { "SourceShellID", uno::Any(rSrcShellID) },
488 { "DestinationShellID", uno::Any(rDestShellID) }
489 }));
490 xPersist->storeToEntry(pImpl->mxStorage, rName, aSeq, aObjArgs);
492 else
494 //TODO/LATER: possible optimization, don't store immediately
495 //xPersist->setPersistentEntry( pImpl->mxStorage, rName, embed::EntryInitModes::ENTRY_NO_INIT, aSeq, aSeq );
496 xPersist->storeAsEntry( pImpl->mxStorage, rName, aSeq, aSeq );
497 xPersist->saveCompleted( true );
501 catch (uno::Exception const& e)
503 SAL_WARN("comphelper.container", "EmbeddedObjectContainer::StoreEmbeddedObject: exception caught: " << e);
504 // TODO/LATER: better error recovery should keep storage intact
505 return false;
508 return true;
511 bool EmbeddedObjectContainer::InsertEmbeddedObject( const uno::Reference < embed::XEmbeddedObject >& xObj, OUString& rName )
513 // store it into the container storage
514 if (StoreEmbeddedObject(xObj, rName, false, OUString(), OUString()))
516 // remember object
517 AddEmbeddedObject( xObj, rName );
518 return true;
520 else
521 return false;
524 uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::InsertEmbeddedObject( const uno::Reference < io::XInputStream >& xStm, OUString& rNewName )
526 if ( rNewName.isEmpty() )
527 rNewName = CreateUniqueObjectName();
529 // store it into the container storage
530 bool bIsStorage = false;
533 // first try storage persistence
534 uno::Reference < embed::XStorage > xStore = ::comphelper::OStorageHelper::GetStorageFromInputStream( xStm );
536 // storage was created from stream successfully
537 bIsStorage = true;
539 uno::Reference < embed::XStorage > xNewStore = pImpl->mxStorage->openStorageElement( rNewName, embed::ElementModes::READWRITE );
540 xStore->copyToStorage( xNewStore );
542 catch (const uno::Exception&)
544 if ( bIsStorage )
545 // it is storage persistence, but opening of new substorage or copying to it failed
546 return uno::Reference < embed::XEmbeddedObject >();
548 // stream didn't contain a storage, now try stream persistence
551 uno::Reference < io::XStream > xNewStream = pImpl->mxStorage->openStreamElement( rNewName, embed::ElementModes::READWRITE );
552 ::comphelper::OStorageHelper::CopyInputToOutput( xStm, xNewStream->getOutputStream() );
554 // No mediatype is provided so the default for OLE objects value is used
555 // it is correct so for now, but what if somebody introduces a new stream based embedded object?
556 // Probably introducing of such an object must be restricted ( a storage must be used! ).
557 uno::Reference< beans::XPropertySet > xProps( xNewStream, uno::UNO_QUERY_THROW );
558 xProps->setPropertyValue("MediaType",
559 uno::Any( OUString( "application/vnd.sun.star.oleobject" ) ) );
561 catch (uno::Exception const& e)
563 // complete disaster!
564 SAL_WARN("comphelper.container", "EmbeddedObjectContainer::InsertEmbeddedObject: exception caught: " << e);
565 return uno::Reference < embed::XEmbeddedObject >();
569 // stream was copied into the container storage in either way, now try to open something form it
570 uno::Reference < embed::XEmbeddedObject > xRet = GetEmbeddedObject( rNewName );
573 if ( !xRet.is() )
574 // no object could be created, so withdraw insertion
575 pImpl->mxStorage->removeElement( rNewName );
577 catch (const uno::Exception&)
581 return xRet;
584 uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::InsertEmbeddedObject( const css::uno::Sequence < css::beans::PropertyValue >& aMedium, OUString& rNewName, OUString const* pBaseURL )
586 if ( rNewName.isEmpty() )
587 rNewName = CreateUniqueObjectName();
589 uno::Reference < embed::XEmbeddedObject > xObj;
592 uno::Reference < embed::XEmbeddedObjectCreator > xFactory = embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() );
593 uno::Sequence< beans::PropertyValue > aObjDescr(pBaseURL ? 2 : 1);
594 aObjDescr[0].Name = "Parent";
595 aObjDescr[0].Value <<= pImpl->m_xModel.get();
596 if (pBaseURL)
598 aObjDescr[1].Name = "DefaultParentBaseURL";
599 aObjDescr[1].Value <<= *pBaseURL;
601 xObj.set( xFactory->createInstanceInitFromMediaDescriptor(
602 pImpl->mxStorage, rNewName, aMedium, aObjDescr ), uno::UNO_QUERY );
603 uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
605 OSL_ENSURE( !xObj.is() || xObj->getCurrentState() != embed::EmbedStates::LOADED,
606 "A freshly create object should be running always!" );
608 // possible optimization: store later!
609 if ( xPersist.is())
610 xPersist->storeOwn();
612 AddEmbeddedObject( xObj, rNewName );
614 catch (const uno::Exception&)
618 return xObj;
621 uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::InsertEmbeddedLink( const css::uno::Sequence < css::beans::PropertyValue >& aMedium, OUString& rNewName )
623 if ( rNewName.isEmpty() )
624 rNewName = CreateUniqueObjectName();
626 uno::Reference < embed::XEmbeddedObject > xObj;
629 uno::Reference < embed::XEmbeddedObjectCreator > xFactory = embed::EmbeddedObjectCreator::create(::comphelper::getProcessComponentContext());
630 uno::Sequence< beans::PropertyValue > aObjDescr( 1 );
631 aObjDescr[0].Name = "Parent";
632 aObjDescr[0].Value <<= pImpl->m_xModel.get();
633 xObj.set( xFactory->createInstanceLink( pImpl->mxStorage, rNewName, aMedium, aObjDescr ), uno::UNO_QUERY );
635 uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
637 OSL_ENSURE( !xObj.is() || xObj->getCurrentState() != embed::EmbedStates::LOADED,
638 "A freshly create object should be running always!" );
640 // possible optimization: store later!
641 if ( xPersist.is())
642 xPersist->storeOwn();
644 AddEmbeddedObject( xObj, rNewName );
646 catch (uno::Exception const& e)
648 SAL_WARN("comphelper.container", "EmbeddedObjectContainer::InsertEmbeddedLink: "
649 "exception caught: " << e);
652 return xObj;
655 bool EmbeddedObjectContainer::TryToCopyGraphReplacement( EmbeddedObjectContainer& rSrc,
656 const OUString& aOrigName,
657 const OUString& aTargetName )
659 bool bResult = false;
661 if ( ( &rSrc != this || aOrigName != aTargetName ) && !aOrigName.isEmpty() && !aTargetName.isEmpty() )
663 OUString aMediaType;
664 uno::Reference < io::XInputStream > xGrStream = rSrc.GetGraphicStream( aOrigName, &aMediaType );
665 if ( xGrStream.is() )
666 bResult = InsertGraphicStream( xGrStream, aTargetName, aMediaType );
669 return bResult;
672 uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::CopyAndGetEmbeddedObject(
673 EmbeddedObjectContainer& rSrc, const uno::Reference <embed::XEmbeddedObject>& xObj, OUString& rName,
674 const OUString& rSrcShellID, const OUString& rDestShellID )
676 uno::Reference< embed::XEmbeddedObject > xResult;
678 // TODO/LATER: For now only objects that implement XEmbedPersist have a replacement image, it might change in future
679 // do an incompatible change so that object name is provided in all the move and copy methods
680 OUString aOrigName;
683 uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY_THROW );
684 aOrigName = xPersist->getEntryName();
686 catch (const uno::Exception&)
690 if ( rName.isEmpty() )
691 rName = CreateUniqueObjectName();
693 // objects without persistence are not really stored by the method
694 if (xObj.is() && StoreEmbeddedObject(xObj, rName, true, rSrcShellID, rDestShellID))
696 SAL_INFO_IF(rDestShellID.isEmpty(), "comphelper.container",
697 "SfxObjectShell with no base URL?"); // every shell has a base URL, except the clipboard SwDocShell
698 xResult = Get_Impl(rName, xObj, &rDestShellID);
699 if ( !xResult.is() )
701 // this is a case when object has no real persistence
702 // in such cases a new object should be explicitly created and initialized with the data of the old one
705 uno::Reference< embed::XLinkageSupport > xOrigLinkage( xObj, uno::UNO_QUERY );
706 if ( xOrigLinkage.is() && xOrigLinkage->isLink() )
708 // this is an OOo link, it has no persistence
709 OUString aURL = xOrigLinkage->getLinkURL();
710 if ( aURL.isEmpty() )
711 throw uno::RuntimeException();
713 // create new linked object from the URL the link is based on
714 uno::Reference < embed::XEmbeddedObjectCreator > xCreator =
715 embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() );
717 uno::Sequence< beans::PropertyValue > aMediaDescr( 1 );
718 aMediaDescr[0].Name = "URL";
719 aMediaDescr[0].Value <<= aURL;
720 uno::Sequence< beans::PropertyValue > aObjDescr( 1 );
721 aObjDescr[0].Name = "Parent";
722 aObjDescr[0].Value <<= 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( 1 );
744 aObjDescr[0].Name = "Parent";
745 aObjDescr[0].Value <<= pImpl->m_xModel.get();
746 xResult.set(xCreator->createInstanceInitNew(
747 xObj->getClassID(),
748 xObj->getClassName(),
749 pImpl->mxStorage,
750 rName,
751 aObjDescr ),
752 uno::UNO_QUERY_THROW );
754 if ( xResult->getCurrentState() == embed::EmbedStates::LOADED )
755 xResult->changeState( embed::EmbedStates::RUNNING );
757 uno::Reference< beans::XPropertySet > xTargetProps( xResult->getComponent(), uno::UNO_QUERY_THROW );
759 // copy all the properties from xOrigProps to xTargetProps
760 uno::Reference< beans::XPropertySetInfo > xOrigInfo = xOrigProps->getPropertySetInfo();
761 if ( !xOrigInfo.is() )
762 throw uno::RuntimeException();
764 uno::Sequence< beans::Property > aPropertiesList = xOrigInfo->getProperties();
765 for ( sal_Int32 nInd = 0; nInd < aPropertiesList.getLength(); nInd++ )
769 xTargetProps->setPropertyValue(
770 aPropertiesList[nInd].Name,
771 xOrigProps->getPropertyValue( aPropertiesList[nInd].Name ) );
773 catch (const beans::PropertyVetoException&)
775 // impossibility to copy readonly property is not treated as an error for now
776 // but the assertion is helpful to detect such scenarios and review them
777 SAL_WARN( "comphelper.container", "Could not copy readonly property!" );
782 if ( xResult.is() )
783 AddEmbeddedObject( xResult, rName );
785 catch (const uno::Exception&)
787 if ( xResult.is() )
791 xResult->close( true );
793 catch (const uno::Exception&)
796 xResult.clear();
802 SAL_WARN_IF( !xResult.is(), "comphelper.container", "Can not copy embedded object that has no persistence!" );
804 if ( xResult.is() )
806 // the object is successfully copied, try to copy graphical replacement
807 if ( !aOrigName.isEmpty() )
808 TryToCopyGraphReplacement( rSrc, aOrigName, rName );
810 // the object might need the size to be set
813 if ( xResult->getStatus( embed::Aspects::MSOLE_CONTENT ) & embed::EmbedMisc::EMBED_NEEDSSIZEONLOAD )
814 xResult->setVisualAreaSize( embed::Aspects::MSOLE_CONTENT,
815 xObj->getVisualAreaSize( embed::Aspects::MSOLE_CONTENT ) );
817 catch (const uno::Exception&)
822 return xResult;
825 // #i119941, bKeepToTempStorage: use to specify whether store the removed object to temporary storage+
826 void EmbeddedObjectContainer::RemoveEmbeddedObject( const OUString& rName, bool bKeepToTempStorage )
828 uno::Reference < embed::XEmbeddedObject > xObj = GetEmbeddedObject( rName );
829 if ( xObj.is() )
830 RemoveEmbeddedObject( xObj, bKeepToTempStorage );
833 bool EmbeddedObjectContainer::MoveEmbeddedObject( const OUString& rName, EmbeddedObjectContainer& rCnt )
835 // find object entry
836 auto aIt2 = rCnt.pImpl->maNameToObjectMap.find( rName );
837 OSL_ENSURE( aIt2 == rCnt.pImpl->maNameToObjectMap.end(), "Object does already exist in target container!" );
839 if ( aIt2 != rCnt.pImpl->maNameToObjectMap.end() )
840 return false;
842 uno::Reference < embed::XEmbeddedObject > xObj;
843 auto aIt = pImpl->maNameToObjectMap.find( rName );
844 if ( aIt != pImpl->maNameToObjectMap.end() )
846 xObj = (*aIt).second;
849 if ( xObj.is() )
851 // move object
852 OUString aName( rName );
853 rCnt.InsertEmbeddedObject( xObj, aName );
854 pImpl->maObjectToNameMap.erase( aIt->second );
855 pImpl->maNameToObjectMap.erase( aIt );
856 uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
857 if ( xPersist.is() )
858 pImpl->mxStorage->removeElement( rName );
860 else
862 // copy storages; object *must* have persistence!
863 uno::Reference < embed::XStorage > xOld = pImpl->mxStorage->openStorageElement( rName, embed::ElementModes::READ );
864 uno::Reference < embed::XStorage > xNew = rCnt.pImpl->mxStorage->openStorageElement( rName, embed::ElementModes::READWRITE );
865 xOld->copyToStorage( xNew );
868 rCnt.TryToCopyGraphReplacement( *this, rName, rName );
869 // RemoveGraphicStream( rName );
871 return true;
873 catch (const uno::Exception&)
875 SAL_WARN( "comphelper.container", "Could not move object!");
876 return false;
880 else
881 SAL_WARN( "comphelper.container", "Unknown object!");
882 return false;
885 // #i119941, bKeepToTempStorage: use to specify whether store the removed object to temporary storage+
886 bool EmbeddedObjectContainer::RemoveEmbeddedObject( const uno::Reference < embed::XEmbeddedObject >& xObj, bool bKeepToTempStorage )
888 uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
889 OUString aName;
890 if ( xPersist.is() )
891 aName = xPersist->getEntryName();
893 #if OSL_DEBUG_LEVEL > 1
894 uno::Reference < container::XNameAccess > xAccess( pImpl->mxStorage, uno::UNO_QUERY );
895 uno::Reference < embed::XLinkageSupport > xLink( xPersist, uno::UNO_QUERY );
896 sal_Bool bIsNotEmbedded = !xPersist.is() || ( xLink.is() && xLink->isLink() );
898 // if the object has a persistence and the object is not a link than it must have persistence entry in the storage
899 OSL_ENSURE( bIsNotEmbedded || xAccess->hasByName(aName), "Removing element not present in storage!" );
900 #endif
902 // somebody still needs the object, so we must assign a temporary persistence
905 if ( xPersist.is() && bKeepToTempStorage ) // #i119941
908 if ( !pImpl->mpTempObjectContainer )
910 pImpl->mpTempObjectContainer = new EmbeddedObjectContainer();
913 // TODO/LATER: in future probably the temporary container will have two storages ( of two formats )
914 // the media type will be provided with object insertion
915 OUString aOrigStorMediaType;
916 uno::Reference< beans::XPropertySet > xStorProps( pImpl->mxStorage, uno::UNO_QUERY_THROW );
917 static const OUStringLiteral s_sMediaType("MediaType");
918 xStorProps->getPropertyValue( s_sMediaType ) >>= aOrigStorMediaType;
920 SAL_WARN_IF( aOrigStorMediaType.isEmpty(), "comphelper.container", "No valuable media type in the storage!" );
922 uno::Reference< beans::XPropertySet > xTargetStorProps(
923 pImpl->mpTempObjectContainer->pImpl->mxStorage,
924 uno::UNO_QUERY_THROW );
925 xTargetStorProps->setPropertyValue( s_sMediaType,uno::Any( aOrigStorMediaType ) );
927 catch (const uno::Exception&)
929 SAL_WARN( "comphelper.container", "Can not set the new media type to a storage!" );
933 OUString aTempName, aMediaType;
934 pImpl->mpTempObjectContainer->InsertEmbeddedObject( xObj, aTempName );
936 uno::Reference < io::XInputStream > xStream = GetGraphicStream( xObj, &aMediaType );
937 if ( xStream.is() )
938 pImpl->mpTempObjectContainer->InsertGraphicStream( xStream, aTempName, aMediaType );
940 // object is stored, so at least it can be set to loaded state
941 xObj->changeState( embed::EmbedStates::LOADED );
943 else
944 // objects without persistence need to stay in running state if they shall not be closed
945 xObj->changeState( embed::EmbedStates::RUNNING );
947 catch (const uno::Exception&)
949 return false;
952 auto aIter = std::find_if(pImpl->maNameToObjectMap.begin(), pImpl->maNameToObjectMap.end(),
953 [&xObj](const EmbeddedObjectContainerNameMap::value_type& rEntry) { return rEntry.second == xObj; });
954 if (aIter != pImpl->maNameToObjectMap.end())
956 pImpl->maObjectToNameMap.erase( aIter->second );
957 pImpl->maNameToObjectMap.erase( aIter );
958 uno::Reference < container::XChild > xChild( xObj, uno::UNO_QUERY );
959 if ( xChild.is() )
960 xChild->setParent( uno::Reference < uno::XInterface >() );
962 else
963 SAL_WARN( "comphelper.container", "Object not found for removal!" );
965 if ( xPersist.is() && bKeepToTempStorage ) // #i119941#
967 // remove replacement image (if there is one)
968 RemoveGraphicStream( aName );
970 // now it's time to remove the storage from the container storage
973 #if OSL_DEBUG_LEVEL > 1
974 // if the object has a persistence and the object is not a link than it must have persistence entry in storage
975 OSL_ENSURE( bIsNotEmbedded || pImpl->mxStorage->hasByName( aName ), "The object has no persistence entry in the storage!" );
976 #endif
977 if ( xPersist.is() && pImpl->mxStorage->hasByName( aName ) )
978 pImpl->mxStorage->removeElement( aName );
980 catch (const uno::Exception&)
982 SAL_WARN( "comphelper.container", "Failed to remove object from storage!" );
983 return false;
987 return true;
990 void EmbeddedObjectContainer::CloseEmbeddedObject( const uno::Reference < embed::XEmbeddedObject >& xObj )
992 // disconnect the object from the container and close it if possible
994 auto aIter = std::find_if(pImpl->maNameToObjectMap.begin(), pImpl->maNameToObjectMap.end(),
995 [&xObj](const EmbeddedObjectContainerNameMap::value_type& rEntry) { return rEntry.second == xObj; });
996 if (aIter != pImpl->maNameToObjectMap.end())
998 pImpl->maObjectToNameMap.erase( aIter->second );
999 pImpl->maNameToObjectMap.erase( aIter );
1003 xObj->close( true );
1005 catch (const uno::Exception&)
1007 // it is no problem if the object is already closed
1008 // TODO/LATER: what if the object can not be closed?
1013 uno::Reference < io::XInputStream > EmbeddedObjectContainer::GetGraphicStream( const OUString& aName, OUString* pMediaType )
1015 uno::Reference < io::XInputStream > xStream;
1017 SAL_WARN_IF( aName.isEmpty(), "comphelper.container", "Retrieving graphic for unknown object!" );
1018 if ( !aName.isEmpty() )
1022 uno::Reference < embed::XStorage > xReplacements = pImpl->GetReplacements();
1023 uno::Reference < io::XStream > xGraphicStream = xReplacements->openStreamElement( aName, embed::ElementModes::READ );
1024 xStream = xGraphicStream->getInputStream();
1025 if ( pMediaType )
1027 uno::Reference < beans::XPropertySet > xSet( xStream, uno::UNO_QUERY );
1028 if ( xSet.is() )
1030 uno::Any aAny = xSet->getPropertyValue("MediaType");
1031 aAny >>= *pMediaType;
1035 catch (uno::Exception const& e)
1037 SAL_INFO("comphelper.container",
1038 "EmbeddedObjectContainer::GetGraphicStream(): " << e);
1042 return xStream;
1045 uno::Reference < io::XInputStream > EmbeddedObjectContainer::GetGraphicStream( const css::uno::Reference < css::embed::XEmbeddedObject >& xObj, OUString* pMediaType )
1047 // try to load it from the container storage
1048 return GetGraphicStream( GetEmbeddedObjectName( xObj ), pMediaType );
1051 bool EmbeddedObjectContainer::InsertGraphicStream( const css::uno::Reference < css::io::XInputStream >& rStream, const OUString& rObjectName, const OUString& rMediaType )
1055 uno::Reference < embed::XStorage > xReplacements = pImpl->GetReplacements();
1057 // store it into the subfolder
1058 uno::Reference < io::XOutputStream > xOutStream;
1059 uno::Reference < io::XStream > xGraphicStream = xReplacements->openStreamElement( rObjectName,
1060 embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE );
1061 xOutStream = xGraphicStream->getOutputStream();
1062 ::comphelper::OStorageHelper::CopyInputToOutput( rStream, xOutStream );
1063 xOutStream->flush();
1065 uno::Reference< beans::XPropertySet > xPropSet( xGraphicStream, uno::UNO_QUERY_THROW );
1067 xPropSet->setPropertyValue("UseCommonStoragePasswordEncryption",
1068 uno::Any( true ) );
1069 xPropSet->setPropertyValue("MediaType", uno::Any(rMediaType) );
1071 xPropSet->setPropertyValue("Compressed",
1072 uno::Any( true ) );
1074 catch (const uno::Exception&)
1076 return false;
1079 return true;
1082 bool EmbeddedObjectContainer::InsertGraphicStreamDirectly( const css::uno::Reference < css::io::XInputStream >& rStream, const OUString& rObjectName, const OUString& rMediaType )
1086 uno::Reference < embed::XStorage > xReplacement = pImpl->GetReplacements();
1087 uno::Reference < embed::XOptimizedStorage > xOptRepl( xReplacement, uno::UNO_QUERY_THROW );
1089 // store it into the subfolder
1090 uno::Sequence< beans::PropertyValue > aProps( 3 );
1091 aProps[0].Name = "MediaType";
1092 aProps[0].Value <<= rMediaType;
1093 aProps[1].Name = "UseCommonStoragePasswordEncryption";
1094 aProps[1].Value <<= true;
1095 aProps[2].Name = "Compressed";
1096 aProps[2].Value <<= true;
1098 if ( xReplacement->hasByName( rObjectName ) )
1099 xReplacement->removeElement( rObjectName );
1101 xOptRepl->insertStreamElementDirect( rObjectName, rStream, aProps );
1103 catch (const uno::Exception&)
1105 return false;
1108 return true;
1112 void EmbeddedObjectContainer::RemoveGraphicStream( const OUString& rObjectName )
1116 uno::Reference < embed::XStorage > xReplacements = pImpl->GetReplacements();
1117 xReplacements->removeElement( rObjectName );
1119 catch (const uno::Exception&)
1123 namespace {
1124 void InsertStreamIntoPicturesStorage_Impl( const uno::Reference< embed::XStorage >& xDocStor,
1125 const uno::Reference< io::XInputStream >& xInStream,
1126 const OUString& aStreamName )
1128 OSL_ENSURE( !aStreamName.isEmpty() && xInStream.is() && xDocStor.is(), "Misuse of the method!" );
1132 uno::Reference< embed::XStorage > xPictures = xDocStor->openStorageElement(
1133 "Pictures",
1134 embed::ElementModes::READWRITE );
1135 uno::Reference< io::XStream > xObjReplStr = xPictures->openStreamElement(
1136 aStreamName,
1137 embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE );
1138 uno::Reference< io::XOutputStream > xOutStream(
1139 xObjReplStr->getInputStream(), uno::UNO_QUERY_THROW );
1141 ::comphelper::OStorageHelper::CopyInputToOutput( xInStream, xOutStream );
1142 xOutStream->closeOutput();
1144 uno::Reference< embed::XTransactedObject > xTransact( xPictures, uno::UNO_QUERY );
1145 if ( xTransact.is() )
1146 xTransact->commit();
1148 catch (const uno::Exception&)
1150 SAL_WARN( "comphelper.container", "The images storage is not available!" );
1156 bool EmbeddedObjectContainer::StoreAsChildren(bool _bOasisFormat,bool _bCreateEmbedded,const uno::Reference < embed::XStorage >& _xStorage)
1158 bool bResult = false;
1161 comphelper::EmbeddedObjectContainer aCnt( _xStorage );
1162 const uno::Sequence < OUString > aNames = GetObjectNames();
1163 const OUString* pIter = aNames.getConstArray();
1164 const OUString* pEnd = pIter + aNames.getLength();
1165 for(;pIter != pEnd;++pIter)
1167 uno::Reference < embed::XEmbeddedObject > xObj = GetEmbeddedObject( *pIter );
1168 SAL_WARN_IF( !xObj.is(), "comphelper.container", "An empty entry in the embedded objects list!" );
1169 if ( xObj.is() )
1171 bool bSwitchBackToLoaded = false;
1172 uno::Reference< embed::XLinkageSupport > xLink( xObj, uno::UNO_QUERY );
1174 uno::Reference < io::XInputStream > xStream;
1175 OUString aMediaType;
1177 sal_Int32 nCurState = xObj->getCurrentState();
1178 if ( nCurState == embed::EmbedStates::LOADED || nCurState == embed::EmbedStates::RUNNING )
1180 // means that the object is not active
1181 // copy replacement image from old to new container
1182 xStream = GetGraphicStream( xObj, &aMediaType );
1185 if ( !xStream.is() && getUserAllowsLinkUpdate() )
1187 // the image must be regenerated
1188 // TODO/LATER: another aspect could be used
1189 if ( xObj->getCurrentState() == embed::EmbedStates::LOADED )
1190 bSwitchBackToLoaded = true;
1192 xStream = GetGraphicReplacementStream(
1193 embed::Aspects::MSOLE_CONTENT,
1194 xObj,
1195 &aMediaType );
1198 if ( _bOasisFormat || (xLink.is() && xLink->isLink()) )
1200 if ( xStream.is() )
1202 if ( _bOasisFormat )
1204 // if it is an embedded object or the optimized inserting fails the normal inserting should be done
1205 if ( _bCreateEmbedded
1206 || !aCnt.InsertGraphicStreamDirectly( xStream, *pIter, aMediaType ) )
1207 aCnt.InsertGraphicStream( xStream, *pIter, aMediaType );
1209 else
1211 // it is a linked object exported into SO7 format
1212 InsertStreamIntoPicturesStorage_Impl( _xStorage, xStream, *pIter );
1217 uno::Reference< embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
1218 if ( xPersist.is() )
1220 uno::Sequence< beans::PropertyValue > aArgs( _bOasisFormat ? 2 : 3 );
1221 aArgs[0].Name = "StoreVisualReplacement";
1222 aArgs[0].Value <<= !_bOasisFormat;
1224 // if it is an embedded object or the optimized inserting fails the normal inserting should be done
1225 aArgs[1].Name = "CanTryOptimization";
1226 aArgs[1].Value <<= !_bCreateEmbedded;
1227 if ( !_bOasisFormat )
1229 // if object has no cached replacement it will use this one
1230 aArgs[2].Name = "VisualReplacement";
1231 aArgs[2].Value <<= xStream;
1236 xPersist->storeAsEntry( _xStorage, xPersist->getEntryName(), uno::Sequence< beans::PropertyValue >(), aArgs );
1238 catch (const embed::WrongStateException&)
1240 SAL_WARN("comphelper.container", "failed to store '" << *pIter << "'");
1244 if ( bSwitchBackToLoaded )
1245 // switch back to loaded state; that way we have a minimum cache confusion
1246 xObj->changeState( embed::EmbedStates::LOADED );
1250 bResult = aCnt.CommitImageSubStorage();
1253 catch (const uno::Exception& e)
1255 // TODO/LATER: error handling
1256 bResult = false;
1257 SAL_WARN("comphelper.container", "failed. Message: " << e);
1260 // the old SO6 format does not store graphical replacements
1261 if ( !_bOasisFormat && bResult )
1265 // the substorage still can not be locked by the embedded object container
1266 OUString aObjReplElement( "ObjectReplacements" );
1267 if ( _xStorage->hasByName( aObjReplElement ) && _xStorage->isStorageElement( aObjReplElement ) )
1268 _xStorage->removeElement( aObjReplElement );
1270 catch (const uno::Exception&)
1272 // TODO/LATER: error handling;
1273 bResult = false;
1276 return bResult;
1279 bool EmbeddedObjectContainer::StoreChildren(bool _bOasisFormat,bool _bObjectsOnly)
1281 bool bResult = true;
1282 const uno::Sequence < OUString > aNames = GetObjectNames();
1283 const OUString* pIter = aNames.getConstArray();
1284 const OUString* pEnd = pIter + aNames.getLength();
1285 for(;pIter != pEnd;++pIter)
1287 uno::Reference < embed::XEmbeddedObject > xObj = GetEmbeddedObject( *pIter );
1288 SAL_WARN_IF( !xObj.is(), "comphelper.container", "An empty entry in the embedded objects list!" );
1289 if ( xObj.is() )
1291 sal_Int32 nCurState = xObj->getCurrentState();
1292 if ( _bOasisFormat && nCurState != embed::EmbedStates::LOADED && nCurState != embed::EmbedStates::RUNNING )
1294 // means that the object is active
1295 // the image must be regenerated
1296 OUString aMediaType;
1298 // TODO/LATER: another aspect could be used
1299 uno::Reference < io::XInputStream > xStream =
1300 GetGraphicReplacementStream(
1301 embed::Aspects::MSOLE_CONTENT,
1302 xObj,
1303 &aMediaType );
1304 if ( xStream.is() )
1306 if ( !InsertGraphicStreamDirectly( xStream, *pIter, aMediaType ) )
1307 InsertGraphicStream( xStream, *pIter, aMediaType );
1311 // TODO/LATER: currently the object by default does not cache replacement image
1312 // that means that if somebody loads SO7 document and store its objects using
1313 // this method the images might be lost.
1314 // Currently this method is only used on storing to alien formats, that means
1315 // that SO7 documents storing does not use it, and all other filters are
1316 // based on OASIS format. But if it changes the method must be fixed. The fix
1317 // must be done only on demand since it can affect performance.
1319 uno::Reference< embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
1320 if ( xPersist.is() )
1324 //TODO/LATER: only storing if changed!
1325 //xPersist->storeOwn(); //commented, i120168
1327 // begin:all charts will be persisted as xml format on disk when saving, which is time consuming.
1328 // '_bObjectsOnly' mean we are storing to alien formats.
1329 // 'isStorageElement' mean current object is NOT a MS OLE format. (may also include in future), i120168
1330 if (_bObjectsOnly && (nCurState == embed::EmbedStates::LOADED || nCurState == embed::EmbedStates::RUNNING)
1331 && (pImpl->mxStorage->isStorageElement( *pIter ) ))
1333 uno::Reference< util::XModifiable > xModifiable( xObj->getComponent(), uno::UNO_QUERY );
1334 if ( xModifiable.is() && xModifiable->isModified())
1336 xPersist->storeOwn();
1338 else
1340 //do nothing. Embedded model is not modified, no need to persist.
1343 else //the embedded object is in active status, always store back it.
1345 xPersist->storeOwn();
1347 //end i120168
1349 catch (const uno::Exception&)
1351 // TODO/LATER: error handling
1352 bResult = false;
1353 break;
1357 if ( !_bOasisFormat && !_bObjectsOnly )
1359 // copy replacement images for linked objects
1362 uno::Reference< embed::XLinkageSupport > xLink( xObj, uno::UNO_QUERY );
1363 if ( xLink.is() && xLink->isLink() )
1365 OUString aMediaType;
1366 uno::Reference < io::XInputStream > xInStream = GetGraphicStream( xObj, &aMediaType );
1367 if ( xInStream.is() )
1368 InsertStreamIntoPicturesStorage_Impl( pImpl->mxStorage, xInStream, *pIter );
1371 catch (const uno::Exception&)
1378 if ( bResult && _bOasisFormat )
1379 bResult = CommitImageSubStorage();
1381 if ( bResult && !_bObjectsOnly )
1385 ReleaseImageSubStorage();
1386 OUString aObjReplElement( "ObjectReplacements" );
1387 if ( !_bOasisFormat && pImpl->mxStorage->hasByName( aObjReplElement ) && pImpl->mxStorage->isStorageElement( aObjReplElement ) )
1388 pImpl->mxStorage->removeElement( aObjReplElement );
1390 catch (const uno::Exception&)
1392 // TODO/LATER: error handling
1393 bResult = false;
1396 return bResult;
1399 uno::Reference< io::XInputStream > EmbeddedObjectContainer::GetGraphicReplacementStream(
1400 sal_Int64 nViewAspect,
1401 const uno::Reference< embed::XEmbeddedObject >& xObj,
1402 OUString* pMediaType )
1404 uno::Reference< io::XInputStream > xInStream;
1405 if ( xObj.is() )
1409 // retrieving of the visual representation can switch object to running state
1410 embed::VisualRepresentation aRep = xObj->getPreferredVisualRepresentation( nViewAspect );
1411 if ( pMediaType )
1412 *pMediaType = aRep.Flavor.MimeType;
1414 uno::Sequence < sal_Int8 > aSeq;
1415 aRep.Data >>= aSeq;
1416 xInStream = new ::comphelper::SequenceInputStream( aSeq );
1418 catch (const uno::Exception&)
1423 return xInStream;
1426 bool EmbeddedObjectContainer::SetPersistentEntries(const uno::Reference< embed::XStorage >& _xStorage,bool _bClearModifedFlag)
1428 bool bError = false;
1429 const uno::Sequence < OUString > aNames = GetObjectNames();
1430 const OUString* pIter = aNames.getConstArray();
1431 const OUString* pEnd = pIter + aNames.getLength();
1432 for(;pIter != pEnd;++pIter)
1434 uno::Reference < embed::XEmbeddedObject > xObj = GetEmbeddedObject( *pIter );
1435 SAL_WARN_IF( !xObj.is(), "comphelper.container", "An empty entry in the embedded objects list!" );
1436 if ( xObj.is() )
1438 uno::Reference< embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
1439 if ( xPersist.is() )
1443 xPersist->setPersistentEntry( _xStorage,
1444 *pIter,
1445 embed::EntryInitModes::NO_INIT,
1446 uno::Sequence< beans::PropertyValue >(),
1447 uno::Sequence< beans::PropertyValue >() );
1450 catch (const uno::Exception&)
1452 // TODO/LATER: error handling
1453 bError = true;
1454 break;
1457 if ( _bClearModifedFlag )
1459 // if this method is used as part of SaveCompleted the object must stay unmodified after execution
1462 uno::Reference< util::XModifiable > xModif( xObj->getComponent(), uno::UNO_QUERY_THROW );
1463 if ( xModif->isModified() )
1464 xModif->setModified( false );
1466 catch (const uno::Exception&)
1472 return bError;
1475 bool EmbeddedObjectContainer::getUserAllowsLinkUpdate() const
1477 return pImpl->mbUserAllowsLinkUpdate;
1480 void EmbeddedObjectContainer::setUserAllowsLinkUpdate(bool bNew)
1482 if(pImpl->mbUserAllowsLinkUpdate != bNew)
1484 pImpl->mbUserAllowsLinkUpdate = bNew;
1490 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */