LibreOffice Module comphelper (master) 1
embeddedobjectcontainer.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
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>
38
47#include <sal/log.hxx>
48
49#include <algorithm>
50#include <unordered_map>
51
52
53using namespace ::com::sun::star;
54
55namespace comphelper {
56
57typedef std::unordered_map<OUString, uno::Reference<embed::XEmbeddedObject>> EmbeddedObjectContainerNameMap;
59{
60 // TODO/LATER: remove objects from temp. Container storage when object is disposed
62 // to speed up lookup by Reference
63 std::unordered_map<uno::Reference<embed::XEmbeddedObject>, OUString> maObjectToNameMap;
64 uno::Reference < embed::XStorage > mxStorage;
66 uno::Reference < embed::XStorage > mxImageStorage;
67 uno::WeakReference < uno::XInterface > m_xModel;
68
69 bool mbOwnsStorage : 1;
71
72 const uno::Reference < embed::XStorage >& GetReplacements();
73};
74
75const uno::Reference < embed::XStorage >& EmbedImpl::GetReplacements()
76{
77 if ( !mxImageStorage.is() )
78 {
79 try
80 {
81 mxImageStorage = mxStorage->openStorageElement(
82 "ObjectReplacements", embed::ElementModes::READWRITE );
83 }
84 catch (const uno::Exception&)
85 {
86 mxImageStorage = mxStorage->openStorageElement(
87 "ObjectReplacements", embed::ElementModes::READ );
88 }
89 }
90
91 if ( !mxImageStorage.is() )
92 throw io::IOException("No ObjectReplacements");
93
94 return mxImageStorage;
95}
96
98 : pImpl(new EmbedImpl)
99{
101 pImpl->mbOwnsStorage = true;
102 pImpl->mbUserAllowsLinkUpdate = true;
103 pImpl->mpTempObjectContainer = nullptr;
104}
105
106EmbeddedObjectContainer::EmbeddedObjectContainer( const uno::Reference < embed::XStorage >& rStor )
107 : pImpl(new EmbedImpl)
108{
109 pImpl->mxStorage = rStor;
110 pImpl->mbOwnsStorage = false;
111 pImpl->mbUserAllowsLinkUpdate = true;
112 pImpl->mpTempObjectContainer = nullptr;
113}
114
115EmbeddedObjectContainer::EmbeddedObjectContainer( const uno::Reference < embed::XStorage >& rStor, const uno::Reference < uno::XInterface >& xModel )
116 : pImpl(new EmbedImpl)
117{
118 pImpl->mxStorage = rStor;
119 pImpl->mbOwnsStorage = false;
120 pImpl->mbUserAllowsLinkUpdate = true;
121 pImpl->mpTempObjectContainer = nullptr;
122 pImpl->m_xModel = xModel;
123}
124
125void EmbeddedObjectContainer::SwitchPersistence( const uno::Reference < embed::XStorage >& rStor )
126{
128
129 if ( pImpl->mbOwnsStorage )
130 pImpl->mxStorage->dispose();
131
132 pImpl->mxStorage = rStor;
133 pImpl->mbOwnsStorage = false;
134}
135
137{
138 if ( !pImpl->mxImageStorage )
139 return true;
140
141 try
142 {
143 bool bReadOnlyMode = true;
144 uno::Reference < beans::XPropertySet > xSet(pImpl->mxImageStorage,uno::UNO_QUERY);
145 if ( xSet.is() )
146 {
147 // get the open mode from the parent storage
148 sal_Int32 nMode = 0;
149 uno::Any aAny = xSet->getPropertyValue("OpenMode");
150 if ( aAny >>= nMode )
151 bReadOnlyMode = !(nMode & embed::ElementModes::WRITE );
152 } // if ( xSet.is() )
153 if ( !bReadOnlyMode )
154 {
155 uno::Reference< embed::XTransactedObject > xTransact( pImpl->mxImageStorage, uno::UNO_QUERY_THROW );
156 xTransact->commit();
157 }
158 }
159 catch (const uno::Exception&)
160 {
161 return false;
162 }
163
164 return true;
165}
166
168{
170
171 if ( pImpl->mxImageStorage.is() )
172 {
173 try
174 {
175 pImpl->mxImageStorage->dispose();
176 pImpl->mxImageStorage.clear();
177 }
178 catch (const uno::Exception&)
179 {
180 SAL_WARN( "comphelper.container", "Problems releasing image substorage!" );
181 }
182 }
183}
184
186{
188
189 if ( pImpl->mbOwnsStorage )
190 pImpl->mxStorage->dispose();
191
192 delete pImpl->mpTempObjectContainer;
193}
194
196{
197 for( const auto& rObj : pImpl->maNameToObjectMap )
198 {
199 uno::Reference < util::XCloseable > const & xClose = rObj.second;
200 if( xClose.is() )
201 {
202 try
203 {
204 xClose->close( true );
205 }
206 catch (const uno::Exception&)
207 {
208 }
209 }
210 }
211}
212
214{
215 OUString aStr;
216 sal_Int32 i=1;
217 do
218 {
219 aStr = "Object " + OUString::number( i++ );
220 }
221 while( HasEmbeddedObject( aStr ) );
222 // TODO/LATER: should we consider deleted objects?
223
224 return aStr;
225}
226
227uno::Sequence < OUString > EmbeddedObjectContainer::GetObjectNames() const
228{
229 return comphelper::mapKeysToSequence(pImpl->maNameToObjectMap);
230}
231
233{
234 return !pImpl->maNameToObjectMap.empty();
235}
236
238{
239 auto aIt = pImpl->maNameToObjectMap.find( rName );
240 if (aIt != pImpl->maNameToObjectMap.end())
241 return true;
242 if (!pImpl->mxStorage.is())
243 return false;
244 return pImpl->mxStorage->hasByName(rName);
245}
246
247bool EmbeddedObjectContainer::HasEmbeddedObject( const uno::Reference < embed::XEmbeddedObject >& xObj ) const
248{
249 return pImpl->maObjectToNameMap.find(xObj) != pImpl->maObjectToNameMap.end();
250}
251
253{
254 // allows to detect whether the object was already instantiated
255 // currently the filter instantiate it on loading, so this method allows
256 // to avoid objects pointing to the same persistence
257 auto aIt = pImpl->maNameToObjectMap.find( rName );
258 return ( aIt != pImpl->maNameToObjectMap.end() );
259}
260
261OUString EmbeddedObjectContainer::GetEmbeddedObjectName( const css::uno::Reference < css::embed::XEmbeddedObject >& xObj ) const
262{
263 auto it = pImpl->maObjectToNameMap.find(xObj);
264 if (it == pImpl->maObjectToNameMap.end())
265 {
266 SAL_WARN( "comphelper.container", "Unknown object!" );
267 return OUString();
268 }
269 return it->second;
270}
271
272uno::Reference< embed::XEmbeddedObject>
274 const OUString& rName, OUString const*const pBaseURL)
275{
276 SAL_WARN_IF( rName.isEmpty(), "comphelper.container", "Empty object name!");
277
278 uno::Reference < embed::XEmbeddedObject > xObj;
279 auto aIt = pImpl->maNameToObjectMap.find( rName );
280
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)
287 {
288 (void)*pIter;
289 }
290 OSL_ENSURE( aIt != pImpl->maNameToObjectMap.end() || xAccess->hasByName(rName), "Could not return object!" );
291#endif
292
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);
298
299 return xObj;
300}
301
302uno::Reference<embed::XEmbeddedObject> EmbeddedObjectContainer::Get_Impl(
303 const OUString& rName,
304 const uno::Reference<embed::XEmbeddedObject>& xCopy,
305 OUString const*const pBaseURL)
306{
307 uno::Reference < embed::XEmbeddedObject > xObj;
308 try
309 {
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() )
314 {
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 );
320 }
321
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)
330 {
331 ++itObjDescr;
332 itObjDescr->Name = "DefaultParentBaseURL";
333 itObjDescr->Value <<= *pBaseURL;
334 }
335 if ( xCopy.is() )
336 {
337 ++itObjDescr;
338 itObjDescr->Name = "CloneFrom";
339 itObjDescr->Value <<= xCopy;
340 }
341
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 );
347
348 // insert object into my list
349 AddEmbeddedObject( xObj, rName );
350 }
351 catch (uno::Exception const& e)
352 {
353 SAL_WARN("comphelper.container", "EmbeddedObjectContainer::Get_Impl: exception caught: " << e);
354 }
355
356 return xObj;
357}
358
359uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::CreateEmbeddedObject( const uno::Sequence < sal_Int8 >& rClassId,
360 const uno::Sequence < beans::PropertyValue >& rArgs, OUString& rNewName, OUString const* pBaseURL )
361{
362 if ( rNewName.isEmpty() )
363 rNewName = CreateUniqueObjectName();
364
365 SAL_WARN_IF( HasEmbeddedObject(rNewName), "comphelper.container", "Object to create already exists!");
366
367 // create object from classid by inserting it into storage
368 uno::Reference < embed::XEmbeddedObject > xObj;
369 try
370 {
371 uno::Reference < embed::XEmbeddedObjectCreator > xFactory = embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() );
372
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)
379 {
380 pObjDescr[1].Name = "DefaultParentBaseURL";
381 pObjDescr[1].Value <<= *pBaseURL;
382 }
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 );
387
388 AddEmbeddedObject( xObj, rNewName );
389
390 OSL_ENSURE( !xObj.is() || xObj->getCurrentState() != embed::EmbedStates::LOADED,
391 "A freshly create object should be running always!" );
392 }
393 catch (uno::Exception const& e)
394 {
395 SAL_WARN("comphelper.container", "EmbeddedObjectContainer::CreateEmbeddedObject: exception caught: " << e);
396 }
397
398 return xObj;
399}
400
401uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::CreateEmbeddedObject( const uno::Sequence < sal_Int8 >& rClassId, OUString& rNewName, OUString const* pBaseURL )
402{
403 return CreateEmbeddedObject( rClassId, uno::Sequence < beans::PropertyValue >(), rNewName, pBaseURL );
404}
405
406void EmbeddedObjectContainer::AddEmbeddedObject( const css::uno::Reference < css::embed::XEmbeddedObject >& xObj, const OUString& rName )
407{
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
417
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() );
426
427 // look for object in temporary container
428 if ( !pImpl->mpTempObjectContainer )
429 return;
430
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;
436
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() )
442 {
443 InsertGraphicStream( xStream, rName, aMediaType );
444 xStream = nullptr;
445 pImpl->mpTempObjectContainer->RemoveGraphicStream( aTempName );
446 }
447
448 // remove object from storage of temporary container
449 uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
450 if ( xPersist.is() )
451 {
452 try
453 {
454 pImpl->mpTempObjectContainer->pImpl->mxStorage->removeElement( aTempName );
455 }
456 catch (const uno::Exception&)
457 {
458 }
459 }
460
461 // temp. container needs to forget the object
462 pImpl->mpTempObjectContainer->pImpl->maObjectToNameMap.erase( aIter->second );
463 pImpl->mpTempObjectContainer->pImpl->maNameToObjectMap.erase( aIter );
464}
465
467 const uno::Reference < embed::XEmbeddedObject >& xObj, OUString& rName, bool bCopy,
468 const OUString& rSrcShellID, const OUString& rDestShellID )
469{
470 uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
471 if ( rName.isEmpty() )
472 rName = CreateUniqueObjectName();
473
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
479
480 // insert objects' storage into the container storage (if object has one)
481 try
482 {
483 if ( xPersist.is() )
484 {
485 uno::Sequence < beans::PropertyValue > aSeq;
486 if ( bCopy )
487 {
489 { "SourceShellID", uno::Any(rSrcShellID) },
490 { "DestinationShellID", uno::Any(rDestShellID) }
491 }));
492 xPersist->storeToEntry(pImpl->mxStorage, rName, aSeq, aObjArgs);
493 }
494 else
495 {
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 );
500 }
501 }
502 }
503 catch (uno::Exception const& e)
504 {
505 SAL_WARN("comphelper.container", "EmbeddedObjectContainer::StoreEmbeddedObject: exception caught: " << e);
506 // TODO/LATER: better error recovery should keep storage intact
507 return false;
508 }
509
510 return true;
511}
512
513bool EmbeddedObjectContainer::InsertEmbeddedObject( const uno::Reference < embed::XEmbeddedObject >& xObj, OUString& rName )
514{
515 // store it into the container storage
516 if (StoreEmbeddedObject(xObj, rName, false, OUString(), OUString()))
517 {
518 // remember object
519 AddEmbeddedObject( xObj, rName );
520 return true;
521 }
522 else
523 return false;
524}
525
526uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::InsertEmbeddedObject( const uno::Reference < io::XInputStream >& xStm, OUString& rNewName )
527{
528 if ( rNewName.isEmpty() )
529 rNewName = CreateUniqueObjectName();
530
531 // store it into the container storage
532 bool bIsStorage = false;
533 try
534 {
535 // first try storage persistence
536 uno::Reference < embed::XStorage > xStore = ::comphelper::OStorageHelper::GetStorageFromInputStream( xStm );
537
538 // storage was created from stream successfully
539 bIsStorage = true;
540
541 uno::Reference < embed::XStorage > xNewStore = pImpl->mxStorage->openStorageElement( rNewName, embed::ElementModes::READWRITE );
542 xStore->copyToStorage( xNewStore );
543 }
544 catch (const uno::Exception&)
545 {
546 if ( bIsStorage )
547 // it is storage persistence, but opening of new substorage or copying to it failed
548 return uno::Reference < embed::XEmbeddedObject >();
549
550 // stream didn't contain a storage, now try stream persistence
551 try
552 {
553 uno::Reference < io::XStream > xNewStream = pImpl->mxStorage->openStreamElement( rNewName, embed::ElementModes::READWRITE );
554 ::comphelper::OStorageHelper::CopyInputToOutput( xStm, xNewStream->getOutputStream() );
555
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" ) ) );
562 }
563 catch (uno::Exception const& e)
564 {
565 // complete disaster!
566 SAL_WARN("comphelper.container", "EmbeddedObjectContainer::InsertEmbeddedObject: exception caught: " << e);
567 return uno::Reference < embed::XEmbeddedObject >();
568 }
569 }
570
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 );
573 try
574 {
575 if ( !xRet.is() )
576 // no object could be created, so withdraw insertion
577 pImpl->mxStorage->removeElement( rNewName );
578 }
579 catch (const uno::Exception&)
580 {
581 }
582
583 return xRet;
584}
585
586uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::InsertEmbeddedObject( const css::uno::Sequence < css::beans::PropertyValue >& aMedium, OUString& rNewName, OUString const* pBaseURL )
587{
588 if ( rNewName.isEmpty() )
589 rNewName = CreateUniqueObjectName();
590
591 uno::Reference < embed::XEmbeddedObject > xObj;
592 try
593 {
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)
600 {
601 pObjDescr[1].Name = "DefaultParentBaseURL";
602 pObjDescr[1].Value <<= *pBaseURL;
603 }
604 xObj.set( xFactory->createInstanceInitFromMediaDescriptor(
605 pImpl->mxStorage, rNewName, aMedium, aObjDescr ), uno::UNO_QUERY );
606 uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
607
608 OSL_ENSURE( !xObj.is() || xObj->getCurrentState() != embed::EmbedStates::LOADED,
609 "A freshly create object should be running always!" );
610
611 // possible optimization: store later!
612 if ( xPersist.is())
613 xPersist->storeOwn();
614
615 AddEmbeddedObject( xObj, rNewName );
616 }
617 catch (const uno::Exception&)
618 {
619 }
620
621 return xObj;
622}
623
624uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::InsertEmbeddedLink( const css::uno::Sequence < css::beans::PropertyValue >& aMedium, OUString& rNewName )
625{
626 if ( rNewName.isEmpty() )
627 rNewName = CreateUniqueObjectName();
628
629 uno::Reference < embed::XEmbeddedObject > xObj;
630 try
631 {
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 );
636
637 uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
638
639 OSL_ENSURE( !xObj.is() || xObj->getCurrentState() != embed::EmbedStates::LOADED,
640 "A freshly create object should be running always!" );
641
642 // possible optimization: store later!
643 if ( xPersist.is())
644 xPersist->storeOwn();
645
646 AddEmbeddedObject( xObj, rNewName );
647 }
648 catch (uno::Exception const& e)
649 {
650 SAL_WARN("comphelper.container", "EmbeddedObjectContainer::InsertEmbeddedLink: "
651 "exception caught: " << e);
652 }
653
654 return xObj;
655}
656
658 const OUString& aOrigName,
659 const OUString& aTargetName )
660{
661 bool bResult = false;
662
663 if ( ( &rSrc != this || aOrigName != aTargetName ) && !aOrigName.isEmpty() && !aTargetName.isEmpty() )
664 {
665 OUString aMediaType;
666 uno::Reference < io::XInputStream > xGrStream = rSrc.GetGraphicStream( aOrigName, &aMediaType );
667 if ( xGrStream.is() )
668 bResult = InsertGraphicStream( xGrStream, aTargetName, aMediaType );
669 }
670
671 return bResult;
672}
673
674uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::CopyAndGetEmbeddedObject(
675 EmbeddedObjectContainer& rSrc, const uno::Reference <embed::XEmbeddedObject>& xObj, OUString& rName,
676 const OUString& rSrcShellID, const OUString& rDestShellID )
677{
678 uno::Reference< embed::XEmbeddedObject > xResult;
679
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;
683 try
684 {
685 uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY_THROW );
686 aOrigName = xPersist->getEntryName();
687 }
688 catch (const uno::Exception&)
689 {
690 }
691
692 if ( rName.isEmpty() )
693 rName = CreateUniqueObjectName();
694
695 // objects without persistence are not really stored by the method
696 if (xObj.is() && StoreEmbeddedObject(xObj, rName, true, rSrcShellID, rDestShellID))
697 {
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() )
702 {
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
705 try
706 {
707 uno::Reference< embed::XLinkageSupport > xOrigLinkage( xObj, uno::UNO_QUERY );
708 if ( xOrigLinkage.is() && xOrigLinkage->isLink() )
709 {
710 // this is an OOo link, it has no persistence
711 OUString aURL = xOrigLinkage->getLinkURL();
712 if ( aURL.isEmpty() )
713 throw uno::RuntimeException();
714
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() );
718
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 );
729 }
730 else
731 {
732 // the component is required for copying of this object
733 if ( xObj->getCurrentState() == embed::EmbedStates::LOADED )
734 xObj->changeState( embed::EmbedStates::RUNNING );
735
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 );
738
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() );
742
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 );
752
753 if ( xResult->getCurrentState() == embed::EmbedStates::LOADED )
754 xResult->changeState( embed::EmbedStates::RUNNING );
755
756 uno::Reference< beans::XPropertySet > xTargetProps( xResult->getComponent(), uno::UNO_QUERY_THROW );
757
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();
762
763 const uno::Sequence< beans::Property > aPropertiesList = xOrigInfo->getProperties();
764 for ( const auto & p : aPropertiesList )
765 {
766 try
767 {
768 xTargetProps->setPropertyValue(
769 p.Name,
770 xOrigProps->getPropertyValue( p.Name ) );
771 }
772 catch (const beans::PropertyVetoException&)
773 {
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!" );
777 }
778 }
779 }
780
781 if ( xResult.is() )
782 AddEmbeddedObject( xResult, rName );
783 }
784 catch (const uno::Exception&)
785 {
786 if ( xResult.is() )
787 {
788 try
789 {
790 xResult->close( true );
791 }
792 catch (const uno::Exception&)
793 {
794 }
795 xResult.clear();
796 }
797 }
798 }
799 }
800
801 SAL_WARN_IF( !xResult.is(), "comphelper.container", "Can not copy embedded object that has no persistence!" );
802
803 if ( xResult.is() )
804 {
805 // the object is successfully copied, try to copy graphical replacement
806 if ( !aOrigName.isEmpty() )
807 TryToCopyGraphReplacement( rSrc, aOrigName, rName );
808
809 // the object might need the size to be set
810 try
811 {
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 ) );
815 }
816 catch (const uno::Exception&)
817 {
818 }
819 }
820
821 return xResult;
822}
823
824// #i119941, bKeepToTempStorage: use to specify whether store the removed object to temporary storage+
825void EmbeddedObjectContainer::RemoveEmbeddedObject( const OUString& rName, bool bKeepToTempStorage )
826{
827 uno::Reference < embed::XEmbeddedObject > xObj = GetEmbeddedObject( rName );
828 if ( xObj.is() )
829 RemoveEmbeddedObject( xObj, bKeepToTempStorage );
830}
831
833{
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!" );
837
838 if ( aIt2 != rCnt.pImpl->maNameToObjectMap.end() )
839 return false;
840
841 uno::Reference < embed::XEmbeddedObject > xObj;
842 auto aIt = pImpl->maNameToObjectMap.find( rName );
843 if ( aIt != pImpl->maNameToObjectMap.end() )
844 {
845 xObj = (*aIt).second;
846 try
847 {
848 if ( xObj.is() )
849 {
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 );
858 }
859 else
860 {
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 );
865 }
866
867 rCnt.TryToCopyGraphReplacement( *this, rName, rName );
868 // RemoveGraphicStream( rName );
869
870 return true;
871 }
872 catch (const uno::Exception&)
873 {
874 SAL_WARN( "comphelper.container", "Could not move object!");
875 return false;
876 }
877
878 }
879 else
880 SAL_WARN( "comphelper.container", "Unknown object!");
881 return false;
882}
883
884// #i119941, bKeepToTempStorage: use to specify whether store the removed object to temporary storage+
885bool EmbeddedObjectContainer::RemoveEmbeddedObject( const uno::Reference < embed::XEmbeddedObject >& xObj, bool bKeepToTempStorage )
886{
887 uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
888 OUString aName;
889 if ( xPersist.is() )
890 aName = xPersist->getEntryName();
891
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() );
896
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
900
901 // somebody still needs the object, so we must assign a temporary persistence
902 try
903 {
904 if ( xPersist.is() && bKeepToTempStorage ) // #i119941
905 {
906
907 if ( !pImpl->mpTempObjectContainer )
908 {
909 pImpl->mpTempObjectContainer = new EmbeddedObjectContainer();
910 try
911 {
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 OUStringLiteral s_sMediaType(u"MediaType");
917 xStorProps->getPropertyValue( s_sMediaType ) >>= aOrigStorMediaType;
918
919 SAL_WARN_IF( aOrigStorMediaType.isEmpty(), "comphelper.container", "No valuable media type in the storage!" );
920
921 uno::Reference< beans::XPropertySet > xTargetStorProps(
922 pImpl->mpTempObjectContainer->pImpl->mxStorage,
923 uno::UNO_QUERY_THROW );
924 xTargetStorProps->setPropertyValue( s_sMediaType,uno::Any( aOrigStorMediaType ) );
925 }
926 catch (const uno::Exception&)
927 {
928 SAL_WARN( "comphelper.container", "Can not set the new media type to a storage!" );
929 }
930 }
931
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;
940
941 pImpl->mpTempObjectContainer->InsertEmbeddedObject( xObj, aTempName );
942
943 uno::Reference < io::XInputStream > xStream = GetGraphicStream( xObj, &aMediaType );
944 if ( xStream.is() )
945 pImpl->mpTempObjectContainer->InsertGraphicStream( xStream, aTempName, aMediaType );
946
947 // object is stored, so at least it can be set to loaded state
948 xObj->changeState( embed::EmbedStates::LOADED );
949 }
950 else
951 // objects without persistence need to stay in running state if they shall not be closed
952 xObj->changeState( embed::EmbedStates::RUNNING );
953 }
954 catch (const uno::Exception&)
955 {
956 return false;
957 }
958
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())
962 {
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 >() );
968 }
969 else
970 SAL_WARN( "comphelper.container", "Object not found for removal!" );
971
972 if ( !xPersist || !bKeepToTempStorage ) // #i119941#
973 return true;
974
975 // remove replacement image (if there is one)
976 RemoveGraphicStream( aName );
977
978 // now it's time to remove the storage from the container storage
979 try
980 {
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 );
987 }
988 catch (const uno::Exception&)
989 {
990 SAL_WARN( "comphelper.container", "Failed to remove object from storage!" );
991 return false;
992 }
993
994 return true;
995}
996
997void EmbeddedObjectContainer::CloseEmbeddedObject( const uno::Reference < embed::XEmbeddedObject >& xObj )
998{
999 // disconnect the object from the container and close it if possible
1000
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;
1005
1006 pImpl->maObjectToNameMap.erase( aIter->second );
1007 pImpl->maNameToObjectMap.erase( aIter );
1008
1009 try
1010 {
1011 xObj->close( true );
1012 }
1013 catch (const uno::Exception&)
1014 {
1015 // it is no problem if the object is already closed
1016 // TODO/LATER: what if the object can not be closed?
1017 }
1018}
1019
1020uno::Reference < io::XInputStream > EmbeddedObjectContainer::GetGraphicStream( const OUString& aName, OUString* pMediaType )
1021{
1022 uno::Reference < io::XInputStream > xStream;
1023
1024 SAL_WARN_IF( aName.isEmpty(), "comphelper.container", "Retrieving graphic for unknown object!" );
1025 if ( !aName.isEmpty() )
1026 {
1027 try
1028 {
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 )
1033 {
1034 uno::Reference < beans::XPropertySet > xSet( xStream, uno::UNO_QUERY );
1035 if ( xSet.is() )
1036 {
1037 uno::Any aAny = xSet->getPropertyValue("MediaType");
1038 aAny >>= *pMediaType;
1039 }
1040 }
1041 }
1042 catch (uno::Exception const& e)
1043 {
1044 SAL_INFO("comphelper.container",
1045 "EmbeddedObjectContainer::GetGraphicStream(): " << e);
1046 }
1047 }
1048
1049 return xStream;
1050}
1051
1052uno::Reference < io::XInputStream > EmbeddedObjectContainer::GetGraphicStream( const css::uno::Reference < css::embed::XEmbeddedObject >& xObj, OUString* pMediaType )
1053{
1054 // try to load it from the container storage
1055 return GetGraphicStream( GetEmbeddedObjectName( xObj ), pMediaType );
1056}
1057
1058bool EmbeddedObjectContainer::InsertGraphicStream( const css::uno::Reference < css::io::XInputStream >& rStream, const OUString& rObjectName, const OUString& rMediaType )
1059{
1060 try
1061 {
1062 uno::Reference < embed::XStorage > xReplacements = pImpl->GetReplacements();
1063
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();
1070 xOutStream->flush();
1071
1072 uno::Reference< beans::XPropertySet > xPropSet( xGraphicStream, uno::UNO_QUERY_THROW );
1073
1074 xPropSet->setPropertyValue("UseCommonStoragePasswordEncryption",
1075 uno::Any( true ) );
1076 xPropSet->setPropertyValue("MediaType", uno::Any(rMediaType) );
1077
1078 xPropSet->setPropertyValue("Compressed",
1079 uno::Any( true ) );
1080 }
1081 catch (const uno::Exception&)
1082 {
1083 return false;
1084 }
1085
1086 return true;
1087}
1088
1089bool EmbeddedObjectContainer::InsertGraphicStreamDirectly( const css::uno::Reference < css::io::XInputStream >& rStream, const OUString& rObjectName, const OUString& rMediaType )
1090{
1091 try
1092 {
1093 uno::Reference < embed::XStorage > xReplacement = pImpl->GetReplacements();
1094 uno::Reference < embed::XOptimizedStorage > xOptRepl( xReplacement, uno::UNO_QUERY_THROW );
1095
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)
1101 };
1102
1103 if ( xReplacement->hasByName( rObjectName ) )
1104 xReplacement->removeElement( rObjectName );
1105
1106 xOptRepl->insertStreamElementDirect( rObjectName, rStream, aProps );
1107 }
1108 catch (const uno::Exception&)
1109 {
1110 return false;
1111 }
1112
1113 return true;
1114}
1115
1116
1117void EmbeddedObjectContainer::RemoveGraphicStream( const OUString& rObjectName )
1118{
1119 try
1120 {
1121 uno::Reference < embed::XStorage > xReplacements = pImpl->GetReplacements();
1122 xReplacements->removeElement( rObjectName );
1123 }
1124 catch (const uno::Exception&)
1125 {
1126 }
1127}
1128namespace {
1129 void InsertStreamIntoPicturesStorage_Impl( const uno::Reference< embed::XStorage >& xDocStor,
1130 const uno::Reference< io::XInputStream >& xInStream,
1131 const OUString& aStreamName )
1132 {
1133 OSL_ENSURE( !aStreamName.isEmpty() && xInStream.is() && xDocStor.is(), "Misuse of the method!" );
1134
1135 try
1136 {
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 );
1145
1146 ::comphelper::OStorageHelper::CopyInputToOutput( xInStream, xOutStream );
1147 xOutStream->closeOutput();
1148
1149 uno::Reference< embed::XTransactedObject > xTransact( xPictures, uno::UNO_QUERY );
1150 if ( xTransact.is() )
1151 xTransact->commit();
1152 }
1153 catch (const uno::Exception&)
1154 {
1155 SAL_WARN( "comphelper.container", "The images storage is not available!" );
1156 }
1157 }
1158
1159}
1160
1161bool EmbeddedObjectContainer::StoreAsChildren(bool _bOasisFormat,bool _bCreateEmbedded, bool _bAutoSaveEvent,
1162 const uno::Reference < embed::XStorage >& _xStorage)
1163{
1164 bool bResult = false;
1165 try
1166 {
1167 comphelper::EmbeddedObjectContainer aCnt( _xStorage );
1168 const uno::Sequence < OUString > aNames = GetObjectNames();
1169 const OUString* pIter = aNames.getConstArray();
1170 const OUString* pEnd = pIter + aNames.getLength();
1171 for(;pIter != pEnd;++pIter)
1172 {
1173 uno::Reference < embed::XEmbeddedObject > xObj = GetEmbeddedObject( *pIter );
1174 SAL_WARN_IF( !xObj.is(), "comphelper.container", "An empty entry in the embedded objects list!" );
1175 if ( xObj.is() )
1176 {
1177 bool bSwitchBackToLoaded = false;
1178 uno::Reference< embed::XLinkageSupport > xLink( xObj, uno::UNO_QUERY );
1179
1180 uno::Reference < io::XInputStream > xStream;
1181 OUString aMediaType;
1182
1183 sal_Int32 nCurState = xObj->getCurrentState();
1184 if ( nCurState == embed::EmbedStates::LOADED || nCurState == embed::EmbedStates::RUNNING )
1185 {
1186 // means that the object is not active
1187 // copy replacement image from old to new container
1188 xStream = GetGraphicStream( xObj, &aMediaType );
1189 }
1190
1191 if ( !xStream.is() && getUserAllowsLinkUpdate() )
1192 {
1193 // the image must be regenerated
1194 // TODO/LATER: another aspect could be used
1195 if ( xObj->getCurrentState() == embed::EmbedStates::LOADED )
1196 bSwitchBackToLoaded = true;
1197
1199 embed::Aspects::MSOLE_CONTENT,
1200 xObj,
1201 &aMediaType );
1202 }
1203
1204 if ( _bOasisFormat || (xLink.is() && xLink->isLink()) )
1205 {
1206 if ( xStream.is() )
1207 {
1208 if ( _bOasisFormat )
1209 {
1210 // if it is an embedded object or the optimized inserting fails the normal inserting should be done
1211 if ( _bCreateEmbedded
1212 || !aCnt.InsertGraphicStreamDirectly( xStream, *pIter, aMediaType ) )
1213 aCnt.InsertGraphicStream( xStream, *pIter, aMediaType );
1214 }
1215 else
1216 {
1217 // it is a linked object exported into SO7 format
1218 InsertStreamIntoPicturesStorage_Impl( _xStorage, xStream, *pIter );
1219 }
1220 }
1221 }
1222
1223 uno::Reference< embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
1224 if ( xPersist.is() )
1225 {
1226 uno::Sequence< beans::PropertyValue > aArgs( _bOasisFormat ? 3 : 4 );
1227 auto pArgs = aArgs.getArray();
1228 pArgs[0].Name = "StoreVisualReplacement";
1229 pArgs[0].Value <<= !_bOasisFormat;
1230
1231 // if it is an embedded object or the optimized inserting fails the normal inserting should be done
1232 pArgs[1].Name = "CanTryOptimization";
1233 pArgs[1].Value <<= !_bCreateEmbedded;
1234
1235 pArgs[2].Name = "AutoSaveEvent";
1236 pArgs[2].Value <<= _bAutoSaveEvent;
1237
1238 if ( !_bOasisFormat )
1239 {
1240 // if object has no cached replacement it will use this one
1241 pArgs[3].Name = "VisualReplacement";
1242 pArgs[3].Value <<= xStream;
1243 }
1244
1245 try
1246 {
1247 xPersist->storeAsEntry( _xStorage, xPersist->getEntryName(), uno::Sequence< beans::PropertyValue >(), aArgs );
1248 }
1249 catch (const embed::WrongStateException&)
1250 {
1251 SAL_WARN("comphelper.container", "failed to store '" << *pIter << "'");
1252 }
1253 }
1254
1255 if ( bSwitchBackToLoaded )
1256 // switch back to loaded state; that way we have a minimum cache confusion
1257 xObj->changeState( embed::EmbedStates::LOADED );
1258 }
1259 }
1260
1261 bResult = aCnt.CommitImageSubStorage();
1262
1263 }
1264 catch (const uno::Exception& e)
1265 {
1266 // TODO/LATER: error handling
1267 bResult = false;
1268 SAL_WARN("comphelper.container", "failed. Message: " << e);
1269 }
1270
1271 // the old SO6 format does not store graphical replacements
1272 if ( !_bOasisFormat && bResult )
1273 {
1274 try
1275 {
1276 // the substorage still can not be locked by the embedded object container
1277 OUString aObjReplElement( "ObjectReplacements" );
1278 if ( _xStorage->hasByName( aObjReplElement ) && _xStorage->isStorageElement( aObjReplElement ) )
1279 _xStorage->removeElement( aObjReplElement );
1280 }
1281 catch (const uno::Exception&)
1282 {
1283 // TODO/LATER: error handling;
1284 bResult = false;
1285 }
1286 }
1287 return bResult;
1288}
1289
1290bool EmbeddedObjectContainer::StoreChildren(bool _bOasisFormat,bool _bObjectsOnly)
1291{
1292 bool bResult = true;
1293 const uno::Sequence < OUString > aNames = GetObjectNames();
1294 const OUString* pIter = aNames.getConstArray();
1295 const OUString* pEnd = pIter + aNames.getLength();
1296 for(;pIter != pEnd;++pIter)
1297 {
1298 try
1299 {
1300 uno::Reference < embed::XEmbeddedObject > xObj = GetEmbeddedObject( *pIter );
1301 SAL_WARN_IF( !xObj.is(), "comphelper.container", "An empty entry in the embedded objects list!" );
1302 if ( xObj.is() )
1303 {
1304 sal_Int32 nCurState = xObj->getCurrentState();
1305 if ( _bOasisFormat && nCurState != embed::EmbedStates::LOADED && nCurState != embed::EmbedStates::RUNNING )
1306 {
1307 // means that the object is active
1308 // the image must be regenerated
1309 OUString aMediaType;
1310
1311 // TODO/LATER: another aspect could be used
1312 uno::Reference < io::XInputStream > xStream =
1314 embed::Aspects::MSOLE_CONTENT,
1315 xObj,
1316 &aMediaType );
1317 if ( xStream.is() )
1318 {
1319 if ( !InsertGraphicStreamDirectly( xStream, *pIter, aMediaType ) )
1320 InsertGraphicStream( xStream, *pIter, aMediaType );
1321 }
1322 }
1323
1324 // TODO/LATER: currently the object by default does not cache replacement image
1325 // that means that if somebody loads SO7 document and store its objects using
1326 // this method the images might be lost.
1327 // Currently this method is only used on storing to alien formats, that means
1328 // that SO7 documents storing does not use it, and all other filters are
1329 // based on OASIS format. But if it changes the method must be fixed. The fix
1330 // must be done only on demand since it can affect performance.
1331
1332 uno::Reference< embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
1333 if ( xPersist.is() )
1334 {
1335 try
1336 {
1337 //TODO/LATER: only storing if changed!
1338 //xPersist->storeOwn(); //commented, i120168
1339
1340 // begin:all charts will be persisted as xml format on disk when saving, which is time consuming.
1341 // '_bObjectsOnly' mean we are storing to alien formats.
1342 // 'isStorageElement' mean current object is NOT a MS OLE format. (may also include in future), i120168
1343 if (_bObjectsOnly && (nCurState == embed::EmbedStates::LOADED || nCurState == embed::EmbedStates::RUNNING)
1344 && (pImpl->mxStorage->isStorageElement( *pIter ) ))
1345 {
1346 uno::Reference< util::XModifiable > xModifiable( xObj->getComponent(), uno::UNO_QUERY );
1347 if ( xModifiable.is() && xModifiable->isModified())
1348 {
1349 xPersist->storeOwn();
1350 }
1351 else
1352 {
1353 //do nothing. Embedded model is not modified, no need to persist.
1354 }
1355 }
1356 else //the embedded object is in active status, always store back it.
1357 {
1358 xPersist->storeOwn();
1359 }
1360 //end i120168
1361 }
1362 catch (const uno::Exception&)
1363 {
1364 // TODO/LATER: error handling
1365 bResult = false;
1366 break;
1367 }
1368 }
1369
1370 if ( !_bOasisFormat && !_bObjectsOnly )
1371 {
1372 // copy replacement images for linked objects
1373 try
1374 {
1375 uno::Reference< embed::XLinkageSupport > xLink( xObj, uno::UNO_QUERY );
1376 if ( xLink.is() && xLink->isLink() )
1377 {
1378 OUString aMediaType;
1379 uno::Reference < io::XInputStream > xInStream = GetGraphicStream( xObj, &aMediaType );
1380 if ( xInStream.is() )
1381 InsertStreamIntoPicturesStorage_Impl( pImpl->mxStorage, xInStream, *pIter );
1382 }
1383 }
1384 catch (const uno::Exception&)
1385 {
1386 }
1387 }
1388 }
1389 }
1390 catch (const uno::Exception&)
1391 {
1392 // TODO/LATER: error handling
1393 }
1394 }
1395
1396 if ( bResult && _bOasisFormat )
1397 bResult = CommitImageSubStorage();
1398
1399 if ( bResult && !_bObjectsOnly )
1400 {
1401 try
1402 {
1404 OUString aObjReplElement( "ObjectReplacements" );
1405 if ( !_bOasisFormat && pImpl->mxStorage->hasByName( aObjReplElement ) && pImpl->mxStorage->isStorageElement( aObjReplElement ) )
1406 pImpl->mxStorage->removeElement( aObjReplElement );
1407 }
1408 catch (const uno::Exception&)
1409 {
1410 // TODO/LATER: error handling
1411 bResult = false;
1412 }
1413 }
1414 return bResult;
1415}
1416
1418 sal_Int64 nViewAspect,
1419 const uno::Reference< embed::XEmbeddedObject >& xObj,
1420 OUString* pMediaType )
1421{
1422 uno::Reference< io::XInputStream > xInStream;
1423 if ( xObj.is() )
1424 {
1425 try
1426 {
1427 // retrieving of the visual representation can switch object to running state
1428 embed::VisualRepresentation aRep = xObj->getPreferredVisualRepresentation( nViewAspect );
1429 if ( pMediaType )
1430 *pMediaType = aRep.Flavor.MimeType;
1431
1432 uno::Sequence < sal_Int8 > aSeq;
1433 aRep.Data >>= aSeq;
1434 xInStream = new ::comphelper::SequenceInputStream( aSeq );
1435 }
1436 catch (const uno::Exception&)
1437 {
1438 }
1439 }
1440
1441 return xInStream;
1442}
1443
1444bool EmbeddedObjectContainer::SetPersistentEntries(const uno::Reference< embed::XStorage >& _xStorage,bool _bClearModifiedFlag)
1445{
1446 bool bError = false;
1447 const uno::Sequence < OUString > aNames = GetObjectNames();
1448 const OUString* pIter = aNames.getConstArray();
1449 const OUString* pEnd = pIter + aNames.getLength();
1450 for(;pIter != pEnd;++pIter)
1451 {
1452 uno::Reference < embed::XEmbeddedObject > xObj = GetEmbeddedObject( *pIter );
1453 SAL_WARN_IF( !xObj.is(), "comphelper.container", "An empty entry in the embedded objects list!" );
1454 if ( xObj.is() )
1455 {
1456 uno::Reference< embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
1457 if ( xPersist.is() )
1458 {
1459 try
1460 {
1461 xPersist->setPersistentEntry( _xStorage,
1462 *pIter,
1463 embed::EntryInitModes::NO_INIT,
1464 uno::Sequence< beans::PropertyValue >(),
1465 uno::Sequence< beans::PropertyValue >() );
1466
1467 }
1468 catch (const uno::Exception&)
1469 {
1470 // TODO/LATER: error handling
1471 bError = true;
1472 break;
1473 }
1474 }
1475 if ( _bClearModifiedFlag )
1476 {
1477 // if this method is used as part of SaveCompleted the object must stay unmodified after execution
1478 try
1479 {
1480 uno::Reference< util::XModifiable > xModif( xObj->getComponent(), uno::UNO_QUERY_THROW );
1481 if ( xModif->isModified() )
1482 xModif->setModified( false );
1483 }
1484 catch (const uno::Exception&)
1485 {
1486 }
1487 }
1488 }
1489 }
1490 return bError;
1491}
1492
1494{
1495 return pImpl->mbUserAllowsLinkUpdate;
1496}
1497
1499{
1500 if(pImpl->mbUserAllowsLinkUpdate != bNew)
1501 {
1502 pImpl->mbUserAllowsLinkUpdate = bNew;
1503 }
1504}
1505
1506}
1507
1508/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Reference< XInputStream > xStream
bool StoreAsChildren(bool _bOasisFormat, bool _bCreateEmbedded, bool _bAutoSaveEvent, const css::uno::Reference< css::embed::XStorage > &_xStorage)
bool InsertGraphicStreamDirectly(const css::uno::Reference< css::io::XInputStream > &rStream, const OUString &rObjectName, const OUString &rMediaType)
css::uno::Reference< css::io::XInputStream > GetGraphicStream(const css::uno::Reference< css::embed::XEmbeddedObject > &, OUString *pMediaType=nullptr)
bool MoveEmbeddedObject(const OUString &rName, EmbeddedObjectContainer &)
bool SetPersistentEntries(const css::uno::Reference< css::embed::XStorage > &_xStorage, bool _bClearModifiedFlag=true)
call setPersistentEntry for each embedded object in the container
void RemoveGraphicStream(const OUString &rObjectName)
void AddEmbeddedObject(const css::uno::Reference< css::embed::XEmbeddedObject > &, const OUString &)
bool InsertGraphicStream(const css::uno::Reference< css::io::XInputStream > &rStream, const OUString &rObjectName, const OUString &rMediaType)
css::uno::Reference< css::embed::XEmbeddedObject > Get_Impl(const OUString &, const css::uno::Reference< css::embed::XEmbeddedObject > &xCopy, OUString const *pBaseURL)
bool StoreChildren(bool _bOasisFormat, bool _bObjectsOnly)
bool TryToCopyGraphReplacement(EmbeddedObjectContainer &rSrc, const OUString &aOrigName, const OUString &aTargetName)
void CloseEmbeddedObject(const css::uno::Reference< css::embed::XEmbeddedObject > &)
OUString GetEmbeddedObjectName(const css::uno::Reference< css::embed::XEmbeddedObject > &) const
css::uno::Reference< css::embed::XEmbeddedObject > InsertEmbeddedLink(const css::uno::Sequence< css::beans::PropertyValue > &, OUString &)
void SwitchPersistence(const css::uno::Reference< css::embed::XStorage > &)
css::uno::Reference< css::embed::XEmbeddedObject > CopyAndGetEmbeddedObject(EmbeddedObjectContainer &rSrc, const css::uno::Reference< css::embed::XEmbeddedObject > &xObj, OUString &rName, const OUString &rSrcShellID, const OUString &rDestShellID)
void RemoveEmbeddedObject(const OUString &rName, bool bKeepToTempStorage=true)
static css::uno::Reference< css::io::XInputStream > GetGraphicReplacementStream(sal_Int64 nViewAspect, const css::uno::Reference< css::embed::XEmbeddedObject > &, OUString *pMediaType)
css::uno::Sequence< OUString > GetObjectNames() const
bool StoreEmbeddedObject(const css::uno::Reference< css::embed::XEmbeddedObject > &xObj, OUString &rName, bool bCopy, const OUString &rSrcShellID, const OUString &rDestShellID)
css::uno::Reference< css::embed::XEmbeddedObject > GetEmbeddedObject(const OUString &, OUString const *pBaseURL=nullptr)
css::uno::Reference< css::embed::XEmbeddedObject > CreateEmbeddedObject(const css::uno::Sequence< sal_Int8 > &, OUString &, OUString const *pBaseURL=nullptr)
bool InsertEmbeddedObject(const css::uno::Reference< css::embed::XEmbeddedObject > &, OUString &)
static css::uno::Reference< css::embed::XStorage > GetStorageFromInputStream(const css::uno::Reference< css::io::XInputStream > &xStream, const css::uno::Reference< css::uno::XComponentContext > &rxContext=css::uno::Reference< css::uno::XComponentContext >())
static void CopyInputToOutput(const css::uno::Reference< css::io::XInputStream > &xInput, const css::uno::Reference< css::io::XOutputStream > &xOutput)
static css::uno::Reference< css::embed::XStorage > GetTemporaryStorage(const css::uno::Reference< css::uno::XComponentContext > &rxContext=css::uno::Reference< css::uno::XComponentContext >())
URL aURL
Reference< XSingleServiceFactory > xFactory
OUString aName
void * p
Sequence< sal_Int8 > aSeq
#define SAL_INFO_IF(condition, area, stream)
#define SAL_WARN_IF(condition, area, stream)
#define SAL_WARN(area, stream)
#define SAL_INFO(area, stream)
aStr
std::unordered_map< OUString, uno::Reference< embed::XEmbeddedObject > > EmbeddedObjectContainerNameMap
css::uno::Sequence< typename M::key_type > mapKeysToSequence(M const &map)
Copy (keys or values) from an associate container into a Sequence.
Definition: sequence.hxx:300
Reference< XComponentContext > getProcessComponentContext()
This function gets the process service factory's default component context.
css::uno::Sequence< css::beans::PropertyValue > InitPropertySequence(::std::initializer_list< ::std::pair< OUString, css::uno::Any > > vInit)
Init list for property sequences.
css::beans::PropertyValue makePropertyValue(const OUString &rName, T &&rValue)
Creates a beans::PropertyValue easily, i.e.
int i
const uno::Reference< embed::XStorage > & GetReplacements()
uno::Reference< embed::XStorage > mxImageStorage
std::unordered_map< uno::Reference< embed::XEmbeddedObject >, OUString > maObjectToNameMap
uno::WeakReference< uno::XInterface > m_xModel
uno::Reference< embed::XStorage > mxStorage
EmbeddedObjectContainer * mpTempObjectContainer
EmbeddedObjectContainerNameMap maNameToObjectMap
Reference< XModel > xModel
unsigned char sal_Bool