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>
45 #include <cppuhelper/weakref.hxx>
46 #include <sal/log.hxx>
47 
48 #include <algorithm>
49 #include <unordered_map>
50 
51 
52 using namespace ::com::sun::star;
53 
54 namespace comphelper {
55 
56 typedef std::unordered_map<OUString, uno::Reference<embed::XEmbeddedObject>> EmbeddedObjectContainerNameMap;
57 struct EmbedImpl
58 {
59  // TODO/LATER: remove objects from temp. Container storage when object is disposed
60  EmbeddedObjectContainerNameMap maNameToObjectMap;
61  // to speed up lookup by Reference
62  std::unordered_map<uno::Reference<embed::XEmbeddedObject>, OUString> maObjectToNameMap;
63  uno::Reference < embed::XStorage > mxStorage;
65  uno::Reference < embed::XStorage > mxImageStorage;
66  uno::WeakReference < uno::XInterface > m_xModel;
67 
68  bool mbOwnsStorage : 1;
70 
71  const uno::Reference < embed::XStorage >& GetReplacements();
72 };
73 
74 const uno::Reference < embed::XStorage >& EmbedImpl::GetReplacements()
75 {
76  if ( !mxImageStorage.is() )
77  {
78  try
79  {
80  mxImageStorage = mxStorage->openStorageElement(
81  "ObjectReplacements", embed::ElementModes::READWRITE );
82  }
83  catch (const uno::Exception&)
84  {
85  mxImageStorage = mxStorage->openStorageElement(
86  "ObjectReplacements", embed::ElementModes::READ );
87  }
88  }
89 
90  if ( !mxImageStorage.is() )
91  throw io::IOException("No ObjectReplacements");
92 
93  return mxImageStorage;
94 }
95 
97  : pImpl(new EmbedImpl)
98 {
100  pImpl->mbOwnsStorage = true;
101  pImpl->mbUserAllowsLinkUpdate = true;
102  pImpl->mpTempObjectContainer = nullptr;
103 }
104 
105 EmbeddedObjectContainer::EmbeddedObjectContainer( const uno::Reference < embed::XStorage >& rStor )
106  : pImpl(new EmbedImpl)
107 {
108  pImpl->mxStorage = rStor;
109  pImpl->mbOwnsStorage = false;
110  pImpl->mbUserAllowsLinkUpdate = true;
111  pImpl->mpTempObjectContainer = nullptr;
112 }
113 
114 EmbeddedObjectContainer::EmbeddedObjectContainer( const uno::Reference < embed::XStorage >& rStor, const uno::Reference < uno::XInterface >& xModel )
115  : pImpl(new EmbedImpl)
116 {
117  pImpl->mxStorage = rStor;
118  pImpl->mbOwnsStorage = false;
119  pImpl->mbUserAllowsLinkUpdate = true;
120  pImpl->mpTempObjectContainer = nullptr;
121  pImpl->m_xModel = xModel;
122 }
123 
124 void EmbeddedObjectContainer::SwitchPersistence( const uno::Reference < embed::XStorage >& rStor )
125 {
127 
128  if ( pImpl->mbOwnsStorage )
129  pImpl->mxStorage->dispose();
130 
131  pImpl->mxStorage = rStor;
132  pImpl->mbOwnsStorage = false;
133 }
134 
136 {
137  if ( pImpl->mxImageStorage.is() )
138  {
139  try
140  {
141  bool bReadOnlyMode = true;
142  uno::Reference < beans::XPropertySet > xSet(pImpl->mxImageStorage,uno::UNO_QUERY);
143  if ( xSet.is() )
144  {
145  // get the open mode from the parent storage
146  sal_Int32 nMode = 0;
147  uno::Any aAny = xSet->getPropertyValue("OpenMode");
148  if ( aAny >>= nMode )
149  bReadOnlyMode = !(nMode & embed::ElementModes::WRITE );
150  } // if ( xSet.is() )
151  if ( !bReadOnlyMode )
152  {
153  uno::Reference< embed::XTransactedObject > xTransact( pImpl->mxImageStorage, uno::UNO_QUERY_THROW );
154  xTransact->commit();
155  }
156  }
157  catch (const uno::Exception&)
158  {
159  return false;
160  }
161  }
162 
163  return true;
164 }
165 
167 {
169 
170  if ( pImpl->mxImageStorage.is() )
171  {
172  try
173  {
174  pImpl->mxImageStorage->dispose();
175  pImpl->mxImageStorage.clear();
176  }
177  catch (const uno::Exception&)
178  {
179  SAL_WARN( "comphelper.container", "Problems releasing image substorage!" );
180  }
181  }
182 }
183 
185 {
187 
188  if ( pImpl->mbOwnsStorage )
189  pImpl->mxStorage->dispose();
190 
191  delete pImpl->mpTempObjectContainer;
192 }
193 
195 {
196  for( const auto& rObj : pImpl->maNameToObjectMap )
197  {
198  uno::Reference < util::XCloseable > const & xClose = rObj.second;
199  if( xClose.is() )
200  {
201  try
202  {
203  xClose->close( true );
204  }
205  catch (const uno::Exception&)
206  {
207  }
208  }
209  }
210 }
211 
213 {
214  OUString aStr;
215  sal_Int32 i=1;
216  do
217  {
218  aStr = "Object " + OUString::number( i++ );
219  }
220  while( HasEmbeddedObject( aStr ) );
221  // TODO/LATER: should we consider deleted objects?
222 
223  return aStr;
224 }
225 
226 uno::Sequence < OUString > EmbeddedObjectContainer::GetObjectNames() const
227 {
228  return comphelper::mapKeysToSequence(pImpl->maNameToObjectMap);
229 }
230 
232 {
233  return !pImpl->maNameToObjectMap.empty();
234 }
235 
236 bool EmbeddedObjectContainer::HasEmbeddedObject( const OUString& rName )
237 {
238  auto aIt = pImpl->maNameToObjectMap.find( rName );
239  if (aIt != pImpl->maNameToObjectMap.end())
240  return true;
241  if (!pImpl->mxStorage.is())
242  return false;
243  return pImpl->mxStorage->hasByName(rName);
244 }
245 
246 bool EmbeddedObjectContainer::HasEmbeddedObject( const uno::Reference < embed::XEmbeddedObject >& xObj ) const
247 {
248  return pImpl->maObjectToNameMap.find(xObj) != pImpl->maObjectToNameMap.end();
249 }
250 
252 {
253  // allows to detect whether the object was already instantiated
254  // currently the filter instantiate it on loading, so this method allows
255  // to avoid objects pointing to the same persistence
256  auto aIt = pImpl->maNameToObjectMap.find( rName );
257  return ( aIt != pImpl->maNameToObjectMap.end() );
258 }
259 
260 OUString EmbeddedObjectContainer::GetEmbeddedObjectName( const css::uno::Reference < css::embed::XEmbeddedObject >& xObj ) const
261 {
262  auto it = pImpl->maObjectToNameMap.find(xObj);
263  if (it == pImpl->maObjectToNameMap.end())
264  {
265  SAL_WARN( "comphelper.container", "Unknown object!" );
266  return OUString();
267  }
268  return it->second;
269 }
270 
271 uno::Reference< embed::XEmbeddedObject>
273  const OUString& rName, OUString const*const pBaseURL)
274 {
275  SAL_WARN_IF( rName.isEmpty(), "comphelper.container", "Empty object name!");
276 
277  uno::Reference < embed::XEmbeddedObject > xObj;
278  auto aIt = pImpl->maNameToObjectMap.find( rName );
279 
280 #if OSL_DEBUG_LEVEL > 1
281  uno::Reference < container::XNameAccess > xAccess( pImpl->mxStorage, uno::UNO_QUERY );
282  uno::Sequence< OUString> aSeq = xAccess->getElementNames();
283  const OUString* pIter = aSeq.getConstArray();
284  const OUString* pEnd = pIter + aSeq.getLength();
285  for(;pIter != pEnd;++pIter)
286  {
287  (void)*pIter;
288  }
289  OSL_ENSURE( aIt != pImpl->maNameToObjectMap.end() || xAccess->hasByName(rName), "Could not return object!" );
290 #endif
291 
292  // check if object was already created
293  if ( aIt != pImpl->maNameToObjectMap.end() )
294  xObj = (*aIt).second;
295  else
296  xObj = Get_Impl(rName, uno::Reference<embed::XEmbeddedObject>(), pBaseURL);
297 
298  return xObj;
299 }
300 
301 uno::Reference<embed::XEmbeddedObject> EmbeddedObjectContainer::Get_Impl(
302  const OUString& rName,
303  const uno::Reference<embed::XEmbeddedObject>& xCopy,
304  OUString const*const pBaseURL)
305 {
306  uno::Reference < embed::XEmbeddedObject > xObj;
307  try
308  {
309  // create the object from the storage
310  uno::Reference < beans::XPropertySet > xSet( pImpl->mxStorage, uno::UNO_QUERY );
311  bool bReadOnlyMode = true;
312  if ( xSet.is() )
313  {
314  // get the open mode from the parent storage
315  sal_Int32 nMode = 0;
316  uno::Any aAny = xSet->getPropertyValue("OpenMode");
317  if ( aAny >>= nMode )
318  bReadOnlyMode = !(nMode & embed::ElementModes::WRITE );
319  }
320 
321  // object was not added until now - should happen only by calling this method from "inside"
322  //TODO/LATER: it would be good to detect an error when an object should be created already, but isn't (not an "inside" call)
323  uno::Reference < embed::XEmbeddedObjectCreator > xFactory = embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() );
324  uno::Sequence< beans::PropertyValue > aObjDescr(1 + (xCopy.is() ? 1 : 0) + (pBaseURL ? 1 : 0));
325  aObjDescr[0].Name = "Parent";
326  aObjDescr[0].Value <<= pImpl->m_xModel.get();
327  sal_Int32 i = 1;
328  if (pBaseURL)
329  {
330  aObjDescr[i].Name = "DefaultParentBaseURL";
331  aObjDescr[i].Value <<= *pBaseURL;
332  ++i;
333  }
334  if ( xCopy.is() )
335  {
336  aObjDescr[i].Name = "CloneFrom";
337  aObjDescr[i].Value <<= xCopy;
338  }
339 
340  uno::Sequence< beans::PropertyValue > aMediaDescr( 1 );
341  aMediaDescr[0].Name = "ReadOnly";
342  aMediaDescr[0].Value <<= bReadOnlyMode;
343  xObj.set( xFactory->createInstanceInitFromEntry(
344  pImpl->mxStorage, rName,
345  aMediaDescr, aObjDescr ), uno::UNO_QUERY );
346 
347  // insert object into my list
348  AddEmbeddedObject( xObj, rName );
349  }
350  catch (uno::Exception const& e)
351  {
352  SAL_WARN("comphelper.container", "EmbeddedObjectContainer::Get_Impl: exception caught: " << e);
353  }
354 
355  return xObj;
356 }
357 
358 uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::CreateEmbeddedObject( const uno::Sequence < sal_Int8 >& rClassId,
359  const uno::Sequence < beans::PropertyValue >& rArgs, OUString& rNewName, OUString const* pBaseURL )
360 {
361  if ( rNewName.isEmpty() )
362  rNewName = CreateUniqueObjectName();
363 
364  SAL_WARN_IF( HasEmbeddedObject(rNewName), "comphelper.container", "Object to create already exists!");
365 
366  // create object from classid by inserting it into storage
367  uno::Reference < embed::XEmbeddedObject > xObj;
368  try
369  {
370  uno::Reference < embed::XEmbeddedObjectCreator > xFactory = embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() );
371 
372  const size_t nExtraArgs = pBaseURL ? 2 : 1;
373  uno::Sequence< beans::PropertyValue > aObjDescr( rArgs.getLength() + nExtraArgs );
374  aObjDescr[0].Name = "Parent";
375  aObjDescr[0].Value <<= pImpl->m_xModel.get();
376  if (pBaseURL)
377  {
378  aObjDescr[1].Name = "DefaultParentBaseURL";
379  aObjDescr[1].Value <<= *pBaseURL;
380  }
381  std::copy( rArgs.begin(), rArgs.end(), aObjDescr.getArray() + nExtraArgs );
382  xObj.set( xFactory->createInstanceInitNew(
383  rClassId, OUString(), pImpl->mxStorage, rNewName,
384  aObjDescr ), uno::UNO_QUERY );
385 
386  AddEmbeddedObject( xObj, rNewName );
387 
388  OSL_ENSURE( !xObj.is() || xObj->getCurrentState() != embed::EmbedStates::LOADED,
389  "A freshly create object should be running always!" );
390  }
391  catch (uno::Exception const& e)
392  {
393  SAL_WARN("comphelper.container", "EmbeddedObjectContainer::CreateEmbeddedObject: exception caught: " << e);
394  }
395 
396  return xObj;
397 }
398 
399 uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::CreateEmbeddedObject( const uno::Sequence < sal_Int8 >& rClassId, OUString& rNewName, OUString const* pBaseURL )
400 {
401  return CreateEmbeddedObject( rClassId, uno::Sequence < beans::PropertyValue >(), rNewName, pBaseURL );
402 }
403 
404 void EmbeddedObjectContainer::AddEmbeddedObject( const css::uno::Reference < css::embed::XEmbeddedObject >& xObj, const OUString& rName )
405 {
406 #if OSL_DEBUG_LEVEL > 1
407  SAL_WARN_IF( rName.isEmpty(), "comphelper.container", "Added object doesn't have a name!");
408  uno::Reference < container::XNameAccess > xAccess( pImpl->mxStorage, uno::UNO_QUERY );
409  uno::Reference < embed::XEmbedPersist > xEmb( xObj, uno::UNO_QUERY );
410  uno::Reference < embed::XLinkageSupport > xLink( xEmb, uno::UNO_QUERY );
411  // if the object has a persistence and the object is not a link than it must have persistence entry in the storage
412  OSL_ENSURE( !( xEmb.is() && ( !xLink.is() || !xLink->isLink() ) ) || xAccess->hasByName(rName),
413  "Added element not in storage!" );
414 #endif
415 
416  // remember object - it needs to be in storage already
417  auto aIt = pImpl->maNameToObjectMap.find( rName );
418  OSL_ENSURE( aIt == pImpl->maNameToObjectMap.end(), "Element already inserted!" );
419  pImpl->maNameToObjectMap[ rName ] = xObj;
420  pImpl->maObjectToNameMap[ xObj ] = rName;
421  uno::Reference < container::XChild > xChild( xObj, uno::UNO_QUERY );
422  if ( xChild.is() && xChild->getParent() != pImpl->m_xModel.get() )
423  xChild->setParent( pImpl->m_xModel.get() );
424 
425  // look for object in temporary container
426  if ( !pImpl->mpTempObjectContainer )
427  return;
428 
429  auto& rObjectContainer = pImpl->mpTempObjectContainer->pImpl->maNameToObjectMap;
430  auto aIter = std::find_if(rObjectContainer.begin(), rObjectContainer.end(),
431  [&xObj](const EmbeddedObjectContainerNameMap::value_type& rEntry) { return rEntry.second == xObj; });
432  if (aIter == rObjectContainer.end())
433  return;
434 
435  // copy replacement image from temporary container (if there is any)
436  OUString aTempName = aIter->first;
437  OUString aMediaType;
438  uno::Reference < io::XInputStream > xStream = pImpl->mpTempObjectContainer->GetGraphicStream( xObj, &aMediaType );
439  if ( xStream.is() )
440  {
441  InsertGraphicStream( xStream, rName, aMediaType );
442  xStream = nullptr;
443  pImpl->mpTempObjectContainer->RemoveGraphicStream( aTempName );
444  }
445 
446  // remove object from storage of temporary container
447  uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
448  if ( xPersist.is() )
449  {
450  try
451  {
452  pImpl->mpTempObjectContainer->pImpl->mxStorage->removeElement( aTempName );
453  }
454  catch (const uno::Exception&)
455  {
456  }
457  }
458 
459  // temp. container needs to forget the object
460  pImpl->mpTempObjectContainer->pImpl->maObjectToNameMap.erase( aIter->second );
461  pImpl->mpTempObjectContainer->pImpl->maNameToObjectMap.erase( aIter );
462 }
463 
465  const uno::Reference < embed::XEmbeddedObject >& xObj, OUString& rName, bool bCopy,
466  const OUString& rSrcShellID, const OUString& rDestShellID )
467 {
468  uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
469  if ( rName.isEmpty() )
470  rName = CreateUniqueObjectName();
471 
472 #if OSL_DEBUG_LEVEL > 1
473  uno::Reference < container::XNameAccess > xAccess( pImpl->mxStorage, uno::UNO_QUERY );
474  OSL_ENSURE( !xPersist.is() || !xAccess->hasByName(rName), "Inserting element already present in storage!" );
475  OSL_ENSURE( xPersist.is() || xObj->getCurrentState() == embed::EmbedStates::RUNNING, "Non persistent object inserted!");
476 #endif
477 
478  // insert objects' storage into the container storage (if object has one)
479  try
480  {
481  if ( xPersist.is() )
482  {
483  uno::Sequence < beans::PropertyValue > aSeq;
484  if ( bCopy )
485  {
486  auto aObjArgs(::comphelper::InitPropertySequence({
487  { "SourceShellID", uno::Any(rSrcShellID) },
488  { "DestinationShellID", uno::Any(rDestShellID) }
489  }));
490  xPersist->storeToEntry(pImpl->mxStorage, rName, aSeq, aObjArgs);
491  }
492  else
493  {
494  //TODO/LATER: possible optimization, don't store immediately
495  //xPersist->setPersistentEntry( pImpl->mxStorage, rName, embed::EntryInitModes::ENTRY_NO_INIT, aSeq, aSeq );
496  xPersist->storeAsEntry( pImpl->mxStorage, rName, aSeq, aSeq );
497  xPersist->saveCompleted( true );
498  }
499  }
500  }
501  catch (uno::Exception const& e)
502  {
503  SAL_WARN("comphelper.container", "EmbeddedObjectContainer::StoreEmbeddedObject: exception caught: " << e);
504  // TODO/LATER: better error recovery should keep storage intact
505  return false;
506  }
507 
508  return true;
509 }
510 
511 bool EmbeddedObjectContainer::InsertEmbeddedObject( const uno::Reference < embed::XEmbeddedObject >& xObj, OUString& rName )
512 {
513  // store it into the container storage
514  if (StoreEmbeddedObject(xObj, rName, false, OUString(), OUString()))
515  {
516  // remember object
517  AddEmbeddedObject( xObj, rName );
518  return true;
519  }
520  else
521  return false;
522 }
523 
524 uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::InsertEmbeddedObject( const uno::Reference < io::XInputStream >& xStm, OUString& rNewName )
525 {
526  if ( rNewName.isEmpty() )
527  rNewName = CreateUniqueObjectName();
528 
529  // store it into the container storage
530  bool bIsStorage = false;
531  try
532  {
533  // first try storage persistence
534  uno::Reference < embed::XStorage > xStore = ::comphelper::OStorageHelper::GetStorageFromInputStream( xStm );
535 
536  // storage was created from stream successfully
537  bIsStorage = true;
538 
539  uno::Reference < embed::XStorage > xNewStore = pImpl->mxStorage->openStorageElement( rNewName, embed::ElementModes::READWRITE );
540  xStore->copyToStorage( xNewStore );
541  }
542  catch (const uno::Exception&)
543  {
544  if ( bIsStorage )
545  // it is storage persistence, but opening of new substorage or copying to it failed
546  return uno::Reference < embed::XEmbeddedObject >();
547 
548  // stream didn't contain a storage, now try stream persistence
549  try
550  {
551  uno::Reference < io::XStream > xNewStream = pImpl->mxStorage->openStreamElement( rNewName, embed::ElementModes::READWRITE );
552  ::comphelper::OStorageHelper::CopyInputToOutput( xStm, xNewStream->getOutputStream() );
553 
554  // No mediatype is provided so the default for OLE objects value is used
555  // it is correct so for now, but what if somebody introduces a new stream based embedded object?
556  // Probably introducing of such an object must be restricted ( a storage must be used! ).
557  uno::Reference< beans::XPropertySet > xProps( xNewStream, uno::UNO_QUERY_THROW );
558  xProps->setPropertyValue("MediaType",
559  uno::Any( OUString( "application/vnd.sun.star.oleobject" ) ) );
560  }
561  catch (uno::Exception const& e)
562  {
563  // complete disaster!
564  SAL_WARN("comphelper.container", "EmbeddedObjectContainer::InsertEmbeddedObject: exception caught: " << e);
565  return uno::Reference < embed::XEmbeddedObject >();
566  }
567  }
568 
569  // stream was copied into the container storage in either way, now try to open something form it
570  uno::Reference < embed::XEmbeddedObject > xRet = GetEmbeddedObject( rNewName );
571  try
572  {
573  if ( !xRet.is() )
574  // no object could be created, so withdraw insertion
575  pImpl->mxStorage->removeElement( rNewName );
576  }
577  catch (const uno::Exception&)
578  {
579  }
580 
581  return xRet;
582 }
583 
584 uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::InsertEmbeddedObject( const css::uno::Sequence < css::beans::PropertyValue >& aMedium, OUString& rNewName, OUString const* pBaseURL )
585 {
586  if ( rNewName.isEmpty() )
587  rNewName = CreateUniqueObjectName();
588 
589  uno::Reference < embed::XEmbeddedObject > xObj;
590  try
591  {
592  uno::Reference < embed::XEmbeddedObjectCreator > xFactory = embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() );
593  uno::Sequence< beans::PropertyValue > aObjDescr(pBaseURL ? 2 : 1);
594  aObjDescr[0].Name = "Parent";
595  aObjDescr[0].Value <<= pImpl->m_xModel.get();
596  if (pBaseURL)
597  {
598  aObjDescr[1].Name = "DefaultParentBaseURL";
599  aObjDescr[1].Value <<= *pBaseURL;
600  }
601  xObj.set( xFactory->createInstanceInitFromMediaDescriptor(
602  pImpl->mxStorage, rNewName, aMedium, aObjDescr ), uno::UNO_QUERY );
603  uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
604 
605  OSL_ENSURE( !xObj.is() || xObj->getCurrentState() != embed::EmbedStates::LOADED,
606  "A freshly create object should be running always!" );
607 
608  // possible optimization: store later!
609  if ( xPersist.is())
610  xPersist->storeOwn();
611 
612  AddEmbeddedObject( xObj, rNewName );
613  }
614  catch (const uno::Exception&)
615  {
616  }
617 
618  return xObj;
619 }
620 
621 uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::InsertEmbeddedLink( const css::uno::Sequence < css::beans::PropertyValue >& aMedium, OUString& rNewName )
622 {
623  if ( rNewName.isEmpty() )
624  rNewName = CreateUniqueObjectName();
625 
626  uno::Reference < embed::XEmbeddedObject > xObj;
627  try
628  {
629  uno::Reference < embed::XEmbeddedObjectCreator > xFactory = embed::EmbeddedObjectCreator::create(::comphelper::getProcessComponentContext());
630  uno::Sequence< beans::PropertyValue > aObjDescr( 1 );
631  aObjDescr[0].Name = "Parent";
632  aObjDescr[0].Value <<= pImpl->m_xModel.get();
633  xObj.set( xFactory->createInstanceLink( pImpl->mxStorage, rNewName, aMedium, aObjDescr ), uno::UNO_QUERY );
634 
635  uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
636 
637  OSL_ENSURE( !xObj.is() || xObj->getCurrentState() != embed::EmbedStates::LOADED,
638  "A freshly create object should be running always!" );
639 
640  // possible optimization: store later!
641  if ( xPersist.is())
642  xPersist->storeOwn();
643 
644  AddEmbeddedObject( xObj, rNewName );
645  }
646  catch (uno::Exception const& e)
647  {
648  SAL_WARN("comphelper.container", "EmbeddedObjectContainer::InsertEmbeddedLink: "
649  "exception caught: " << e);
650  }
651 
652  return xObj;
653 }
654 
656  const OUString& aOrigName,
657  const OUString& aTargetName )
658 {
659  bool bResult = false;
660 
661  if ( ( &rSrc != this || aOrigName != aTargetName ) && !aOrigName.isEmpty() && !aTargetName.isEmpty() )
662  {
663  OUString aMediaType;
664  uno::Reference < io::XInputStream > xGrStream = rSrc.GetGraphicStream( aOrigName, &aMediaType );
665  if ( xGrStream.is() )
666  bResult = InsertGraphicStream( xGrStream, aTargetName, aMediaType );
667  }
668 
669  return bResult;
670 }
671 
672 uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::CopyAndGetEmbeddedObject(
673  EmbeddedObjectContainer& rSrc, const uno::Reference <embed::XEmbeddedObject>& xObj, OUString& rName,
674  const OUString& rSrcShellID, const OUString& rDestShellID )
675 {
676  uno::Reference< embed::XEmbeddedObject > xResult;
677 
678  // TODO/LATER: For now only objects that implement XEmbedPersist have a replacement image, it might change in future
679  // do an incompatible change so that object name is provided in all the move and copy methods
680  OUString aOrigName;
681  try
682  {
683  uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY_THROW );
684  aOrigName = xPersist->getEntryName();
685  }
686  catch (const uno::Exception&)
687  {
688  }
689 
690  if ( rName.isEmpty() )
691  rName = CreateUniqueObjectName();
692 
693  // objects without persistence are not really stored by the method
694  if (xObj.is() && StoreEmbeddedObject(xObj, rName, true, rSrcShellID, rDestShellID))
695  {
696  SAL_INFO_IF(rDestShellID.isEmpty(), "comphelper.container",
697  "SfxObjectShell with no base URL?"); // every shell has a base URL, except the clipboard SwDocShell
698  xResult = Get_Impl(rName, xObj, &rDestShellID);
699  if ( !xResult.is() )
700  {
701  // this is a case when object has no real persistence
702  // in such cases a new object should be explicitly created and initialized with the data of the old one
703  try
704  {
705  uno::Reference< embed::XLinkageSupport > xOrigLinkage( xObj, uno::UNO_QUERY );
706  if ( xOrigLinkage.is() && xOrigLinkage->isLink() )
707  {
708  // this is an OOo link, it has no persistence
709  OUString aURL = xOrigLinkage->getLinkURL();
710  if ( aURL.isEmpty() )
711  throw uno::RuntimeException();
712 
713  // create new linked object from the URL the link is based on
714  uno::Reference < embed::XEmbeddedObjectCreator > xCreator =
715  embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() );
716 
717  uno::Sequence< beans::PropertyValue > aMediaDescr( 1 );
718  aMediaDescr[0].Name = "URL";
719  aMediaDescr[0].Value <<= aURL;
720  uno::Sequence< beans::PropertyValue > aObjDescr( 1 );
721  aObjDescr[0].Name = "Parent";
722  aObjDescr[0].Value <<= pImpl->m_xModel.get();
723  xResult.set(xCreator->createInstanceLink(
724  pImpl->mxStorage,
725  rName,
726  aMediaDescr,
727  aObjDescr ),
728  uno::UNO_QUERY_THROW );
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( 1 );
744  aObjDescr[0].Name = "Parent";
745  aObjDescr[0].Value <<= pImpl->m_xModel.get();
746  xResult.set(xCreator->createInstanceInitNew(
747  xObj->getClassID(),
748  xObj->getClassName(),
749  pImpl->mxStorage,
750  rName,
751  aObjDescr ),
752  uno::UNO_QUERY_THROW );
753 
754  if ( xResult->getCurrentState() == embed::EmbedStates::LOADED )
755  xResult->changeState( embed::EmbedStates::RUNNING );
756 
757  uno::Reference< beans::XPropertySet > xTargetProps( xResult->getComponent(), uno::UNO_QUERY_THROW );
758 
759  // copy all the properties from xOrigProps to xTargetProps
760  uno::Reference< beans::XPropertySetInfo > xOrigInfo = xOrigProps->getPropertySetInfo();
761  if ( !xOrigInfo.is() )
762  throw uno::RuntimeException();
763 
764  const uno::Sequence< beans::Property > aPropertiesList = xOrigInfo->getProperties();
765  for ( const auto & p : aPropertiesList )
766  {
767  try
768  {
769  xTargetProps->setPropertyValue(
770  p.Name,
771  xOrigProps->getPropertyValue( p.Name ) );
772  }
773  catch (const beans::PropertyVetoException&)
774  {
775  // impossibility to copy readonly property is not treated as an error for now
776  // but the assertion is helpful to detect such scenarios and review them
777  SAL_WARN( "comphelper.container", "Could not copy readonly property!" );
778  }
779  }
780  }
781 
782  if ( xResult.is() )
783  AddEmbeddedObject( xResult, rName );
784  }
785  catch (const uno::Exception&)
786  {
787  if ( xResult.is() )
788  {
789  try
790  {
791  xResult->close( true );
792  }
793  catch (const uno::Exception&)
794  {
795  }
796  xResult.clear();
797  }
798  }
799  }
800  }
801 
802  SAL_WARN_IF( !xResult.is(), "comphelper.container", "Can not copy embedded object that has no persistence!" );
803 
804  if ( xResult.is() )
805  {
806  // the object is successfully copied, try to copy graphical replacement
807  if ( !aOrigName.isEmpty() )
808  TryToCopyGraphReplacement( rSrc, aOrigName, rName );
809 
810  // the object might need the size to be set
811  try
812  {
813  if ( xResult->getStatus( embed::Aspects::MSOLE_CONTENT ) & embed::EmbedMisc::EMBED_NEEDSSIZEONLOAD )
814  xResult->setVisualAreaSize( embed::Aspects::MSOLE_CONTENT,
815  xObj->getVisualAreaSize( embed::Aspects::MSOLE_CONTENT ) );
816  }
817  catch (const uno::Exception&)
818  {
819  }
820  }
821 
822  return xResult;
823 }
824 
825 // #i119941, bKeepToTempStorage: use to specify whether store the removed object to temporary storage+
826 void EmbeddedObjectContainer::RemoveEmbeddedObject( const OUString& rName, bool bKeepToTempStorage )
827 {
828  uno::Reference < embed::XEmbeddedObject > xObj = GetEmbeddedObject( rName );
829  if ( xObj.is() )
830  RemoveEmbeddedObject( xObj, bKeepToTempStorage );
831 }
832 
834 {
835  // find object entry
836  auto aIt2 = rCnt.pImpl->maNameToObjectMap.find( rName );
837  OSL_ENSURE( aIt2 == rCnt.pImpl->maNameToObjectMap.end(), "Object does already exist in target container!" );
838 
839  if ( aIt2 != rCnt.pImpl->maNameToObjectMap.end() )
840  return false;
841 
842  uno::Reference < embed::XEmbeddedObject > xObj;
843  auto aIt = pImpl->maNameToObjectMap.find( rName );
844  if ( aIt != pImpl->maNameToObjectMap.end() )
845  {
846  xObj = (*aIt).second;
847  try
848  {
849  if ( xObj.is() )
850  {
851  // move object
852  OUString aName( rName );
853  rCnt.InsertEmbeddedObject( xObj, aName );
854  pImpl->maObjectToNameMap.erase( aIt->second );
855  pImpl->maNameToObjectMap.erase( aIt );
856  uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
857  if ( xPersist.is() )
858  pImpl->mxStorage->removeElement( rName );
859  }
860  else
861  {
862  // copy storages; object *must* have persistence!
863  uno::Reference < embed::XStorage > xOld = pImpl->mxStorage->openStorageElement( rName, embed::ElementModes::READ );
864  uno::Reference < embed::XStorage > xNew = rCnt.pImpl->mxStorage->openStorageElement( rName, embed::ElementModes::READWRITE );
865  xOld->copyToStorage( xNew );
866  }
867 
868  rCnt.TryToCopyGraphReplacement( *this, rName, rName );
869  // RemoveGraphicStream( rName );
870 
871  return true;
872  }
873  catch (const uno::Exception&)
874  {
875  SAL_WARN( "comphelper.container", "Could not move object!");
876  return false;
877  }
878 
879  }
880  else
881  SAL_WARN( "comphelper.container", "Unknown object!");
882  return false;
883 }
884 
885 // #i119941, bKeepToTempStorage: use to specify whether store the removed object to temporary storage+
886 bool EmbeddedObjectContainer::RemoveEmbeddedObject( const uno::Reference < embed::XEmbeddedObject >& xObj, bool bKeepToTempStorage )
887 {
888  uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
889  OUString aName;
890  if ( xPersist.is() )
891  aName = xPersist->getEntryName();
892 
893 #if OSL_DEBUG_LEVEL > 1
894  uno::Reference < container::XNameAccess > xAccess( pImpl->mxStorage, uno::UNO_QUERY );
895  uno::Reference < embed::XLinkageSupport > xLink( xPersist, uno::UNO_QUERY );
896  sal_Bool bIsNotEmbedded = !xPersist.is() || ( xLink.is() && xLink->isLink() );
897 
898  // if the object has a persistence and the object is not a link than it must have persistence entry in the storage
899  OSL_ENSURE( bIsNotEmbedded || xAccess->hasByName(aName), "Removing element not present in storage!" );
900 #endif
901 
902  // somebody still needs the object, so we must assign a temporary persistence
903  try
904  {
905  if ( xPersist.is() && bKeepToTempStorage ) // #i119941
906  {
907 
908  if ( !pImpl->mpTempObjectContainer )
909  {
910  pImpl->mpTempObjectContainer = new EmbeddedObjectContainer();
911  try
912  {
913  // TODO/LATER: in future probably the temporary container will have two storages ( of two formats )
914  // the media type will be provided with object insertion
915  OUString aOrigStorMediaType;
916  uno::Reference< beans::XPropertySet > xStorProps( pImpl->mxStorage, uno::UNO_QUERY_THROW );
917  static constexpr OUStringLiteral s_sMediaType(u"MediaType");
918  xStorProps->getPropertyValue( s_sMediaType ) >>= aOrigStorMediaType;
919 
920  SAL_WARN_IF( aOrigStorMediaType.isEmpty(), "comphelper.container", "No valuable media type in the storage!" );
921 
922  uno::Reference< beans::XPropertySet > xTargetStorProps(
923  pImpl->mpTempObjectContainer->pImpl->mxStorage,
924  uno::UNO_QUERY_THROW );
925  xTargetStorProps->setPropertyValue( s_sMediaType,uno::Any( aOrigStorMediaType ) );
926  }
927  catch (const uno::Exception&)
928  {
929  SAL_WARN( "comphelper.container", "Can not set the new media type to a storage!" );
930  }
931  }
932 
933  OUString aTempName, aMediaType;
934  /* Do not create a new name for a removed object, in the pImpl->mpTempObjectContainer,
935  because the original m_aEntryName of xObj will be overwritten by InsertEmbeddedObject(),
936  so uno::Reference < embed::XEmbeddedObject >& xObj will misbehave in
937  EmbeddedObjectContainer::StoreAsChildren and SfxObjectShell::SaveCompletedChildren
938  and will throw an exception because of objects with the same names! */
939  if( !pImpl->mpTempObjectContainer->HasEmbeddedObject(aName) )
940  aTempName = aName;
941 
942  pImpl->mpTempObjectContainer->InsertEmbeddedObject( xObj, aTempName );
943 
944  uno::Reference < io::XInputStream > xStream = GetGraphicStream( xObj, &aMediaType );
945  if ( xStream.is() )
946  pImpl->mpTempObjectContainer->InsertGraphicStream( xStream, aTempName, aMediaType );
947 
948  // object is stored, so at least it can be set to loaded state
949  xObj->changeState( embed::EmbedStates::LOADED );
950  }
951  else
952  // objects without persistence need to stay in running state if they shall not be closed
953  xObj->changeState( embed::EmbedStates::RUNNING );
954  }
955  catch (const uno::Exception&)
956  {
957  return false;
958  }
959 
960  auto aIter = std::find_if(pImpl->maNameToObjectMap.begin(), pImpl->maNameToObjectMap.end(),
961  [&xObj](const EmbeddedObjectContainerNameMap::value_type& rEntry) { return rEntry.second == xObj; });
962  if (aIter != pImpl->maNameToObjectMap.end())
963  {
964  pImpl->maObjectToNameMap.erase( aIter->second );
965  pImpl->maNameToObjectMap.erase( aIter );
966  uno::Reference < container::XChild > xChild( xObj, uno::UNO_QUERY );
967  if ( xChild.is() )
968  xChild->setParent( uno::Reference < uno::XInterface >() );
969  }
970  else
971  SAL_WARN( "comphelper.container", "Object not found for removal!" );
972 
973  if ( xPersist.is() && bKeepToTempStorage ) // #i119941#
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 
995  return true;
996 }
997 
998 void EmbeddedObjectContainer::CloseEmbeddedObject( const uno::Reference < embed::XEmbeddedObject >& xObj )
999 {
1000  // disconnect the object from the container and close it if possible
1001 
1002  auto aIter = std::find_if(pImpl->maNameToObjectMap.begin(), pImpl->maNameToObjectMap.end(),
1003  [&xObj](const EmbeddedObjectContainerNameMap::value_type& rEntry) { return rEntry.second == xObj; });
1004  if (aIter == pImpl->maNameToObjectMap.end())
1005  return;
1006 
1007  pImpl->maObjectToNameMap.erase( aIter->second );
1008  pImpl->maNameToObjectMap.erase( aIter );
1009 
1010  try
1011  {
1012  xObj->close( true );
1013  }
1014  catch (const uno::Exception&)
1015  {
1016  // it is no problem if the object is already closed
1017  // TODO/LATER: what if the object can not be closed?
1018  }
1019 }
1020 
1021 uno::Reference < io::XInputStream > EmbeddedObjectContainer::GetGraphicStream( const OUString& aName, OUString* pMediaType )
1022 {
1023  uno::Reference < io::XInputStream > xStream;
1024 
1025  SAL_WARN_IF( aName.isEmpty(), "comphelper.container", "Retrieving graphic for unknown object!" );
1026  if ( !aName.isEmpty() )
1027  {
1028  try
1029  {
1030  uno::Reference < embed::XStorage > xReplacements = pImpl->GetReplacements();
1031  uno::Reference < io::XStream > xGraphicStream = xReplacements->openStreamElement( aName, embed::ElementModes::READ );
1032  xStream = xGraphicStream->getInputStream();
1033  if ( pMediaType )
1034  {
1035  uno::Reference < beans::XPropertySet > xSet( xStream, uno::UNO_QUERY );
1036  if ( xSet.is() )
1037  {
1038  uno::Any aAny = xSet->getPropertyValue("MediaType");
1039  aAny >>= *pMediaType;
1040  }
1041  }
1042  }
1043  catch (uno::Exception const& e)
1044  {
1045  SAL_INFO("comphelper.container",
1046  "EmbeddedObjectContainer::GetGraphicStream(): " << e);
1047  }
1048  }
1049 
1050  return xStream;
1051 }
1052 
1053 uno::Reference < io::XInputStream > EmbeddedObjectContainer::GetGraphicStream( const css::uno::Reference < css::embed::XEmbeddedObject >& xObj, OUString* pMediaType )
1054 {
1055  // try to load it from the container storage
1056  return GetGraphicStream( GetEmbeddedObjectName( xObj ), pMediaType );
1057 }
1058 
1059 bool EmbeddedObjectContainer::InsertGraphicStream( const css::uno::Reference < css::io::XInputStream >& rStream, const OUString& rObjectName, const OUString& rMediaType )
1060 {
1061  try
1062  {
1063  uno::Reference < embed::XStorage > xReplacements = pImpl->GetReplacements();
1064 
1065  // store it into the subfolder
1066  uno::Reference < io::XOutputStream > xOutStream;
1067  uno::Reference < io::XStream > xGraphicStream = xReplacements->openStreamElement( rObjectName,
1068  embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE );
1069  xOutStream = xGraphicStream->getOutputStream();
1070  ::comphelper::OStorageHelper::CopyInputToOutput( rStream, xOutStream );
1071  xOutStream->flush();
1072 
1073  uno::Reference< beans::XPropertySet > xPropSet( xGraphicStream, uno::UNO_QUERY_THROW );
1074 
1075  xPropSet->setPropertyValue("UseCommonStoragePasswordEncryption",
1076  uno::Any( true ) );
1077  xPropSet->setPropertyValue("MediaType", uno::Any(rMediaType) );
1078 
1079  xPropSet->setPropertyValue("Compressed",
1080  uno::Any( true ) );
1081  }
1082  catch (const uno::Exception&)
1083  {
1084  return false;
1085  }
1086 
1087  return true;
1088 }
1089 
1090 bool EmbeddedObjectContainer::InsertGraphicStreamDirectly( const css::uno::Reference < css::io::XInputStream >& rStream, const OUString& rObjectName, const OUString& rMediaType )
1091 {
1092  try
1093  {
1094  uno::Reference < embed::XStorage > xReplacement = pImpl->GetReplacements();
1095  uno::Reference < embed::XOptimizedStorage > xOptRepl( xReplacement, uno::UNO_QUERY_THROW );
1096 
1097  // store it into the subfolder
1098  uno::Sequence< beans::PropertyValue > aProps( 3 );
1099  aProps[0].Name = "MediaType";
1100  aProps[0].Value <<= rMediaType;
1101  aProps[1].Name = "UseCommonStoragePasswordEncryption";
1102  aProps[1].Value <<= true;
1103  aProps[2].Name = "Compressed";
1104  aProps[2].Value <<= true;
1105 
1106  if ( xReplacement->hasByName( rObjectName ) )
1107  xReplacement->removeElement( rObjectName );
1108 
1109  xOptRepl->insertStreamElementDirect( rObjectName, rStream, aProps );
1110  }
1111  catch (const uno::Exception&)
1112  {
1113  return false;
1114  }
1115 
1116  return true;
1117 }
1118 
1119 
1120 void EmbeddedObjectContainer::RemoveGraphicStream( const OUString& rObjectName )
1121 {
1122  try
1123  {
1124  uno::Reference < embed::XStorage > xReplacements = pImpl->GetReplacements();
1125  xReplacements->removeElement( rObjectName );
1126  }
1127  catch (const uno::Exception&)
1128  {
1129  }
1130 }
1131 namespace {
1132  void InsertStreamIntoPicturesStorage_Impl( const uno::Reference< embed::XStorage >& xDocStor,
1133  const uno::Reference< io::XInputStream >& xInStream,
1134  const OUString& aStreamName )
1135  {
1136  OSL_ENSURE( !aStreamName.isEmpty() && xInStream.is() && xDocStor.is(), "Misuse of the method!" );
1137 
1138  try
1139  {
1140  uno::Reference< embed::XStorage > xPictures = xDocStor->openStorageElement(
1141  "Pictures",
1142  embed::ElementModes::READWRITE );
1143  uno::Reference< io::XStream > xObjReplStr = xPictures->openStreamElement(
1144  aStreamName,
1145  embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE );
1146  uno::Reference< io::XOutputStream > xOutStream(
1147  xObjReplStr->getInputStream(), uno::UNO_QUERY_THROW );
1148 
1149  ::comphelper::OStorageHelper::CopyInputToOutput( xInStream, xOutStream );
1150  xOutStream->closeOutput();
1151 
1152  uno::Reference< embed::XTransactedObject > xTransact( xPictures, uno::UNO_QUERY );
1153  if ( xTransact.is() )
1154  xTransact->commit();
1155  }
1156  catch (const uno::Exception&)
1157  {
1158  SAL_WARN( "comphelper.container", "The images storage is not available!" );
1159  }
1160  }
1161 
1162 }
1163 
1164 bool EmbeddedObjectContainer::StoreAsChildren(bool _bOasisFormat,bool _bCreateEmbedded,const uno::Reference < embed::XStorage >& _xStorage)
1165 {
1166  bool bResult = false;
1167  try
1168  {
1169  comphelper::EmbeddedObjectContainer aCnt( _xStorage );
1170  const uno::Sequence < OUString > aNames = GetObjectNames();
1171  const OUString* pIter = aNames.getConstArray();
1172  const OUString* pEnd = pIter + aNames.getLength();
1173  for(;pIter != pEnd;++pIter)
1174  {
1175  uno::Reference < embed::XEmbeddedObject > xObj = GetEmbeddedObject( *pIter );
1176  SAL_WARN_IF( !xObj.is(), "comphelper.container", "An empty entry in the embedded objects list!" );
1177  if ( xObj.is() )
1178  {
1179  bool bSwitchBackToLoaded = false;
1180  uno::Reference< embed::XLinkageSupport > xLink( xObj, uno::UNO_QUERY );
1181 
1182  uno::Reference < io::XInputStream > xStream;
1183  OUString aMediaType;
1184 
1185  sal_Int32 nCurState = xObj->getCurrentState();
1186  if ( nCurState == embed::EmbedStates::LOADED || nCurState == embed::EmbedStates::RUNNING )
1187  {
1188  // means that the object is not active
1189  // copy replacement image from old to new container
1190  xStream = GetGraphicStream( xObj, &aMediaType );
1191  }
1192 
1193  if ( !xStream.is() && getUserAllowsLinkUpdate() )
1194  {
1195  // the image must be regenerated
1196  // TODO/LATER: another aspect could be used
1197  if ( xObj->getCurrentState() == embed::EmbedStates::LOADED )
1198  bSwitchBackToLoaded = true;
1199 
1200  xStream = GetGraphicReplacementStream(
1201  embed::Aspects::MSOLE_CONTENT,
1202  xObj,
1203  &aMediaType );
1204  }
1205 
1206  if ( _bOasisFormat || (xLink.is() && xLink->isLink()) )
1207  {
1208  if ( xStream.is() )
1209  {
1210  if ( _bOasisFormat )
1211  {
1212  // if it is an embedded object or the optimized inserting fails the normal inserting should be done
1213  if ( _bCreateEmbedded
1214  || !aCnt.InsertGraphicStreamDirectly( xStream, *pIter, aMediaType ) )
1215  aCnt.InsertGraphicStream( xStream, *pIter, aMediaType );
1216  }
1217  else
1218  {
1219  // it is a linked object exported into SO7 format
1220  InsertStreamIntoPicturesStorage_Impl( _xStorage, xStream, *pIter );
1221  }
1222  }
1223  }
1224 
1225  uno::Reference< embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
1226  if ( xPersist.is() )
1227  {
1228  uno::Sequence< beans::PropertyValue > aArgs( _bOasisFormat ? 2 : 3 );
1229  aArgs[0].Name = "StoreVisualReplacement";
1230  aArgs[0].Value <<= !_bOasisFormat;
1231 
1232  // if it is an embedded object or the optimized inserting fails the normal inserting should be done
1233  aArgs[1].Name = "CanTryOptimization";
1234  aArgs[1].Value <<= !_bCreateEmbedded;
1235  if ( !_bOasisFormat )
1236  {
1237  // if object has no cached replacement it will use this one
1238  aArgs[2].Name = "VisualReplacement";
1239  aArgs[2].Value <<= xStream;
1240  }
1241 
1242  try
1243  {
1244  xPersist->storeAsEntry( _xStorage, xPersist->getEntryName(), uno::Sequence< beans::PropertyValue >(), aArgs );
1245  }
1246  catch (const embed::WrongStateException&)
1247  {
1248  SAL_WARN("comphelper.container", "failed to store '" << *pIter << "'");
1249  }
1250  }
1251 
1252  if ( bSwitchBackToLoaded )
1253  // switch back to loaded state; that way we have a minimum cache confusion
1254  xObj->changeState( embed::EmbedStates::LOADED );
1255  }
1256  }
1257 
1258  bResult = aCnt.CommitImageSubStorage();
1259 
1260  }
1261  catch (const uno::Exception& e)
1262  {
1263  // TODO/LATER: error handling
1264  bResult = false;
1265  SAL_WARN("comphelper.container", "failed. Message: " << e);
1266  }
1267 
1268  // the old SO6 format does not store graphical replacements
1269  if ( !_bOasisFormat && bResult )
1270  {
1271  try
1272  {
1273  // the substorage still can not be locked by the embedded object container
1274  OUString aObjReplElement( "ObjectReplacements" );
1275  if ( _xStorage->hasByName( aObjReplElement ) && _xStorage->isStorageElement( aObjReplElement ) )
1276  _xStorage->removeElement( aObjReplElement );
1277  }
1278  catch (const uno::Exception&)
1279  {
1280  // TODO/LATER: error handling;
1281  bResult = false;
1282  }
1283  }
1284  return bResult;
1285 }
1286 
1287 bool EmbeddedObjectContainer::StoreChildren(bool _bOasisFormat,bool _bObjectsOnly)
1288 {
1289  bool bResult = true;
1290  const uno::Sequence < OUString > aNames = GetObjectNames();
1291  const OUString* pIter = aNames.getConstArray();
1292  const OUString* pEnd = pIter + aNames.getLength();
1293  for(;pIter != pEnd;++pIter)
1294  {
1295  try
1296  {
1297  uno::Reference < embed::XEmbeddedObject > xObj = GetEmbeddedObject( *pIter );
1298  SAL_WARN_IF( !xObj.is(), "comphelper.container", "An empty entry in the embedded objects list!" );
1299  if ( xObj.is() )
1300  {
1301  sal_Int32 nCurState = xObj->getCurrentState();
1302  if ( _bOasisFormat && nCurState != embed::EmbedStates::LOADED && nCurState != embed::EmbedStates::RUNNING )
1303  {
1304  // means that the object is active
1305  // the image must be regenerated
1306  OUString aMediaType;
1307 
1308  // TODO/LATER: another aspect could be used
1309  uno::Reference < io::XInputStream > xStream =
1311  embed::Aspects::MSOLE_CONTENT,
1312  xObj,
1313  &aMediaType );
1314  if ( xStream.is() )
1315  {
1316  if ( !InsertGraphicStreamDirectly( xStream, *pIter, aMediaType ) )
1317  InsertGraphicStream( xStream, *pIter, aMediaType );
1318  }
1319  }
1320 
1321  // TODO/LATER: currently the object by default does not cache replacement image
1322  // that means that if somebody loads SO7 document and store its objects using
1323  // this method the images might be lost.
1324  // Currently this method is only used on storing to alien formats, that means
1325  // that SO7 documents storing does not use it, and all other filters are
1326  // based on OASIS format. But if it changes the method must be fixed. The fix
1327  // must be done only on demand since it can affect performance.
1328 
1329  uno::Reference< embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
1330  if ( xPersist.is() )
1331  {
1332  try
1333  {
1334  //TODO/LATER: only storing if changed!
1335  //xPersist->storeOwn(); //commented, i120168
1336 
1337  // begin:all charts will be persisted as xml format on disk when saving, which is time consuming.
1338  // '_bObjectsOnly' mean we are storing to alien formats.
1339  // 'isStorageElement' mean current object is NOT a MS OLE format. (may also include in future), i120168
1340  if (_bObjectsOnly && (nCurState == embed::EmbedStates::LOADED || nCurState == embed::EmbedStates::RUNNING)
1341  && (pImpl->mxStorage->isStorageElement( *pIter ) ))
1342  {
1343  uno::Reference< util::XModifiable > xModifiable( xObj->getComponent(), uno::UNO_QUERY );
1344  if ( xModifiable.is() && xModifiable->isModified())
1345  {
1346  xPersist->storeOwn();
1347  }
1348  else
1349  {
1350  //do nothing. Embedded model is not modified, no need to persist.
1351  }
1352  }
1353  else //the embedded object is in active status, always store back it.
1354  {
1355  xPersist->storeOwn();
1356  }
1357  //end i120168
1358  }
1359  catch (const uno::Exception&)
1360  {
1361  // TODO/LATER: error handling
1362  bResult = false;
1363  break;
1364  }
1365  }
1366 
1367  if ( !_bOasisFormat && !_bObjectsOnly )
1368  {
1369  // copy replacement images for linked objects
1370  try
1371  {
1372  uno::Reference< embed::XLinkageSupport > xLink( xObj, uno::UNO_QUERY );
1373  if ( xLink.is() && xLink->isLink() )
1374  {
1375  OUString aMediaType;
1376  uno::Reference < io::XInputStream > xInStream = GetGraphicStream( xObj, &aMediaType );
1377  if ( xInStream.is() )
1378  InsertStreamIntoPicturesStorage_Impl( pImpl->mxStorage, xInStream, *pIter );
1379  }
1380  }
1381  catch (const uno::Exception&)
1382  {
1383  }
1384  }
1385  }
1386  }
1387  catch (const uno::Exception&)
1388  {
1389  // TODO/LATER: error handling
1390  }
1391  }
1392 
1393  if ( bResult && _bOasisFormat )
1394  bResult = CommitImageSubStorage();
1395 
1396  if ( bResult && !_bObjectsOnly )
1397  {
1398  try
1399  {
1401  OUString aObjReplElement( "ObjectReplacements" );
1402  if ( !_bOasisFormat && pImpl->mxStorage->hasByName( aObjReplElement ) && pImpl->mxStorage->isStorageElement( aObjReplElement ) )
1403  pImpl->mxStorage->removeElement( aObjReplElement );
1404  }
1405  catch (const uno::Exception&)
1406  {
1407  // TODO/LATER: error handling
1408  bResult = false;
1409  }
1410  }
1411  return bResult;
1412 }
1413 
1414 uno::Reference< io::XInputStream > EmbeddedObjectContainer::GetGraphicReplacementStream(
1415  sal_Int64 nViewAspect,
1416  const uno::Reference< embed::XEmbeddedObject >& xObj,
1417  OUString* pMediaType )
1418 {
1419  uno::Reference< io::XInputStream > xInStream;
1420  if ( xObj.is() )
1421  {
1422  try
1423  {
1424  // retrieving of the visual representation can switch object to running state
1425  embed::VisualRepresentation aRep = xObj->getPreferredVisualRepresentation( nViewAspect );
1426  if ( pMediaType )
1427  *pMediaType = aRep.Flavor.MimeType;
1428 
1429  uno::Sequence < sal_Int8 > aSeq;
1430  aRep.Data >>= aSeq;
1431  xInStream = new ::comphelper::SequenceInputStream( aSeq );
1432  }
1433  catch (const uno::Exception&)
1434  {
1435  }
1436  }
1437 
1438  return xInStream;
1439 }
1440 
1441 bool EmbeddedObjectContainer::SetPersistentEntries(const uno::Reference< embed::XStorage >& _xStorage,bool _bClearModifiedFlag)
1442 {
1443  bool bError = false;
1444  const uno::Sequence < OUString > aNames = GetObjectNames();
1445  const OUString* pIter = aNames.getConstArray();
1446  const OUString* pEnd = pIter + aNames.getLength();
1447  for(;pIter != pEnd;++pIter)
1448  {
1449  uno::Reference < embed::XEmbeddedObject > xObj = GetEmbeddedObject( *pIter );
1450  SAL_WARN_IF( !xObj.is(), "comphelper.container", "An empty entry in the embedded objects list!" );
1451  if ( xObj.is() )
1452  {
1453  uno::Reference< embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
1454  if ( xPersist.is() )
1455  {
1456  try
1457  {
1458  xPersist->setPersistentEntry( _xStorage,
1459  *pIter,
1460  embed::EntryInitModes::NO_INIT,
1461  uno::Sequence< beans::PropertyValue >(),
1462  uno::Sequence< beans::PropertyValue >() );
1463 
1464  }
1465  catch (const uno::Exception&)
1466  {
1467  // TODO/LATER: error handling
1468  bError = true;
1469  break;
1470  }
1471  }
1472  if ( _bClearModifiedFlag )
1473  {
1474  // if this method is used as part of SaveCompleted the object must stay unmodified after execution
1475  try
1476  {
1477  uno::Reference< util::XModifiable > xModif( xObj->getComponent(), uno::UNO_QUERY_THROW );
1478  if ( xModif->isModified() )
1479  xModif->setModified( false );
1480  }
1481  catch (const uno::Exception&)
1482  {
1483  }
1484  }
1485  }
1486  }
1487  return bError;
1488 }
1489 
1491 {
1492  return pImpl->mbUserAllowsLinkUpdate;
1493 }
1494 
1496 {
1497  if(pImpl->mbUserAllowsLinkUpdate != bNew)
1498  {
1499  pImpl->mbUserAllowsLinkUpdate = bNew;
1500  }
1501 }
1502 
1503 }
1504 
1505 /* 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)
bool StoreAsChildren(bool _bOasisFormat, bool _bCreateEmbedded, const css::uno::Reference< css::embed::XStorage > &_xStorage)
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 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:293
typedef void(CALLTYPE *GetFuncDataPtr)(sal_uInt16 &nNo