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 
39 #include <comphelper/seqstream.hxx>
43 #include <comphelper/sequence.hxx>
46 #include <cppuhelper/weakref.hxx>
47 #include <sal/log.hxx>
48 
49 #include <algorithm>
50 #include <unordered_map>
51 
52 
53 using namespace ::com::sun::star;
54 
55 namespace comphelper {
56 
57 typedef std::unordered_map<OUString, uno::Reference<embed::XEmbeddedObject>> EmbeddedObjectContainerNameMap;
58 struct EmbedImpl
59 {
60  // TODO/LATER: remove objects from temp. Container storage when object is disposed
61  EmbeddedObjectContainerNameMap maNameToObjectMap;
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 
75 const 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 
106 EmbeddedObjectContainer::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 
115 EmbeddedObjectContainer::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 
125 void 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 
227 uno::Sequence < OUString > EmbeddedObjectContainer::GetObjectNames() const
228 {
229  return comphelper::mapKeysToSequence(pImpl->maNameToObjectMap);
230 }
231 
233 {
234  return !pImpl->maNameToObjectMap.empty();
235 }
236 
237 bool EmbeddedObjectContainer::HasEmbeddedObject( const OUString& rName )
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 
247 bool 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 
261 OUString 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 
272 uno::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 
302 uno::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 
359 uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::CreateEmbeddedObject( const uno::Sequence < sal_Int8 >& rClassId,
360  const uno::Sequence < beans::PropertyValue >& rArgs, OUString& rNewName, OUString const* pBaseURL )
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 
401 uno::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 
406 void 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  {
488  auto aObjArgs(::comphelper::InitPropertySequence({
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 
513 bool 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 
526 uno::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 
586 uno::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 
624 uno::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 
674 uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::CopyAndGetEmbeddedObject(
675  EmbeddedObjectContainer& rSrc, const uno::Reference <embed::XEmbeddedObject>& xObj, OUString& rName,
676  const OUString& rSrcShellID, const OUString& rDestShellID )
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+
825 void 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+
885 bool 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 
997 void 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 
1020 uno::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 
1052 uno::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 
1058 bool 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();
1069  ::comphelper::OStorageHelper::CopyInputToOutput( rStream, xOutStream );
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 
1089 bool 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 
1117 void 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 }
1128 namespace {
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 
1161 bool 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 
1198  xStream = GetGraphicReplacementStream(
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 
1290 bool 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 
1417 uno::Reference< io::XInputStream > EmbeddedObjectContainer::GetGraphicReplacementStream(
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 
1444 bool 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: */
css::uno::Reference< css::embed::XEmbeddedObject > CreateEmbeddedObject(const css::uno::Sequence< sal_Int8 > &, OUString &, OUString const *pBaseURL=nullptr)
URL aURL
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 >())
EmbeddedObjectContainerNameMap maNameToObjectMap
static void CopyInputToOutput(const css::uno::Reference< css::io::XInputStream > &xInput, const css::uno::Reference< css::io::XOutputStream > &xOutput)
#define SAL_INFO_IF(condition, area, stream)
css::beans::PropertyValue makePropertyValue(const OUString &rName, T &&rValue)
Creates a beans::PropertyValue easily, i.e.
void CloseEmbeddedObject(const css::uno::Reference< css::embed::XEmbeddedObject > &)
void AddEmbeddedObject(const css::uno::Reference< css::embed::XEmbeddedObject > &, const OUString &)
EmbeddedObjectContainer * mpTempObjectContainer
css::uno::Reference< css::embed::XEmbeddedObject > InsertEmbeddedLink(const css::uno::Sequence< css::beans::PropertyValue > &, OUString &)
css::uno::Reference< css::embed::XEmbeddedObject > Get_Impl(const OUString &, const css::uno::Reference< css::embed::XEmbeddedObject > &xCopy, OUString const *pBaseURL)
css::uno::Reference< css::io::XInputStream > GetGraphicStream(const css::uno::Reference< css::embed::XEmbeddedObject > &, OUString *pMediaType=nullptr)
Reference< XInputStream > xStream
void SwitchPersistence(const css::uno::Reference< css::embed::XStorage > &)
css::uno::Sequence< css::beans::PropertyValue > InitPropertySequence(::std::initializer_list< ::std::pair< OUString, css::uno::Any > > vInit)
Init list for property sequences.
std::unordered_map< uno::Reference< embed::XEmbeddedObject >, OUString > maObjectToNameMap
static css::uno::Reference< css::embed::XStorage > GetTemporaryStorage(const css::uno::Reference< css::uno::XComponentContext > &rxContext=css::uno::Reference< css::uno::XComponentContext >())
bool StoreAsChildren(bool _bOasisFormat, bool _bCreateEmbedded, bool _bAutoSaveEvent, const css::uno::Reference< css::embed::XStorage > &_xStorage)
bool SetPersistentEntries(const css::uno::Reference< css::embed::XStorage > &_xStorage, bool _bClearModifiedFlag=true)
call setPersistentEntry for each embedded object in the container
int i
bool InsertGraphicStream(const css::uno::Reference< css::io::XInputStream > &rStream, const OUString &rObjectName, const OUString &rMediaType)
uno::Reference< embed::XStorage > mxStorage
unsigned char sal_Bool
bool StoreChildren(bool _bOasisFormat, bool _bObjectsOnly)
uno::WeakReference< uno::XInterface > m_xModel
uno::Reference< embed::XStorage > mxImageStorage
OUString GetEmbeddedObjectName(const css::uno::Reference< css::embed::XEmbeddedObject > &) const
css::uno::Reference< css::embed::XEmbeddedObject > GetEmbeddedObject(const OUString &, OUString const *pBaseURL=nullptr)
void RemoveEmbeddedObject(const OUString &rName, bool bKeepToTempStorage=true)
#define SAL_WARN_IF(condition, area, stream)
const uno::Reference< embed::XStorage > & GetReplacements()
bool InsertGraphicStreamDirectly(const css::uno::Reference< css::io::XInputStream > &rStream, const OUString &rObjectName, const OUString &rMediaType)
#define SAL_INFO(area, stream)
OUString aName
void * p
Reference< XComponentContext > getProcessComponentContext()
This function gets the process service factory's default component context.
Sequence< sal_Int8 > aSeq
bool InsertEmbeddedObject(const css::uno::Reference< css::embed::XEmbeddedObject > &, OUString &)
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)
bool StoreEmbeddedObject(const css::uno::Reference< css::embed::XEmbeddedObject > &xObj, OUString &rName, bool bCopy, const OUString &rSrcShellID, const OUString &rDestShellID)
#define SAL_WARN(area, stream)
Reference< XSingleServiceFactory > xFactory
static css::uno::Reference< css::io::XInputStream > GetGraphicReplacementStream(sal_Int64 nViewAspect, const css::uno::Reference< css::embed::XEmbeddedObject > &, OUString *pMediaType)
std::unordered_map< OUString, uno::Reference< embed::XEmbeddedObject > > EmbeddedObjectContainerNameMap
void RemoveGraphicStream(const OUString &rObjectName)
bool TryToCopyGraphReplacement(EmbeddedObjectContainer &rSrc, const OUString &aOrigName, const OUString &aTargetName)
aStr
bool MoveEmbeddedObject(const OUString &rName, EmbeddedObjectContainer &)
css::uno::Sequence< OUString > GetObjectNames() const
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
typedef void(CALLTYPE *GetFuncDataPtr)(sal_uInt16 &nNo