Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / embeddedobj / source / commonembedding / persistence.cxx
blob1ccd1588431818ca88a13ba6d0ee0b2ce24486b6
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <commonembobj.hxx>
21 #include <com/sun/star/embed/Aspects.hpp>
22 #include <com/sun/star/document/XStorageBasedDocument.hpp>
23 #include <com/sun/star/embed/EmbedStates.hpp>
24 #include <com/sun/star/embed/EntryInitModes.hpp>
25 #include <com/sun/star/embed/StorageWrappedTargetException.hpp>
26 #include <com/sun/star/embed/WrongStateException.hpp>
27 #include <com/sun/star/embed/XStorage.hpp>
28 #include <com/sun/star/embed/XOptimizedStorage.hpp>
29 #include <com/sun/star/embed/ElementModes.hpp>
30 #include <com/sun/star/embed/EmbedUpdateModes.hpp>
31 #include <com/sun/star/embed/StorageFactory.hpp>
32 #include <com/sun/star/io/IOException.hpp>
33 #include <com/sun/star/io/TempFile.hpp>
34 #include <com/sun/star/frame/XModel.hpp>
35 #include <com/sun/star/frame/XStorable.hpp>
36 #include <com/sun/star/frame/XLoadable.hpp>
37 #include <com/sun/star/frame/XModule.hpp>
38 #include <com/sun/star/lang/NoSupportException.hpp>
39 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
40 #include <com/sun/star/lang/DisposedException.hpp>
41 #include <com/sun/star/util/XModifiable.hpp>
43 #include <com/sun/star/container/XNameAccess.hpp>
44 #include <com/sun/star/container/XChild.hpp>
45 #include <com/sun/star/util/XCloseable.hpp>
46 #include <com/sun/star/beans/XPropertySet.hpp>
47 #include <com/sun/star/beans/IllegalTypeException.hpp>
48 #include <com/sun/star/chart2/XChartDocument.hpp>
50 #include <com/sun/star/ucb/SimpleFileAccess.hpp>
52 #include <comphelper/fileformat.h>
53 #include <comphelper/storagehelper.hxx>
54 #include <comphelper/mimeconfighelper.hxx>
55 #include <comphelper/namedvaluecollection.hxx>
56 #include <comphelper/propertyvalue.hxx>
57 #include <unotools/mediadescriptor.hxx>
59 #include <comphelper/diagnose_ex.hxx>
60 #include <sal/log.hxx>
61 #include <unotools/configmgr.hxx>
62 #include "persistence.hxx"
64 using namespace ::com::sun::star;
67 uno::Sequence< beans::PropertyValue > GetValuableArgs_Impl( const uno::Sequence< beans::PropertyValue >& aMedDescr,
68 bool bCanUseDocumentBaseURL )
70 uno::Sequence< beans::PropertyValue > aResult;
71 sal_Int32 nResLen = 0;
73 for ( beans::PropertyValue const & prop : aMedDescr )
75 if ( prop.Name == "ComponentData" || prop.Name == "DocumentTitle"
76 || prop.Name == "InteractionHandler" || prop.Name == "JumpMark"
77 // || prop.Name == "Password" // makes no sense for embedded objects
78 || prop.Name == "Preview" || prop.Name == "ReadOnly"
79 || prop.Name == "StartPresentation" || prop.Name == "RepairPackage"
80 || prop.Name == "StatusIndicator" || prop.Name == "ViewData"
81 || prop.Name == "ViewId" || prop.Name == "MacroExecutionMode"
82 || prop.Name == "UpdateDocMode"
83 || (prop.Name == "DocumentBaseURL" && bCanUseDocumentBaseURL) )
85 aResult.realloc( ++nResLen );
86 aResult.getArray()[nResLen-1] = prop;
90 return aResult;
94 static uno::Sequence< beans::PropertyValue > addAsTemplate( const uno::Sequence< beans::PropertyValue >& aOrig )
96 bool bAsTemplateSet = false;
97 sal_Int32 nLength = aOrig.getLength();
98 uno::Sequence< beans::PropertyValue > aResult( aOrig );
100 for ( sal_Int32 nInd = 0; nInd < nLength; nInd++ )
102 if ( aResult[nInd].Name == "AsTemplate" )
104 aResult.getArray()[nInd].Value <<= true;
105 bAsTemplateSet = true;
109 if ( !bAsTemplateSet )
111 aResult.realloc( nLength + 1 );
112 auto pResult = aResult.getArray();
113 pResult[nLength].Name = "AsTemplate";
114 pResult[nLength].Value <<= true;
117 return aResult;
121 static uno::Reference< io::XInputStream > createTempInpStreamFromStor(
122 const uno::Reference< embed::XStorage >& xStorage,
123 const uno::Reference< uno::XComponentContext >& xContext )
125 SAL_WARN_IF( !xStorage.is(), "embeddedobj.common", "The storage can not be empty!" );
127 uno::Reference< io::XInputStream > xResult;
129 uno::Reference < io::XStream > xTempStream( io::TempFile::create(xContext), uno::UNO_QUERY_THROW );
131 uno::Reference < lang::XSingleServiceFactory > xStorageFactory( embed::StorageFactory::create(xContext) );
133 uno::Sequence< uno::Any > aArgs{ uno::Any(xTempStream),
134 uno::Any(embed::ElementModes::READWRITE) };
135 uno::Reference< embed::XStorage > xTempStorage( xStorageFactory->createInstanceWithArguments( aArgs ),
136 uno::UNO_QUERY_THROW );
140 xStorage->copyToStorage( xTempStorage );
141 } catch( const uno::Exception& )
143 css::uno::Any anyEx = cppu::getCaughtException();
144 throw embed::StorageWrappedTargetException(
145 "Can't copy storage!",
146 uno::Reference< uno::XInterface >(),
147 anyEx );
150 try {
151 if ( xTempStorage.is() )
152 xTempStorage->dispose();
154 catch ( const uno::Exception& )
158 try {
159 uno::Reference< io::XOutputStream > xTempOut = xTempStream->getOutputStream();
160 if ( xTempOut.is() )
161 xTempOut->closeOutput();
163 catch ( const uno::Exception& )
167 xResult = xTempStream->getInputStream();
169 return xResult;
174 static void TransferMediaType( const uno::Reference< embed::XStorage >& i_rSource, const uno::Reference< embed::XStorage >& i_rTarget )
178 const uno::Reference< beans::XPropertySet > xSourceProps( i_rSource, uno::UNO_QUERY_THROW );
179 const uno::Reference< beans::XPropertySet > xTargetProps( i_rTarget, uno::UNO_QUERY_THROW );
180 static const OUStringLiteral sMediaTypePropName( u"MediaType" );
181 xTargetProps->setPropertyValue( sMediaTypePropName, xSourceProps->getPropertyValue( sMediaTypePropName ) );
183 catch( const uno::Exception& )
185 DBG_UNHANDLED_EXCEPTION("embeddedobj.common");
190 static uno::Reference< util::XCloseable > CreateDocument( const uno::Reference< uno::XComponentContext >& _rxContext,
191 const OUString& _rDocumentServiceName, bool _bEmbeddedScriptSupport, const bool i_bDocumentRecoverySupport )
193 static constexpr OUStringLiteral sEmbeddedObject = u"EmbeddedObject";
194 static constexpr OUStringLiteral sEmbeddedScriptSupport = u"EmbeddedScriptSupport";
195 static constexpr OUStringLiteral sDocumentRecoverySupport = u"DocumentRecoverySupport";
196 ::comphelper::NamedValueCollection aArguments;
197 aArguments.put( sEmbeddedObject, true );
198 aArguments.put( sEmbeddedScriptSupport, _bEmbeddedScriptSupport );
199 aArguments.put( sDocumentRecoverySupport, i_bDocumentRecoverySupport );
201 uno::Reference< uno::XInterface > xDocument;
204 xDocument = _rxContext->getServiceManager()->createInstanceWithArgumentsAndContext(
205 _rDocumentServiceName, aArguments.getWrappedPropertyValues(), _rxContext );
207 catch( const uno::Exception& )
209 // if an embedded object implementation does not support XInitialization,
210 // the default factory from cppuhelper will throw an
211 // IllegalArgumentException when we try to create the instance with arguments.
212 // Okay, so we fall back to creating the instance without any arguments.
213 OSL_FAIL("Consider implementing interface XInitialization to avoid duplicate construction");
214 xDocument = _rxContext->getServiceManager()->createInstanceWithContext( _rDocumentServiceName, _rxContext );
217 SAL_WARN_IF(!xDocument.is(), "embeddedobj.common", "Service " << _rDocumentServiceName << " is not available?");
218 return uno::Reference< util::XCloseable >( xDocument, uno::UNO_QUERY );
222 static void SetDocToEmbedded( const uno::Reference< frame::XModel >& rDocument, const OUString& aModuleName )
224 if (!rDocument.is())
225 return;
227 uno::Sequence< beans::PropertyValue > aSeq{ comphelper::makePropertyValue("SetEmbedded", true) };
228 rDocument->attachResource( OUString(), aSeq );
230 if ( !aModuleName.isEmpty() )
234 uno::Reference< frame::XModule > xModule( rDocument, uno::UNO_QUERY_THROW );
235 xModule->setIdentifier( aModuleName );
237 catch( const uno::Exception& )
243 void OCommonEmbeddedObject::SwitchOwnPersistence( const uno::Reference< embed::XStorage >& xNewParentStorage,
244 const uno::Reference< embed::XStorage >& xNewObjectStorage,
245 const OUString& aNewName )
247 if ( xNewParentStorage == m_xParentStorage && aNewName == m_aEntryName )
249 SAL_WARN_IF( xNewObjectStorage != m_xObjectStorage, "embeddedobj.common", "The storage must be the same!" );
250 return;
253 auto xOldObjectStorage = m_xObjectStorage;
254 m_xObjectStorage = xNewObjectStorage;
255 m_xParentStorage = xNewParentStorage;
256 m_aEntryName = aNewName;
258 // the linked document should not be switched
259 if ( !m_bIsLinkURL )
261 uno::Reference< document::XStorageBasedDocument > xDoc( m_xDocHolder->GetComponent(), uno::UNO_QUERY );
262 if ( xDoc.is() )
263 SwitchDocToStorage_Impl( xDoc, m_xObjectStorage );
266 try {
267 if ( xOldObjectStorage.is() )
268 xOldObjectStorage->dispose();
270 catch ( const uno::Exception& )
276 void OCommonEmbeddedObject::SwitchOwnPersistence( const uno::Reference< embed::XStorage >& xNewParentStorage,
277 const OUString& aNewName )
279 if ( xNewParentStorage == m_xParentStorage && aNewName == m_aEntryName )
280 return;
282 sal_Int32 nStorageMode = m_bReadOnly ? embed::ElementModes::READ : embed::ElementModes::READWRITE;
284 uno::Reference< embed::XStorage > xNewOwnStorage = xNewParentStorage->openStorageElement( aNewName, nStorageMode );
285 SAL_WARN_IF( !xNewOwnStorage.is(), "embeddedobj.common", "The method can not return empty reference!" );
287 SwitchOwnPersistence( xNewParentStorage, xNewOwnStorage, aNewName );
291 void OCommonEmbeddedObject::EmbedAndReparentDoc_Impl( const uno::Reference< util::XCloseable >& i_rxDocument ) const
293 SetDocToEmbedded( uno::Reference< frame::XModel >( i_rxDocument, uno::UNO_QUERY ), m_aModuleName );
297 uno::Reference < container::XChild > xChild( i_rxDocument, uno::UNO_QUERY );
298 if ( xChild.is() )
299 xChild->setParent( m_xParent );
301 catch( const lang::NoSupportException & )
303 SAL_WARN( "embeddedobj.common", "OCommonEmbeddedObject::EmbedAndReparentDoc: cannot set parent at document!" );
308 uno::Reference< util::XCloseable > OCommonEmbeddedObject::InitNewDocument_Impl()
310 uno::Reference< util::XCloseable > xDocument( CreateDocument( m_xContext, GetDocumentServiceName(),
311 m_bEmbeddedScriptSupport, m_bDocumentRecoverySupport ) );
313 uno::Reference< frame::XModel > xModel( xDocument, uno::UNO_QUERY );
314 uno::Reference< frame::XLoadable > xLoadable( xModel, uno::UNO_QUERY_THROW );
318 // set the document mode to embedded as the first action on document!!!
319 EmbedAndReparentDoc_Impl( xDocument );
321 // if we have a storage to recover the document from, do not use initNew, but instead load from that storage
322 bool bInitNew = true;
323 if ( m_xRecoveryStorage.is() )
325 uno::Reference< document::XStorageBasedDocument > xDoc( xLoadable, uno::UNO_QUERY );
326 SAL_WARN_IF( !xDoc.is(), "embeddedobj.common", "OCommonEmbeddedObject::InitNewDocument_Impl: cannot recover from a storage when the document is not storage based!" );
327 if ( xDoc.is() )
329 ::comphelper::NamedValueCollection aLoadArgs;
330 FillDefaultLoadArgs_Impl( m_xRecoveryStorage, aLoadArgs );
332 xDoc->loadFromStorage( m_xRecoveryStorage, aLoadArgs.getPropertyValues() );
333 SwitchDocToStorage_Impl( xDoc, m_xObjectStorage );
334 bInitNew = false;
338 if ( bInitNew )
340 // init document as a new
341 xLoadable->initNew();
343 xModel->attachResource( xModel->getURL(), m_aDocMediaDescriptor );
345 catch( const uno::Exception& )
347 if ( xDocument.is() )
351 xDocument->close( true );
353 catch( const uno::Exception& )
358 throw; // TODO
361 return xDocument;
365 uno::Reference< util::XCloseable > OCommonEmbeddedObject::LoadLink_Impl()
367 uno::Reference< util::XCloseable > xDocument( CreateDocument( m_xContext, GetDocumentServiceName(),
368 m_bEmbeddedScriptSupport, m_bDocumentRecoverySupport ) );
370 uno::Reference< frame::XLoadable > xLoadable( xDocument, uno::UNO_QUERY_THROW );
372 sal_Int32 nLen = m_bLinkHasPassword ? 3 : 2;
373 uno::Sequence< beans::PropertyValue > aArgs( m_aDocMediaDescriptor.getLength() + nLen );
374 auto pArgs = aArgs.getArray();
376 pArgs[0].Name = "URL";
377 if(m_aLinkTempFile.is())
378 pArgs[0].Value <<= m_aLinkTempFile->getUri();
379 else
380 pArgs[0].Value <<= m_aLinkURL;
382 pArgs[1].Name = "FilterName";
383 pArgs[1].Value <<= m_aLinkFilterName;
385 if ( m_bLinkHasPassword )
387 pArgs[2].Name = "Password";
388 pArgs[2].Value <<= m_aLinkPassword;
391 for ( sal_Int32 nInd = 0; nInd < m_aDocMediaDescriptor.getLength(); nInd++ )
393 pArgs[nInd+nLen].Name = m_aDocMediaDescriptor[nInd].Name;
394 pArgs[nInd+nLen].Value = m_aDocMediaDescriptor[nInd].Value;
399 handleLinkedOLE(CopyBackToOLELink::CopyLinkToTemp);
401 // the document is not really an embedded one, it is a link
402 EmbedAndReparentDoc_Impl( xDocument );
404 // load the document
405 xLoadable->load( aArgs );
407 if ( !m_bLinkHasPassword )
409 // check if there is a password to cache
410 uno::Reference< frame::XModel > xModel( xLoadable, uno::UNO_QUERY_THROW );
411 const uno::Sequence< beans::PropertyValue > aProps = xModel->getArgs();
412 for ( beans::PropertyValue const & prop : aProps )
413 if ( prop.Name == "Password" && ( prop.Value >>= m_aLinkPassword ) )
415 m_bLinkHasPassword = true;
416 break;
420 catch( const uno::Exception& )
422 if ( xDocument.is() )
426 xDocument->close( true );
428 catch( const uno::Exception& )
433 throw; // TODO
436 return xDocument;
441 OUString OCommonEmbeddedObject::GetFilterName( sal_Int32 nVersion ) const
443 OUString aFilterName = GetPresetFilterName();
444 if ( aFilterName.isEmpty() )
446 OUString sDocumentServiceName = GetDocumentServiceName();
447 if (utl::ConfigManager::IsFuzzing() && nVersion == SOFFICE_FILEFORMAT_CURRENT &&
448 sDocumentServiceName == "com.sun.star.chart2.ChartDocument")
450 return "chart8";
452 try {
453 ::comphelper::MimeConfigurationHelper aHelper( m_xContext );
454 aFilterName = aHelper.GetDefaultFilterFromServiceName(sDocumentServiceName, nVersion);
456 // If no filter is found, fall back to the FileFormatVersion=6200 filter, Base only has that.
457 if (aFilterName.isEmpty() && nVersion == SOFFICE_FILEFORMAT_CURRENT)
458 aFilterName = aHelper.GetDefaultFilterFromServiceName(GetDocumentServiceName(), SOFFICE_FILEFORMAT_60);
459 } catch( const uno::Exception& )
463 return aFilterName;
467 void OCommonEmbeddedObject::FillDefaultLoadArgs_Impl( const uno::Reference< embed::XStorage >& i_rxStorage,
468 ::comphelper::NamedValueCollection& o_rLoadArgs ) const
470 o_rLoadArgs.put( "DocumentBaseURL", GetBaseURL_Impl() );
471 o_rLoadArgs.put( "HierarchicalDocumentName", m_aEntryName );
472 o_rLoadArgs.put( "ReadOnly", m_bReadOnly );
474 OUString aFilterName = GetFilterName( ::comphelper::OStorageHelper::GetXStorageFormat( i_rxStorage ) );
475 SAL_WARN_IF( aFilterName.isEmpty(), "embeddedobj.common", "OCommonEmbeddedObject::FillDefaultLoadArgs_Impl: Wrong document service name!" );
476 if ( aFilterName.isEmpty() )
477 throw io::IOException(); // TODO: error message/code
479 o_rLoadArgs.put( "FilterName", aFilterName );
483 uno::Reference< util::XCloseable > OCommonEmbeddedObject::LoadDocumentFromStorage_Impl()
485 ENSURE_OR_THROW( m_xObjectStorage.is(), "no object storage" );
487 const uno::Reference< embed::XStorage > xSourceStorage( m_xRecoveryStorage.is() ? m_xRecoveryStorage : m_xObjectStorage );
489 uno::Reference< util::XCloseable > xDocument( CreateDocument( m_xContext, GetDocumentServiceName(),
490 m_bEmbeddedScriptSupport, m_bDocumentRecoverySupport ) );
492 //#i103460# ODF: take the size given from the parent frame as default
493 uno::Reference< chart2::XChartDocument > xChart( xDocument, uno::UNO_QUERY );
494 if( xChart.is() )
496 uno::Reference< embed::XVisualObject > xChartVisualObject( xChart, uno::UNO_QUERY );
497 if( xChartVisualObject.is() )
498 xChartVisualObject->setVisualAreaSize( embed::Aspects::MSOLE_CONTENT, m_aDefaultSizeForChart_In_100TH_MM );
501 uno::Reference< frame::XLoadable > xLoadable( xDocument, uno::UNO_QUERY );
502 uno::Reference< document::XStorageBasedDocument > xDoc( xDocument, uno::UNO_QUERY );
503 if ( !xDoc.is() && !xLoadable.is() )
504 throw uno::RuntimeException();
506 ::comphelper::NamedValueCollection aLoadArgs;
507 FillDefaultLoadArgs_Impl( xSourceStorage, aLoadArgs );
509 uno::Reference< io::XInputStream > xTempInpStream;
510 if ( !xDoc.is() )
512 xTempInpStream = createTempInpStreamFromStor( xSourceStorage, m_xContext );
513 if ( !xTempInpStream.is() )
514 throw uno::RuntimeException();
516 OUString aTempFileURL;
519 // no need to let the file stay after the stream is removed since the embedded document
520 // can not be stored directly
521 uno::Reference< beans::XPropertySet > xTempStreamProps( xTempInpStream, uno::UNO_QUERY_THROW );
522 xTempStreamProps->getPropertyValue("Uri") >>= aTempFileURL;
524 catch( const uno::Exception& )
528 SAL_WARN_IF( aTempFileURL.isEmpty(), "embeddedobj.common", "Couldn't retrieve temporary file URL!" );
530 aLoadArgs.put( "URL", aTempFileURL );
531 aLoadArgs.put( "InputStream", xTempInpStream );
535 aLoadArgs.merge( m_aDocMediaDescriptor, true );
539 // set the document mode to embedded as the first step!!!
540 EmbedAndReparentDoc_Impl( xDocument );
542 if (m_bReadOnly)
544 aLoadArgs.put("ReadOnly", true);
547 if ( xDoc.is() )
549 xDoc->loadFromStorage( xSourceStorage, aLoadArgs.getPropertyValues() );
550 if ( xSourceStorage != m_xObjectStorage )
551 SwitchDocToStorage_Impl( xDoc, m_xObjectStorage );
553 else
554 xLoadable->load( aLoadArgs.getPropertyValues() );
556 catch( const uno::Exception& )
558 if ( xDocument.is() )
562 xDocument->close( true );
564 catch( const uno::Exception& )
566 DBG_UNHANDLED_EXCEPTION("embeddedobj.common");
570 throw; // TODO
573 return xDocument;
577 uno::Reference< io::XInputStream > OCommonEmbeddedObject::StoreDocumentToTempStream_Impl(
578 sal_Int32 nStorageFormat,
579 const OUString& aBaseURL,
580 const OUString& aHierarchName )
582 uno::Reference < io::XOutputStream > xTempOut(
583 io::TempFile::create(m_xContext),
584 uno::UNO_QUERY_THROW );
585 uno::Reference< io::XInputStream > aResult( xTempOut, uno::UNO_QUERY_THROW );
587 uno::Reference< frame::XStorable > xStorable;
589 osl::MutexGuard aGuard( m_aMutex );
590 if ( m_xDocHolder.is() )
591 xStorable.set( m_xDocHolder->GetComponent(), uno::UNO_QUERY );
594 if( !xStorable.is() )
595 throw uno::RuntimeException("No storage is provided for storing!"); // TODO:
597 OUString aFilterName = GetFilterName( nStorageFormat );
599 SAL_WARN_IF( aFilterName.isEmpty(), "embeddedobj.common", "Wrong document service name!" );
600 if ( aFilterName.isEmpty() )
601 throw io::IOException("No filter name provided / Wrong document service name"); // TODO:
603 uno::Sequence< beans::PropertyValue > aArgs{
604 comphelper::makePropertyValue("FilterName", aFilterName),
605 comphelper::makePropertyValue("OutputStream", xTempOut),
606 comphelper::makePropertyValue("DocumentBaseURL", aBaseURL),
607 comphelper::makePropertyValue("HierarchicalDocumentName", aHierarchName)
610 xStorable->storeToURL( "private:stream", aArgs );
613 xTempOut->closeOutput();
615 catch( const uno::Exception& )
617 SAL_WARN( "embeddedobj.common", "Looks like stream was closed already" );
620 return aResult;
624 void OCommonEmbeddedObject::SaveObject_Impl()
626 if ( !m_xClientSite.is() )
627 return;
631 // check whether the component is modified,
632 // if not there is no need for storing
633 uno::Reference< util::XModifiable > xModifiable( m_xDocHolder->GetComponent(), uno::UNO_QUERY );
634 if ( xModifiable.is() && !xModifiable->isModified() )
635 return;
637 catch( const uno::Exception& )
640 try {
641 m_xClientSite->saveObject();
643 catch( const uno::Exception& )
645 SAL_WARN( "embeddedobj.common", "The object was not stored!" );
650 OUString OCommonEmbeddedObject::GetBaseURL_Impl() const
652 OUString aBaseURL;
654 if ( m_xClientSite.is() )
658 uno::Reference< frame::XModel > xParentModel( m_xClientSite->getComponent(), uno::UNO_QUERY_THROW );
659 const uno::Sequence< beans::PropertyValue > aModelProps = xParentModel->getArgs();
660 for ( beans::PropertyValue const & prop : aModelProps )
661 if ( prop.Name == "DocumentBaseURL" )
663 prop.Value >>= aBaseURL;
664 break;
667 catch( const uno::Exception& )
671 if ( aBaseURL.isEmpty() )
673 for ( beans::PropertyValue const & prop : m_aDocMediaDescriptor )
674 if ( prop.Name == "DocumentBaseURL" )
676 prop.Value >>= aBaseURL;
677 break;
681 if ( aBaseURL.isEmpty() )
682 aBaseURL = m_aDefaultParentBaseURL;
684 return aBaseURL;
688 OUString OCommonEmbeddedObject::GetBaseURLFrom_Impl(
689 const uno::Sequence< beans::PropertyValue >& lArguments,
690 const uno::Sequence< beans::PropertyValue >& lObjArgs )
692 OUString aBaseURL;
694 for ( beans::PropertyValue const & prop : lArguments )
695 if ( prop.Name == "DocumentBaseURL" )
697 prop.Value >>= aBaseURL;
698 break;
701 if ( aBaseURL.isEmpty() )
703 for ( beans::PropertyValue const & prop : lObjArgs )
704 if ( prop.Name == "DefaultParentBaseURL" )
706 prop.Value >>= aBaseURL;
707 break;
711 return aBaseURL;
715 void OCommonEmbeddedObject::SwitchDocToStorage_Impl( const uno::Reference< document::XStorageBasedDocument >& xDoc, const uno::Reference< embed::XStorage >& xStorage )
717 xDoc->switchToStorage( xStorage );
719 uno::Reference< util::XModifiable > xModif( xDoc, uno::UNO_QUERY );
720 if ( xModif.is() )
721 xModif->setModified( false );
723 if ( m_xRecoveryStorage.is() )
724 m_xRecoveryStorage.clear();
727 namespace {
729 OUString getStringPropertyValue( const uno::Sequence<beans::PropertyValue>& rProps, std::u16string_view rName )
731 OUString aStr;
733 for (beans::PropertyValue const & prop : rProps)
735 if (prop.Name == rName)
737 prop.Value >>= aStr;
738 break;
742 return aStr;
747 void OCommonEmbeddedObject::StoreDocToStorage_Impl(
748 const uno::Reference<embed::XStorage>& xStorage,
749 const uno::Sequence<beans::PropertyValue>& rMediaArgs,
750 const uno::Sequence<beans::PropertyValue>& rObjArgs,
751 sal_Int32 nStorageFormat,
752 const OUString& aHierarchName,
753 bool bAttachToTheStorage )
755 SAL_WARN_IF( !xStorage.is(), "embeddedobj.common", "No storage is provided for storing!" );
757 if ( !xStorage.is() )
758 throw uno::RuntimeException(); // TODO:
760 uno::Reference< document::XStorageBasedDocument > xDoc;
762 osl::MutexGuard aGuard( m_aMutex );
763 if ( m_xDocHolder.is() )
764 xDoc.set( m_xDocHolder->GetComponent(), uno::UNO_QUERY );
767 OUString aBaseURL = GetBaseURLFrom_Impl(rMediaArgs, rObjArgs);
769 if ( xDoc.is() )
771 OUString aFilterName = GetFilterName( nStorageFormat );
773 // No filter found? Try the older format, e.g. Base has only that.
774 if (aFilterName.isEmpty() && nStorageFormat == SOFFICE_FILEFORMAT_CURRENT)
775 aFilterName = GetFilterName( SOFFICE_FILEFORMAT_60 );
777 SAL_WARN_IF( aFilterName.isEmpty(), "embeddedobj.common", "Wrong document service name!" );
778 if ( aFilterName.isEmpty() )
779 throw io::IOException(); // TODO:
781 static constexpr OUStringLiteral sFilterName = u"FilterName";
782 static constexpr OUStringLiteral sHierarchicalDocumentName = u"HierarchicalDocumentName";
783 static constexpr OUStringLiteral sDocumentBaseURL = u"DocumentBaseURL";
784 static constexpr OUStringLiteral sSourceShellID = u"SourceShellID";
785 static constexpr OUStringLiteral sDestinationShellID = u"DestinationShellID";
786 uno::Sequence<beans::PropertyValue> aArgs{
787 comphelper::makePropertyValue(sFilterName, aFilterName),
788 comphelper::makePropertyValue(sHierarchicalDocumentName, aHierarchName),
789 comphelper::makePropertyValue(sDocumentBaseURL, aBaseURL),
790 comphelper::makePropertyValue(sSourceShellID,
791 getStringPropertyValue(rObjArgs, sSourceShellID)),
792 comphelper::makePropertyValue(
793 sDestinationShellID, getStringPropertyValue(rObjArgs, sDestinationShellID))
796 xDoc->storeToStorage( xStorage, aArgs );
797 if ( bAttachToTheStorage )
798 SwitchDocToStorage_Impl( xDoc, xStorage );
800 else
802 // store document to temporary stream based on temporary file
803 uno::Reference < io::XInputStream > xTempIn = StoreDocumentToTempStream_Impl( nStorageFormat, aBaseURL, aHierarchName );
805 SAL_WARN_IF( !xTempIn.is(), "embeddedobj.common", "The stream reference can not be empty!" );
807 // open storage based on document temporary file for reading
808 uno::Reference < lang::XSingleServiceFactory > xStorageFactory = embed::StorageFactory::create(m_xContext);
810 uno::Sequence< uno::Any > aArgs{ uno::Any(xTempIn) };
811 uno::Reference< embed::XStorage > xTempStorage( xStorageFactory->createInstanceWithArguments( aArgs ),
812 uno::UNO_QUERY_THROW );
814 // object storage must be committed automatically
815 xTempStorage->copyToStorage( xStorage );
820 uno::Reference< util::XCloseable > OCommonEmbeddedObject::CreateDocFromMediaDescr_Impl(
821 const uno::Sequence< beans::PropertyValue >& aMedDescr )
823 uno::Reference< util::XCloseable > xDocument( CreateDocument( m_xContext, GetDocumentServiceName(),
824 m_bEmbeddedScriptSupport, m_bDocumentRecoverySupport ) );
826 uno::Reference< frame::XLoadable > xLoadable( xDocument, uno::UNO_QUERY_THROW );
830 // set the document mode to embedded as the first action on the document!!!
831 EmbedAndReparentDoc_Impl( xDocument );
833 xLoadable->load( addAsTemplate( aMedDescr ) );
835 catch( const uno::Exception& )
837 if ( xDocument.is() )
841 xDocument->close( true );
843 catch( const uno::Exception& )
848 throw; // TODO
851 return xDocument;
855 uno::Reference< util::XCloseable > OCommonEmbeddedObject::CreateTempDocFromLink_Impl()
857 uno::Reference< util::XCloseable > xResult;
859 SAL_WARN_IF( !m_bIsLinkURL, "embeddedobj.common", "The object is not a linked one!" );
861 uno::Sequence< beans::PropertyValue > aTempMediaDescr;
863 sal_Int32 nStorageFormat = SOFFICE_FILEFORMAT_CURRENT;
864 try {
865 nStorageFormat = ::comphelper::OStorageHelper::GetXStorageFormat( m_xParentStorage );
867 catch ( const beans::IllegalTypeException& )
869 // the container just has an unknown type, use current file format
871 catch ( const uno::Exception& )
873 SAL_WARN( "embeddedobj.common", "Can not retrieve storage media type!" );
876 if ( m_xDocHolder->GetComponent().is() )
878 aTempMediaDescr.realloc( 4 );
880 // TODO/LATER: may be private:stream should be used as target URL
881 OUString aTempFileURL;
882 uno::Reference< io::XInputStream > xTempStream = StoreDocumentToTempStream_Impl( SOFFICE_FILEFORMAT_CURRENT,
883 OUString(),
884 OUString() );
887 // no need to let the file stay after the stream is removed since the embedded document
888 // can not be stored directly
889 uno::Reference< beans::XPropertySet > xTempStreamProps( xTempStream, uno::UNO_QUERY_THROW );
890 xTempStreamProps->getPropertyValue("Uri") >>= aTempFileURL;
892 catch( const uno::Exception& )
896 SAL_WARN_IF( aTempFileURL.isEmpty(), "embeddedobj.common", "Couldn't retrieve temporary file URL!" );
898 aTempMediaDescr
899 = { comphelper::makePropertyValue("URL", aTempFileURL),
900 comphelper::makePropertyValue("InputStream", xTempStream),
901 comphelper::makePropertyValue("FilterName", GetFilterName( nStorageFormat )),
902 comphelper::makePropertyValue("AsTemplate", true) };
904 else
906 aTempMediaDescr = { comphelper::makePropertyValue(
907 "URL",
908 // tdf#141529 use URL of the linked TempFile if it exists
909 m_aLinkTempFile.is() ? m_aLinkTempFile->getUri() : m_aLinkURL),
910 comphelper::makePropertyValue("FilterName", m_aLinkFilterName) };
913 xResult = CreateDocFromMediaDescr_Impl( aTempMediaDescr );
915 return xResult;
919 void SAL_CALL OCommonEmbeddedObject::setPersistentEntry(
920 const uno::Reference< embed::XStorage >& xStorage,
921 const OUString& sEntName,
922 sal_Int32 nEntryConnectionMode,
923 const uno::Sequence< beans::PropertyValue >& lArguments,
924 const uno::Sequence< beans::PropertyValue >& lObjArgs )
926 // the type of the object must be already set
927 // a kind of typedetection should be done in the factory
929 ::osl::MutexGuard aGuard( m_aMutex );
930 if ( m_bDisposed )
931 throw lang::DisposedException(); // TODO
933 if ( !xStorage.is() )
934 throw lang::IllegalArgumentException( "No parent storage is provided!",
935 static_cast< ::cppu::OWeakObject* >(this),
936 1 );
938 if ( sEntName.isEmpty() )
939 throw lang::IllegalArgumentException( "Empty element name is provided!",
940 static_cast< ::cppu::OWeakObject* >(this),
941 2 );
943 // May be LOADED should be forbidden here ???
944 if ( ( m_nObjectState != -1 || nEntryConnectionMode == embed::EntryInitModes::NO_INIT )
945 && ( m_nObjectState == -1 || nEntryConnectionMode != embed::EntryInitModes::NO_INIT ) )
947 // if the object is not loaded
948 // it can not get persistent representation without initialization
950 // if the object is loaded
951 // it can switch persistent representation only without initialization
953 throw embed::WrongStateException(
954 "Can't change persistent representation of activated object!",
955 static_cast< ::cppu::OWeakObject* >(this) );
958 if ( m_bWaitSaveCompleted )
960 if ( nEntryConnectionMode != embed::EntryInitModes::NO_INIT )
961 throw embed::WrongStateException(
962 "The object waits for saveCompleted() call!",
963 static_cast< ::cppu::OWeakObject* >(this) );
964 // saveCompleted is expected, handle it accordingly
965 if ( m_xNewParentStorage == xStorage && m_aNewEntryName == sEntName )
967 saveCompleted( true );
968 return;
971 // if a completely different entry is provided, switch first back to the old persistence in saveCompleted
972 // and then switch to the target persistence
973 bool bSwitchFurther = ( m_xParentStorage != xStorage || m_aEntryName != sEntName );
974 saveCompleted( false );
975 if ( !bSwitchFurther )
976 return;
979 // for now support of this interface is required to allow breaking of links and converting them to normal embedded
980 // objects, so the persist name must be handled correctly ( althowgh no real persist entry is used )
981 // OSL_ENSURE( !m_bIsLinkURL, "This method implementation must not be used for links!" );
982 if ( m_bIsLinkURL )
984 m_aEntryName = sEntName;
985 return;
988 uno::Reference< container::XNameAccess > xNameAccess( xStorage, uno::UNO_QUERY_THROW );
990 // detect entry existence
991 bool bElExists = xNameAccess->hasByName( sEntName );
993 m_aDocMediaDescriptor = GetValuableArgs_Impl( lArguments,
994 nEntryConnectionMode != embed::EntryInitModes::MEDIA_DESCRIPTOR_INIT );
996 m_bReadOnly = false;
997 for ( beans::PropertyValue const & prop : lArguments )
998 if ( prop.Name == "ReadOnly" )
999 prop.Value >>= m_bReadOnly;
1001 // TODO: use lObjArgs for StoreVisualReplacement
1002 for ( beans::PropertyValue const & prop : lObjArgs )
1003 if ( prop.Name == "OutplaceDispatchInterceptor" )
1005 uno::Reference< frame::XDispatchProviderInterceptor > xDispatchInterceptor;
1006 if ( prop.Value >>= xDispatchInterceptor )
1007 m_xDocHolder->SetOutplaceDispatchInterceptor( xDispatchInterceptor );
1009 else if ( prop.Name == "DefaultParentBaseURL" )
1011 prop.Value >>= m_aDefaultParentBaseURL;
1013 else if ( prop.Name == "Parent" )
1015 prop.Value >>= m_xParent;
1017 else if ( prop.Name == "IndividualMiscStatus" )
1019 sal_Int64 nMiscStatus=0;
1020 prop.Value >>= nMiscStatus;
1021 m_nMiscStatus |= nMiscStatus;
1023 else if ( prop.Name == "CloneFrom" )
1025 uno::Reference < embed::XEmbeddedObject > xObj;
1026 prop.Value >>= xObj;
1027 if ( xObj.is() )
1029 m_bHasClonedSize = true;
1030 m_aClonedSize = xObj->getVisualAreaSize( embed::Aspects::MSOLE_CONTENT );
1031 m_nClonedMapUnit = xObj->getMapUnit( embed::Aspects::MSOLE_CONTENT );
1034 else if ( prop.Name == "OutplaceFrameProperties" )
1036 uno::Sequence< uno::Any > aOutFrameProps;
1037 uno::Sequence< beans::NamedValue > aOutFramePropsTyped;
1038 if ( prop.Value >>= aOutFrameProps )
1040 m_xDocHolder->SetOutplaceFrameProperties( aOutFrameProps );
1042 else if ( prop.Value >>= aOutFramePropsTyped )
1044 aOutFrameProps.realloc( aOutFramePropsTyped.getLength() );
1045 uno::Any* pProp = aOutFrameProps.getArray();
1046 for ( const beans::NamedValue* pTypedProp = aOutFramePropsTyped.getConstArray();
1047 pTypedProp != aOutFramePropsTyped.getConstArray() + aOutFramePropsTyped.getLength();
1048 ++pTypedProp, ++pProp
1051 *pProp <<= *pTypedProp;
1053 m_xDocHolder->SetOutplaceFrameProperties( aOutFrameProps );
1055 else
1056 SAL_WARN( "embeddedobj.common", "OCommonEmbeddedObject::setPersistentEntry: illegal type for argument 'OutplaceFrameProperties'!" );
1058 else if ( prop.Name == "ModuleName" )
1060 prop.Value >>= m_aModuleName;
1062 else if ( prop.Name == "EmbeddedScriptSupport" )
1064 OSL_VERIFY( prop.Value >>= m_bEmbeddedScriptSupport );
1066 else if ( prop.Name == "DocumentRecoverySupport" )
1068 OSL_VERIFY( prop.Value >>= m_bDocumentRecoverySupport );
1070 else if ( prop.Name == "RecoveryStorage" )
1072 OSL_VERIFY( prop.Value >>= m_xRecoveryStorage );
1076 sal_Int32 nStorageMode = m_bReadOnly ? embed::ElementModes::READ : embed::ElementModes::READWRITE;
1078 SwitchOwnPersistence( xStorage, sEntName );
1080 if ( nEntryConnectionMode == embed::EntryInitModes::DEFAULT_INIT )
1082 if ( bElExists )
1084 // the initialization from existing storage allows to leave object in loaded state
1085 m_nObjectState = embed::EmbedStates::LOADED;
1087 else
1089 m_xDocHolder->SetComponent( InitNewDocument_Impl(), m_bReadOnly );
1090 if ( !m_xDocHolder->GetComponent().is() )
1091 throw io::IOException(); // TODO: can not create document
1093 m_nObjectState = embed::EmbedStates::RUNNING;
1096 else
1098 if ( ( nStorageMode & embed::ElementModes::READWRITE ) != embed::ElementModes::READWRITE )
1099 throw io::IOException();
1101 if ( nEntryConnectionMode == embed::EntryInitModes::NO_INIT )
1103 // the document just already changed its storage to store to
1104 // the links to OOo documents for now ignore this call
1105 // TODO: OOo links will have persistence so it will be switched here
1107 else if ( nEntryConnectionMode == embed::EntryInitModes::TRUNCATE_INIT )
1109 if ( m_xRecoveryStorage.is() )
1110 TransferMediaType( m_xRecoveryStorage, m_xObjectStorage );
1112 // TODO:
1113 m_xDocHolder->SetComponent( InitNewDocument_Impl(), m_bReadOnly );
1115 if ( !m_xDocHolder->GetComponent().is() )
1116 throw io::IOException(); // TODO: can not create document
1118 m_nObjectState = embed::EmbedStates::RUNNING;
1120 else if ( nEntryConnectionMode == embed::EntryInitModes::MEDIA_DESCRIPTOR_INIT )
1122 m_xDocHolder->SetComponent( CreateDocFromMediaDescr_Impl( lArguments ), m_bReadOnly );
1123 m_nObjectState = embed::EmbedStates::RUNNING;
1125 //else if ( nEntryConnectionMode == embed::EntryInitModes::TRANSFERABLE_INIT )
1127 //TODO:
1129 else
1130 throw lang::IllegalArgumentException( "Wrong connection mode is provided!",
1131 static_cast< ::cppu::OWeakObject* >(this),
1132 3 );
1137 void SAL_CALL OCommonEmbeddedObject::storeToEntry( const uno::Reference< embed::XStorage >& xStorage,
1138 const OUString& sEntName,
1139 const uno::Sequence< beans::PropertyValue >& lArguments,
1140 const uno::Sequence< beans::PropertyValue >& lObjArgs )
1142 ::osl::ResettableMutexGuard aGuard( m_aMutex );
1143 if ( m_bDisposed )
1144 throw lang::DisposedException(); // TODO
1146 if ( m_nObjectState == -1 )
1148 // the object is still not loaded
1149 throw embed::WrongStateException( "Can't store object without persistence!",
1150 static_cast< ::cppu::OWeakObject* >(this) );
1153 if ( m_bWaitSaveCompleted )
1154 throw embed::WrongStateException(
1155 "The object waits for saveCompleted() call!",
1156 static_cast< ::cppu::OWeakObject* >(this) );
1158 // for now support of this interface is required to allow breaking of links and converting them to normal embedded
1159 // objects, so the persist name must be handled correctly ( althowgh no real persist entry is used )
1160 // OSL_ENSURE( !m_bIsLinkURL, "This method implementation must not be used for links!" );
1161 if ( m_bIsLinkURL )
1162 return;
1164 OSL_ENSURE( m_xParentStorage.is() && m_xObjectStorage.is(), "The object has no valid persistence!" );
1166 sal_Int32 nTargetStorageFormat = SOFFICE_FILEFORMAT_CURRENT;
1167 sal_Int32 nOriginalStorageFormat = SOFFICE_FILEFORMAT_CURRENT;
1168 try {
1169 nTargetStorageFormat = ::comphelper::OStorageHelper::GetXStorageFormat( xStorage );
1171 catch ( const beans::IllegalTypeException& )
1173 // the container just has an unknown type, use current file format
1175 catch ( const uno::Exception& )
1177 SAL_WARN( "embeddedobj.common", "Can not retrieve target storage media type!" );
1179 if (nTargetStorageFormat == SOFFICE_FILEFORMAT_60)
1181 SAL_INFO("embeddedobj.common", "fdo#78159: Storing OOoXML as ODF");
1182 nTargetStorageFormat = SOFFICE_FILEFORMAT_CURRENT;
1183 // setting MediaType is done later anyway, no need to do it here
1188 nOriginalStorageFormat = ::comphelper::OStorageHelper::GetXStorageFormat( m_xParentStorage );
1190 catch ( const beans::IllegalTypeException& )
1192 // the container just has an unknown type, use current file format
1194 catch ( const uno::Exception& )
1196 SAL_WARN( "embeddedobj.common", "Can not retrieve own storage media type!" );
1199 bool bTryOptimization = false;
1200 for ( beans::PropertyValue const & prop : lObjArgs )
1202 // StoreVisualReplacement and VisualReplacement args have no sense here
1203 if ( prop.Name == "CanTryOptimization" )
1204 prop.Value >>= bTryOptimization;
1207 bool bSwitchBackToLoaded = false;
1209 // Storing to different format can be done only in running state.
1210 if ( m_nObjectState == embed::EmbedStates::LOADED )
1212 // TODO/LATER: copying is not legal for documents with relative links.
1213 if ( nTargetStorageFormat == nOriginalStorageFormat )
1215 bool bOptimizationWorks = false;
1216 if ( bTryOptimization )
1220 // try to use optimized copying
1221 uno::Reference< embed::XOptimizedStorage > xSource( m_xParentStorage, uno::UNO_QUERY_THROW );
1222 uno::Reference< embed::XOptimizedStorage > xTarget( xStorage, uno::UNO_QUERY_THROW );
1223 xSource->copyElementDirectlyTo( m_aEntryName, xTarget, sEntName );
1224 bOptimizationWorks = true;
1226 catch( const uno::Exception& )
1231 if ( !bOptimizationWorks )
1232 m_xParentStorage->copyElementTo( m_aEntryName, xStorage, sEntName );
1234 else
1236 changeState( embed::EmbedStates::RUNNING );
1237 bSwitchBackToLoaded = true;
1241 if ( m_nObjectState == embed::EmbedStates::LOADED )
1242 return;
1244 uno::Reference< embed::XStorage > xSubStorage =
1245 xStorage->openStorageElement( sEntName, embed::ElementModes::READWRITE );
1247 if ( !xSubStorage.is() )
1248 throw uno::RuntimeException(); //TODO
1250 aGuard.clear();
1251 // TODO/LATER: support hierarchical name for embedded objects in embedded objects
1252 StoreDocToStorage_Impl(
1253 xSubStorage, lArguments, lObjArgs, nTargetStorageFormat, sEntName, false );
1254 aGuard.reset();
1256 if ( bSwitchBackToLoaded )
1257 changeState( embed::EmbedStates::LOADED );
1259 // TODO: should the listener notification be done?
1263 void SAL_CALL OCommonEmbeddedObject::storeAsEntry( const uno::Reference< embed::XStorage >& xStorage,
1264 const OUString& sEntName,
1265 const uno::Sequence< beans::PropertyValue >& lArguments,
1266 const uno::Sequence< beans::PropertyValue >& lObjArgs )
1268 ::osl::ResettableMutexGuard aGuard( m_aMutex );
1269 if ( m_bDisposed )
1270 throw lang::DisposedException(); // TODO
1272 bool AutoSaveEvent = false;
1273 utl::MediaDescriptor lArgs(lObjArgs);
1274 lArgs[utl::MediaDescriptor::PROP_AUTOSAVEEVENT] >>= AutoSaveEvent;
1276 if ( m_nObjectState == -1 )
1278 // the object is still not loaded
1279 throw embed::WrongStateException( "Can't store object without persistence!",
1280 static_cast< ::cppu::OWeakObject* >(this) );
1283 if ( m_bWaitSaveCompleted )
1284 throw embed::WrongStateException(
1285 "The object waits for saveCompleted() call!",
1286 static_cast< ::cppu::OWeakObject* >(this) );
1288 // for now support of this interface is required to allow breaking of links and converting them to normal embedded
1289 // objects, so the persist name must be handled correctly ( althowgh no real persist entry is used )
1290 // OSL_ENSURE( !m_bIsLinkURL, "This method implementation must not be used for links!" );
1291 if ( m_bIsLinkURL )
1293 m_aNewEntryName = sEntName;
1295 if ( !AutoSaveEvent )
1296 handleLinkedOLE(CopyBackToOLELink::CopyTempToLink);
1298 return;
1301 OSL_ENSURE( m_xParentStorage.is() && m_xObjectStorage.is(), "The object has no valid persistence!" );
1303 sal_Int32 nTargetStorageFormat = SOFFICE_FILEFORMAT_CURRENT;
1304 sal_Int32 nOriginalStorageFormat = SOFFICE_FILEFORMAT_CURRENT;
1305 try {
1306 nTargetStorageFormat = ::comphelper::OStorageHelper::GetXStorageFormat( xStorage );
1308 catch ( const beans::IllegalTypeException& )
1310 // the container just has an unknown type, use current file format
1312 catch ( const uno::Exception& )
1314 SAL_WARN( "embeddedobj.common", "Can not retrieve target storage media type!" );
1316 if (nTargetStorageFormat == SOFFICE_FILEFORMAT_60)
1318 SAL_INFO("embeddedobj.common", "fdo#78159: Storing OOoXML as ODF");
1319 nTargetStorageFormat = SOFFICE_FILEFORMAT_CURRENT;
1320 // setting MediaType is done later anyway, no need to do it here
1325 nOriginalStorageFormat = ::comphelper::OStorageHelper::GetXStorageFormat( m_xParentStorage );
1327 catch ( const beans::IllegalTypeException& )
1329 // the container just has an unknown type, use current file format
1331 catch ( const uno::Exception& )
1333 SAL_WARN( "embeddedobj.common", "Can not retrieve own storage media type!" );
1336 PostEvent_Impl( "OnSaveAs" );
1338 bool bTryOptimization = false;
1339 for ( beans::PropertyValue const & prop : lObjArgs )
1341 // StoreVisualReplacement and VisualReplacement args have no sense here
1342 if ( prop.Name == "CanTryOptimization" )
1343 prop.Value >>= bTryOptimization;
1346 bool bSwitchBackToLoaded = false;
1348 // Storing to different format can be done only in running state.
1349 if ( m_nObjectState == embed::EmbedStates::LOADED )
1351 // TODO/LATER: copying is not legal for documents with relative links.
1352 if ( nTargetStorageFormat == nOriginalStorageFormat )
1354 bool bOptimizationWorks = false;
1355 if ( bTryOptimization )
1359 // try to use optimized copying
1360 uno::Reference< embed::XOptimizedStorage > xSource( m_xParentStorage, uno::UNO_QUERY_THROW );
1361 uno::Reference< embed::XOptimizedStorage > xTarget( xStorage, uno::UNO_QUERY_THROW );
1362 xSource->copyElementDirectlyTo( m_aEntryName, xTarget, sEntName );
1363 bOptimizationWorks = true;
1365 catch( const uno::Exception& )
1370 if ( !bOptimizationWorks )
1371 m_xParentStorage->copyElementTo( m_aEntryName, xStorage, sEntName );
1373 else
1375 changeState( embed::EmbedStates::RUNNING );
1376 bSwitchBackToLoaded = true;
1380 uno::Reference< embed::XStorage > xSubStorage =
1381 xStorage->openStorageElement( sEntName, embed::ElementModes::READWRITE );
1383 if ( !xSubStorage.is() )
1384 throw uno::RuntimeException(); //TODO
1386 if ( m_nObjectState != embed::EmbedStates::LOADED )
1388 aGuard.clear();
1389 // TODO/LATER: support hierarchical name for embedded objects in embedded objects
1390 StoreDocToStorage_Impl(
1391 xSubStorage, lArguments, lObjArgs, nTargetStorageFormat, sEntName, false );
1392 aGuard.reset();
1394 if ( bSwitchBackToLoaded )
1395 changeState( embed::EmbedStates::LOADED );
1398 m_bWaitSaveCompleted = true;
1399 m_xNewObjectStorage = xSubStorage;
1400 m_xNewParentStorage = xStorage;
1401 m_aNewEntryName = sEntName;
1402 m_aNewDocMediaDescriptor = GetValuableArgs_Impl( lArguments, true );
1404 // TODO: register listeners for storages above, in case they are disposed
1405 // an exception will be thrown on saveCompleted( true )
1407 // TODO: should the listener notification be done here or in saveCompleted?
1411 void SAL_CALL OCommonEmbeddedObject::saveCompleted( sal_Bool bUseNew )
1413 ::osl::MutexGuard aGuard( m_aMutex );
1414 if ( m_bDisposed )
1415 throw lang::DisposedException(); // TODO
1417 if ( m_nObjectState == -1 )
1419 // the object is still not loaded
1420 throw embed::WrongStateException( "Can't store object without persistence!",
1421 static_cast< ::cppu::OWeakObject* >(this) );
1424 // for now support of this interface is required to allow breaking of links and converting them to normal embedded
1425 // objects, so the persist name must be handled correctly ( althowgh no real persist entry is used )
1426 // OSL_ENSURE( !m_bIsLinkURL, "This method implementation must not be used for links!" );
1427 if ( m_bIsLinkURL )
1429 if ( bUseNew )
1430 m_aEntryName = m_aNewEntryName;
1431 m_aNewEntryName.clear();
1432 return;
1435 // it is allowed to call saveCompleted( false ) for nonstored objects
1436 if ( !m_bWaitSaveCompleted && !bUseNew )
1437 return;
1439 SAL_WARN_IF( !m_bWaitSaveCompleted, "embeddedobj.common", "Unexpected saveCompleted() call!" );
1440 if ( !m_bWaitSaveCompleted )
1441 throw io::IOException(); // TODO: illegal call
1443 OSL_ENSURE( m_xNewObjectStorage.is() && m_xNewParentStorage.is() , "Internal object information is broken!" );
1444 if ( !m_xNewObjectStorage.is() || !m_xNewParentStorage.is() )
1445 throw uno::RuntimeException(); // TODO: broken internal information
1447 if ( bUseNew )
1449 SwitchOwnPersistence( m_xNewParentStorage, m_xNewObjectStorage, m_aNewEntryName );
1450 m_aDocMediaDescriptor = m_aNewDocMediaDescriptor;
1452 uno::Reference< util::XModifiable > xModif( m_xDocHolder->GetComponent(), uno::UNO_QUERY );
1453 if ( xModif.is() )
1454 xModif->setModified( false );
1456 PostEvent_Impl( "OnSaveAsDone");
1458 else
1460 try {
1461 m_xNewObjectStorage->dispose();
1463 catch ( const uno::Exception& )
1468 m_xNewObjectStorage.clear();
1469 m_xNewParentStorage.clear();
1470 m_aNewEntryName.clear();
1471 m_aNewDocMediaDescriptor.realloc( 0 );
1472 m_bWaitSaveCompleted = false;
1474 if ( bUseNew )
1476 // TODO: notify listeners
1478 if ( m_nUpdateMode == embed::EmbedUpdateModes::ALWAYS_UPDATE )
1480 // TODO: update visual representation
1486 sal_Bool SAL_CALL OCommonEmbeddedObject::hasEntry()
1488 ::osl::MutexGuard aGuard( m_aMutex );
1489 if ( m_bDisposed )
1490 throw lang::DisposedException(); // TODO
1492 if ( m_bWaitSaveCompleted )
1493 throw embed::WrongStateException(
1494 "The object waits for saveCompleted() call!",
1495 static_cast< ::cppu::OWeakObject* >(this) );
1497 if ( m_xObjectStorage.is() )
1498 return true;
1500 return false;
1504 OUString SAL_CALL OCommonEmbeddedObject::getEntryName()
1506 ::osl::MutexGuard aGuard( m_aMutex );
1507 if ( m_bDisposed )
1508 throw lang::DisposedException(); // TODO
1510 if ( m_nObjectState == -1 )
1512 // the object is still not loaded
1513 throw embed::WrongStateException( "The object persistence is not initialized!",
1514 static_cast< ::cppu::OWeakObject* >(this) );
1517 if ( m_bWaitSaveCompleted )
1518 throw embed::WrongStateException(
1519 "The object waits for saveCompleted() call!",
1520 static_cast< ::cppu::OWeakObject* >(this) );
1522 return m_aEntryName;
1526 void SAL_CALL OCommonEmbeddedObject::storeOwn()
1528 // during switching from Activated to Running and from Running to Loaded states the object will
1529 // ask container to store the object, the container has to make decision
1530 // to do so or not
1532 ::osl::ResettableMutexGuard aGuard( m_aMutex );
1533 if ( m_bDisposed )
1534 throw lang::DisposedException(); // TODO
1536 if ( m_nObjectState == -1 )
1538 // the object is still not loaded
1539 throw embed::WrongStateException( "Can't store object without persistence!",
1540 static_cast< ::cppu::OWeakObject* >(this) );
1543 if ( m_bWaitSaveCompleted )
1544 throw embed::WrongStateException(
1545 "The object waits for saveCompleted() call!",
1546 static_cast< ::cppu::OWeakObject* >(this) );
1548 if ( m_bReadOnly )
1549 throw io::IOException(); // TODO: access denied
1551 // nothing to do, if the object is in loaded state
1552 if ( m_nObjectState == embed::EmbedStates::LOADED )
1553 return;
1555 PostEvent_Impl( "OnSave" );
1557 SAL_WARN_IF( !m_xDocHolder->GetComponent().is(), "embeddedobj.common", "If an object is activated or in running state it must have a document!" );
1558 if ( !m_xDocHolder->GetComponent().is() )
1559 throw uno::RuntimeException();
1561 if ( m_bIsLinkURL )
1563 // TODO: just store the document to its location
1564 uno::Reference< frame::XStorable > xStorable( m_xDocHolder->GetComponent(), uno::UNO_QUERY_THROW );
1566 // free the main mutex for the storing time
1567 aGuard.clear();
1569 xStorable->store();
1571 aGuard.reset();
1573 else
1575 OSL_ENSURE( m_xParentStorage.is() && m_xObjectStorage.is(), "The object has no valid persistence!" );
1577 if ( !m_xObjectStorage.is() )
1578 throw io::IOException(); //TODO: access denied
1580 sal_Int32 nStorageFormat = SOFFICE_FILEFORMAT_CURRENT;
1581 try {
1582 nStorageFormat = ::comphelper::OStorageHelper::GetXStorageFormat( m_xParentStorage );
1584 catch ( const beans::IllegalTypeException& )
1586 // the container just has an unknown type, use current file format
1588 catch ( const uno::Exception& )
1590 SAL_WARN( "embeddedobj.common", "Can not retrieve storage media type!" );
1592 if (nStorageFormat == SOFFICE_FILEFORMAT_60)
1594 SAL_INFO("embeddedobj.common", "fdo#78159: Storing OOoXML as ODF");
1595 nStorageFormat = SOFFICE_FILEFORMAT_CURRENT;
1596 // setting MediaType is done later anyway, no need to do it here
1599 aGuard.clear();
1600 uno::Sequence<beans::PropertyValue> aEmpty;
1601 uno::Sequence<beans::PropertyValue> aMediaArgs{ comphelper::makePropertyValue(
1602 "DocumentBaseURL", GetBaseURL_Impl()) };
1603 StoreDocToStorage_Impl( m_xObjectStorage, aMediaArgs, aEmpty, nStorageFormat, m_aEntryName, true );
1604 aGuard.reset();
1607 uno::Reference< util::XModifiable > xModif( m_xDocHolder->GetComponent(), uno::UNO_QUERY );
1608 if ( xModif.is() )
1609 xModif->setModified( false );
1611 PostEvent_Impl( "OnSaveDone" );
1615 sal_Bool SAL_CALL OCommonEmbeddedObject::isReadonly()
1617 ::osl::MutexGuard aGuard( m_aMutex );
1618 if ( m_bDisposed )
1619 throw lang::DisposedException(); // TODO
1621 if ( m_nObjectState == -1 )
1623 // the object is still not loaded
1624 throw embed::WrongStateException( "The object persistence is not initialized!",
1625 static_cast< ::cppu::OWeakObject* >(this) );
1628 if ( m_bWaitSaveCompleted )
1629 throw embed::WrongStateException(
1630 "The object waits for saveCompleted() call!",
1631 static_cast< ::cppu::OWeakObject* >(this) );
1633 return m_bReadOnly;
1637 void SAL_CALL OCommonEmbeddedObject::reload(
1638 const uno::Sequence< beans::PropertyValue >& lArguments,
1639 const uno::Sequence< beans::PropertyValue >& lObjArgs )
1641 // TODO: use lObjArgs
1642 // for now this method is used only to switch readonly state
1644 ::osl::MutexGuard aGuard( m_aMutex );
1645 if ( m_bDisposed )
1646 throw lang::DisposedException(); // TODO
1648 if ( m_nObjectState == -1 )
1650 // the object is still not loaded
1651 throw embed::WrongStateException( "The object persistence is not initialized!",
1652 static_cast< ::cppu::OWeakObject* >(this) );
1655 if ( m_nObjectState != embed::EmbedStates::LOADED )
1657 // the object is still not loaded
1658 throw embed::WrongStateException(
1659 "The object must be in loaded state to be reloaded!",
1660 static_cast< ::cppu::OWeakObject* >(this) );
1663 if ( m_bWaitSaveCompleted )
1664 throw embed::WrongStateException(
1665 "The object waits for saveCompleted() call!",
1666 static_cast< ::cppu::OWeakObject* >(this) );
1668 if ( m_bIsLinkURL )
1670 // reload of the link
1671 OUString aOldLinkFilter = m_aLinkFilterName;
1673 OUString aNewLinkFilter;
1674 for ( beans::PropertyValue const & prop : lArguments )
1676 if ( prop.Name == "URL" )
1678 // the new URL
1679 prop.Value >>= m_aLinkURL;
1680 m_aLinkFilterName.clear();
1682 else if ( prop.Name == "FilterName" )
1684 prop.Value >>= aNewLinkFilter;
1685 m_aLinkFilterName.clear();
1689 ::comphelper::MimeConfigurationHelper aHelper( m_xContext );
1690 if ( m_aLinkFilterName.isEmpty() )
1692 if ( !aNewLinkFilter.isEmpty() )
1693 m_aLinkFilterName = aNewLinkFilter;
1694 else
1696 uno::Sequence< beans::PropertyValue > aArgs{ comphelper::makePropertyValue(
1697 "URL", m_aLinkURL) };
1698 m_aLinkFilterName = aHelper.UpdateMediaDescriptorWithFilterName( aArgs, false );
1702 if ( aOldLinkFilter != m_aLinkFilterName )
1704 uno::Sequence< beans::NamedValue > aObject = aHelper.GetObjectPropsByFilter( m_aLinkFilterName );
1706 // TODO/LATER: probably the document holder could be cleaned explicitly as in the destructor
1707 m_xDocHolder.clear();
1709 LinkInit_Impl( aObject, lArguments, lObjArgs );
1713 m_aDocMediaDescriptor = GetValuableArgs_Impl( lArguments, true );
1715 // TODO: use lObjArgs for StoreVisualReplacement
1716 for ( beans::PropertyValue const & prop : lObjArgs )
1717 if ( prop.Name == "OutplaceDispatchInterceptor" )
1719 uno::Reference< frame::XDispatchProviderInterceptor > xDispatchInterceptor;
1720 if ( prop.Value >>= xDispatchInterceptor )
1721 m_xDocHolder->SetOutplaceDispatchInterceptor( xDispatchInterceptor );
1723 break;
1726 // TODO:
1727 // when document allows reloading through API the object can be reloaded not only in loaded state
1729 bool bOldReadOnlyValue = m_bReadOnly;
1731 m_bReadOnly = false;
1732 for ( beans::PropertyValue const & prop : lArguments )
1733 if ( prop.Name == "ReadOnly" )
1734 prop.Value >>= m_bReadOnly;
1736 if ( bOldReadOnlyValue == m_bReadOnly || m_bIsLinkURL )
1737 return;
1739 // close own storage
1740 try {
1741 if ( m_xObjectStorage.is() )
1742 m_xObjectStorage->dispose();
1744 catch ( const uno::Exception& )
1748 sal_Int32 nStorageMode = m_bReadOnly ? embed::ElementModes::READ : embed::ElementModes::READWRITE;
1749 m_xObjectStorage = m_xParentStorage->openStorageElement( m_aEntryName, nStorageMode );
1752 sal_Bool SAL_CALL OCommonEmbeddedObject::isStored()
1754 if (!m_xObjectStorage.is())
1755 return false;
1757 return m_xObjectStorage->getElementNames().hasElements();
1761 void SAL_CALL OCommonEmbeddedObject::breakLink( const uno::Reference< embed::XStorage >& xStorage,
1762 const OUString& sEntName )
1764 ::osl::ResettableMutexGuard aGuard( m_aMutex );
1765 if ( m_bDisposed )
1766 throw lang::DisposedException(); // TODO
1768 if (!m_bIsLinkURL || m_nObjectState == -1)
1770 // it must be a linked initialized object
1771 throw embed::WrongStateException(
1772 "The object is not a valid linked object!",
1773 static_cast< ::cppu::OWeakObject* >(this) );
1775 // the current implementation of OOo links does not implement this method since it does not implement
1776 // all the set of interfaces required for OOo embedded object ( XEmbedPersist is not supported ).
1778 if ( !xStorage.is() )
1779 throw lang::IllegalArgumentException( "No parent storage is provided!",
1780 static_cast< ::cppu::OWeakObject* >(this),
1781 1 );
1783 if ( sEntName.isEmpty() )
1784 throw lang::IllegalArgumentException( "Empty element name is provided!",
1785 static_cast< ::cppu::OWeakObject* >(this),
1786 2 );
1788 if ( m_bWaitSaveCompleted )
1789 throw embed::WrongStateException(
1790 "The object waits for saveCompleted() call!",
1791 static_cast< ::cppu::OWeakObject* >(this) );
1793 uno::Reference< container::XNameAccess > xNameAccess( xStorage, uno::UNO_QUERY_THROW );
1795 m_bReadOnly = false;
1797 if ( m_xParentStorage != xStorage || m_aEntryName != sEntName )
1798 SwitchOwnPersistence( xStorage, sEntName );
1800 // for linked object it means that it becomes embedded object
1801 // the document must switch it's persistence also
1803 // TODO/LATER: handle the case when temp doc can not be created
1804 // the document is a new embedded object so it must be marked as modified
1805 uno::Reference< util::XCloseable > xDocument = CreateTempDocFromLink_Impl();
1808 if(m_xDocHolder.is() && m_xDocHolder->GetComponent().is())
1810 // tdf#141528 m_xDocHolder->GetComponent() may be not set, so add it
1811 // to the try path to not get thrown out of the local context to the next
1812 // higher try...catch on the stack. To make breakLink work it is
1813 // *necessary* to execute the code below that resets the linked state,
1814 // esp. the *.clear stuff and resetting m_bIsLink.
1815 uno::Reference< util::XModifiable > xModif( m_xDocHolder->GetComponent(), uno::UNO_QUERY_THROW );
1817 // all other locations in this file check for xModif.is(), so do it here, too
1818 if ( xModif.is() )
1819 xModif->setModified( true );
1822 catch( const uno::Exception& )
1825 m_xDocHolder->SetComponent( xDocument, m_bReadOnly );
1826 SAL_WARN_IF( !m_xDocHolder->GetComponent().is(), "embeddedobj.common", "If document can't be created, an exception must be thrown!" );
1828 if ( m_nObjectState == embed::EmbedStates::LOADED )
1830 // the state is changed and can not be switched to loaded state back without saving
1831 m_nObjectState = embed::EmbedStates::RUNNING;
1832 StateChangeNotification_Impl( false, embed::EmbedStates::LOADED, m_nObjectState, aGuard );
1834 else if ( m_nObjectState == embed::EmbedStates::ACTIVE )
1835 m_xDocHolder->Show();
1837 // tdf#141529 reset all stuff involved in linked state, including
1838 // the OLE content copied to the temp file
1839 m_bIsLinkURL = false;
1840 m_aLinkTempFile.clear();
1841 m_aLinkFilterName.clear();
1842 m_aLinkURL.clear();
1846 sal_Bool SAL_CALL OCommonEmbeddedObject::isLink()
1848 ::osl::MutexGuard aGuard( m_aMutex );
1849 if ( m_bDisposed )
1850 throw lang::DisposedException(); // TODO
1852 return m_bIsLinkURL;
1856 OUString SAL_CALL OCommonEmbeddedObject::getLinkURL()
1858 ::osl::MutexGuard aGuard( m_aMutex );
1859 if ( m_bDisposed )
1860 throw lang::DisposedException(); // TODO
1862 if ( !m_bIsLinkURL )
1863 throw embed::WrongStateException(
1864 "The object is not a link object!",
1865 static_cast< ::cppu::OWeakObject* >(this) );
1867 return m_aLinkURL;
1870 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */