Before deactivating a linked OLE saved it and overwrote the original, even when not saving the hosting document at all. This is not intuitive from user perspective and may lead to unexpected data loss (of the OLE content). Reported case was especially about closing the hosted document without saving in the understandable believe that that way the changed OLE will not be changed on external medium. Added mechanism for linked OLE to hold data in a hidden local temp file, synching/writing back on hosting file save. Most complicated was adapting the 'break link' case and ensuring other cases to work, but looks good now from my POV Change-Id: I7f63d667820b2d9725abc598a9dd7360be1f8d5a Reviewed-on: https://gerrit.libreoffice.org/c/core/+/113793 Tested-by: Armin Le Grand <Armin.Le.Grand@me.com> Reviewed-by: Armin Le Grand <Armin.Le.Grand@me.com>
1894 lines
66 KiB
C++
1894 lines
66 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed
|
|
* with this work for additional information regarding copyright
|
|
* ownership. The ASF licenses this file to you under the Apache
|
|
* License, Version 2.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
|
|
*/
|
|
|
|
#include <commonembobj.hxx>
|
|
#include <com/sun/star/embed/Aspects.hpp>
|
|
#include <com/sun/star/document/XStorageBasedDocument.hpp>
|
|
#include <com/sun/star/embed/EmbedStates.hpp>
|
|
#include <com/sun/star/embed/EntryInitModes.hpp>
|
|
#include <com/sun/star/embed/StorageWrappedTargetException.hpp>
|
|
#include <com/sun/star/embed/WrongStateException.hpp>
|
|
#include <com/sun/star/embed/XStorage.hpp>
|
|
#include <com/sun/star/embed/XOptimizedStorage.hpp>
|
|
#include <com/sun/star/embed/ElementModes.hpp>
|
|
#include <com/sun/star/embed/EmbedUpdateModes.hpp>
|
|
#include <com/sun/star/embed/StorageFactory.hpp>
|
|
#include <com/sun/star/io/IOException.hpp>
|
|
#include <com/sun/star/io/TempFile.hpp>
|
|
#include <com/sun/star/frame/XModel.hpp>
|
|
#include <com/sun/star/frame/XStorable.hpp>
|
|
#include <com/sun/star/frame/XLoadable.hpp>
|
|
#include <com/sun/star/frame/XModule.hpp>
|
|
#include <com/sun/star/lang/NoSupportException.hpp>
|
|
#include <com/sun/star/lang/XSingleServiceFactory.hpp>
|
|
#include <com/sun/star/lang/DisposedException.hpp>
|
|
#include <com/sun/star/util/XModifiable.hpp>
|
|
|
|
#include <com/sun/star/container/XNameAccess.hpp>
|
|
#include <com/sun/star/container/XChild.hpp>
|
|
#include <com/sun/star/util/XCloseable.hpp>
|
|
#include <com/sun/star/beans/XPropertySet.hpp>
|
|
#include <com/sun/star/beans/IllegalTypeException.hpp>
|
|
#include <com/sun/star/chart2/XChartDocument.hpp>
|
|
|
|
#include <com/sun/star/ucb/SimpleFileAccess.hpp>
|
|
#include <com/sun/star/io/XTruncate.hpp>
|
|
|
|
#include <comphelper/fileformat.h>
|
|
#include <comphelper/storagehelper.hxx>
|
|
#include <comphelper/mimeconfighelper.hxx>
|
|
#include <comphelper/namedvaluecollection.hxx>
|
|
|
|
#include <tools/diagnose_ex.h>
|
|
#include <sal/log.hxx>
|
|
#include <unotools/configmgr.hxx>
|
|
#include "persistence.hxx"
|
|
|
|
using namespace ::com::sun::star;
|
|
|
|
|
|
uno::Sequence< beans::PropertyValue > GetValuableArgs_Impl( const uno::Sequence< beans::PropertyValue >& aMedDescr,
|
|
bool bCanUseDocumentBaseURL )
|
|
{
|
|
uno::Sequence< beans::PropertyValue > aResult;
|
|
sal_Int32 nResLen = 0;
|
|
|
|
for ( beans::PropertyValue const & prop : aMedDescr )
|
|
{
|
|
if ( prop.Name == "ComponentData" || prop.Name == "DocumentTitle"
|
|
|| prop.Name == "InteractionHandler" || prop.Name == "JumpMark"
|
|
// || prop.Name == "Password" // makes no sense for embedded objects
|
|
|| prop.Name == "Preview" || prop.Name == "ReadOnly"
|
|
|| prop.Name == "StartPresentation" || prop.Name == "RepairPackage"
|
|
|| prop.Name == "StatusIndicator" || prop.Name == "ViewData"
|
|
|| prop.Name == "ViewId" || prop.Name == "MacroExecutionMode"
|
|
|| prop.Name == "UpdateDocMode"
|
|
|| (prop.Name == "DocumentBaseURL" && bCanUseDocumentBaseURL) )
|
|
{
|
|
aResult.realloc( ++nResLen );
|
|
aResult[nResLen-1] = prop;
|
|
}
|
|
}
|
|
|
|
return aResult;
|
|
}
|
|
|
|
|
|
static uno::Sequence< beans::PropertyValue > addAsTemplate( const uno::Sequence< beans::PropertyValue >& aOrig )
|
|
{
|
|
bool bAsTemplateSet = false;
|
|
sal_Int32 nLength = aOrig.getLength();
|
|
uno::Sequence< beans::PropertyValue > aResult( nLength );
|
|
|
|
for ( sal_Int32 nInd = 0; nInd < nLength; nInd++ )
|
|
{
|
|
aResult[nInd].Name = aOrig[nInd].Name;
|
|
if ( aResult[nInd].Name == "AsTemplate" )
|
|
{
|
|
aResult[nInd].Value <<= true;
|
|
bAsTemplateSet = true;
|
|
}
|
|
else
|
|
aResult[nInd].Value = aOrig[nInd].Value;
|
|
}
|
|
|
|
if ( !bAsTemplateSet )
|
|
{
|
|
aResult.realloc( nLength + 1 );
|
|
aResult[nLength].Name = "AsTemplate";
|
|
aResult[nLength].Value <<= true;
|
|
}
|
|
|
|
return aResult;
|
|
}
|
|
|
|
|
|
static uno::Reference< io::XInputStream > createTempInpStreamFromStor(
|
|
const uno::Reference< embed::XStorage >& xStorage,
|
|
const uno::Reference< uno::XComponentContext >& xContext )
|
|
{
|
|
SAL_WARN_IF( !xStorage.is(), "embeddedobj.common", "The storage can not be empty!" );
|
|
|
|
uno::Reference< io::XInputStream > xResult;
|
|
|
|
uno::Reference < io::XStream > xTempStream( io::TempFile::create(xContext), uno::UNO_QUERY_THROW );
|
|
|
|
uno::Reference < lang::XSingleServiceFactory > xStorageFactory( embed::StorageFactory::create(xContext) );
|
|
|
|
uno::Sequence< uno::Any > aArgs( 2 );
|
|
aArgs[0] <<= xTempStream;
|
|
aArgs[1] <<= embed::ElementModes::READWRITE;
|
|
uno::Reference< embed::XStorage > xTempStorage( xStorageFactory->createInstanceWithArguments( aArgs ),
|
|
uno::UNO_QUERY_THROW );
|
|
|
|
try
|
|
{
|
|
xStorage->copyToStorage( xTempStorage );
|
|
} catch( const uno::Exception& )
|
|
{
|
|
css::uno::Any anyEx = cppu::getCaughtException();
|
|
throw embed::StorageWrappedTargetException(
|
|
"Can't copy storage!",
|
|
uno::Reference< uno::XInterface >(),
|
|
anyEx );
|
|
}
|
|
|
|
try {
|
|
if ( xTempStorage.is() )
|
|
xTempStorage->dispose();
|
|
}
|
|
catch ( const uno::Exception& )
|
|
{
|
|
}
|
|
|
|
try {
|
|
uno::Reference< io::XOutputStream > xTempOut = xTempStream->getOutputStream();
|
|
if ( xTempOut.is() )
|
|
xTempOut->closeOutput();
|
|
}
|
|
catch ( const uno::Exception& )
|
|
{
|
|
}
|
|
|
|
xResult = xTempStream->getInputStream();
|
|
|
|
return xResult;
|
|
|
|
}
|
|
|
|
|
|
static void TransferMediaType( const uno::Reference< embed::XStorage >& i_rSource, const uno::Reference< embed::XStorage >& i_rTarget )
|
|
{
|
|
try
|
|
{
|
|
const uno::Reference< beans::XPropertySet > xSourceProps( i_rSource, uno::UNO_QUERY_THROW );
|
|
const uno::Reference< beans::XPropertySet > xTargetProps( i_rTarget, uno::UNO_QUERY_THROW );
|
|
static const OUStringLiteral sMediaTypePropName( u"MediaType" );
|
|
xTargetProps->setPropertyValue( sMediaTypePropName, xSourceProps->getPropertyValue( sMediaTypePropName ) );
|
|
}
|
|
catch( const uno::Exception& )
|
|
{
|
|
DBG_UNHANDLED_EXCEPTION("embeddedobj.common");
|
|
}
|
|
}
|
|
|
|
|
|
static uno::Reference< util::XCloseable > CreateDocument( const uno::Reference< uno::XComponentContext >& _rxContext,
|
|
const OUString& _rDocumentServiceName, bool _bEmbeddedScriptSupport, const bool i_bDocumentRecoverySupport )
|
|
{
|
|
::comphelper::NamedValueCollection aArguments;
|
|
aArguments.put( "EmbeddedObject", true );
|
|
aArguments.put( "EmbeddedScriptSupport", _bEmbeddedScriptSupport );
|
|
aArguments.put( "DocumentRecoverySupport", i_bDocumentRecoverySupport );
|
|
|
|
uno::Reference< uno::XInterface > xDocument;
|
|
try
|
|
{
|
|
xDocument = _rxContext->getServiceManager()->createInstanceWithArgumentsAndContext(
|
|
_rDocumentServiceName, aArguments.getWrappedPropertyValues(), _rxContext );
|
|
}
|
|
catch( const uno::Exception& )
|
|
{
|
|
// if an embedded object implementation does not support XInitialization,
|
|
// the default factory from cppuhelper will throw an
|
|
// IllegalArgumentException when we try to create the instance with arguments.
|
|
// Okay, so we fall back to creating the instance without any arguments.
|
|
OSL_FAIL("Consider implementing interface XInitialization to avoid duplicate construction");
|
|
xDocument = _rxContext->getServiceManager()->createInstanceWithContext( _rDocumentServiceName, _rxContext );
|
|
}
|
|
|
|
SAL_WARN_IF(!xDocument.is(), "embeddedobj.common", "Service " << _rDocumentServiceName << " is not available?");
|
|
return uno::Reference< util::XCloseable >( xDocument, uno::UNO_QUERY );
|
|
}
|
|
|
|
|
|
static void SetDocToEmbedded( const uno::Reference< frame::XModel >& rDocument, const OUString& aModuleName )
|
|
{
|
|
if (!rDocument.is())
|
|
return;
|
|
|
|
uno::Sequence< beans::PropertyValue > aSeq( 1 );
|
|
aSeq[0].Name = "SetEmbedded";
|
|
aSeq[0].Value <<= true;
|
|
rDocument->attachResource( OUString(), aSeq );
|
|
|
|
if ( !aModuleName.isEmpty() )
|
|
{
|
|
try
|
|
{
|
|
uno::Reference< frame::XModule > xModule( rDocument, uno::UNO_QUERY_THROW );
|
|
xModule->setIdentifier( aModuleName );
|
|
}
|
|
catch( const uno::Exception& )
|
|
{}
|
|
}
|
|
}
|
|
|
|
|
|
void OCommonEmbeddedObject::SwitchOwnPersistence( const uno::Reference< embed::XStorage >& xNewParentStorage,
|
|
const uno::Reference< embed::XStorage >& xNewObjectStorage,
|
|
const OUString& aNewName )
|
|
{
|
|
if ( xNewParentStorage == m_xParentStorage && aNewName == m_aEntryName )
|
|
{
|
|
SAL_WARN_IF( xNewObjectStorage != m_xObjectStorage, "embeddedobj.common", "The storage must be the same!" );
|
|
return;
|
|
}
|
|
|
|
auto xOldObjectStorage = m_xObjectStorage;
|
|
m_xObjectStorage = xNewObjectStorage;
|
|
m_xParentStorage = xNewParentStorage;
|
|
m_aEntryName = aNewName;
|
|
|
|
// the linked document should not be switched
|
|
if ( !m_bIsLinkURL )
|
|
{
|
|
uno::Reference< document::XStorageBasedDocument > xDoc( m_xDocHolder->GetComponent(), uno::UNO_QUERY );
|
|
if ( xDoc.is() )
|
|
SwitchDocToStorage_Impl( xDoc, m_xObjectStorage );
|
|
}
|
|
|
|
try {
|
|
if ( xOldObjectStorage.is() )
|
|
xOldObjectStorage->dispose();
|
|
}
|
|
catch ( const uno::Exception& )
|
|
{
|
|
}
|
|
}
|
|
|
|
|
|
void OCommonEmbeddedObject::SwitchOwnPersistence( const uno::Reference< embed::XStorage >& xNewParentStorage,
|
|
const OUString& aNewName )
|
|
{
|
|
if ( xNewParentStorage == m_xParentStorage && aNewName == m_aEntryName )
|
|
return;
|
|
|
|
sal_Int32 nStorageMode = m_bReadOnly ? embed::ElementModes::READ : embed::ElementModes::READWRITE;
|
|
|
|
uno::Reference< embed::XStorage > xNewOwnStorage = xNewParentStorage->openStorageElement( aNewName, nStorageMode );
|
|
SAL_WARN_IF( !xNewOwnStorage.is(), "embeddedobj.common", "The method can not return empty reference!" );
|
|
|
|
SwitchOwnPersistence( xNewParentStorage, xNewOwnStorage, aNewName );
|
|
}
|
|
|
|
|
|
void OCommonEmbeddedObject::EmbedAndReparentDoc_Impl( const uno::Reference< util::XCloseable >& i_rxDocument ) const
|
|
{
|
|
SetDocToEmbedded( uno::Reference< frame::XModel >( i_rxDocument, uno::UNO_QUERY ), m_aModuleName );
|
|
|
|
try
|
|
{
|
|
uno::Reference < container::XChild > xChild( i_rxDocument, uno::UNO_QUERY );
|
|
if ( xChild.is() )
|
|
xChild->setParent( m_xParent );
|
|
}
|
|
catch( const lang::NoSupportException & )
|
|
{
|
|
SAL_WARN( "embeddedobj.common", "OCommonEmbeddedObject::EmbedAndReparentDoc: cannot set parent at document!" );
|
|
}
|
|
}
|
|
|
|
|
|
uno::Reference< util::XCloseable > OCommonEmbeddedObject::InitNewDocument_Impl()
|
|
{
|
|
uno::Reference< util::XCloseable > xDocument( CreateDocument( m_xContext, GetDocumentServiceName(),
|
|
m_bEmbeddedScriptSupport, m_bDocumentRecoverySupport ) );
|
|
|
|
uno::Reference< frame::XModel > xModel( xDocument, uno::UNO_QUERY );
|
|
uno::Reference< frame::XLoadable > xLoadable( xModel, uno::UNO_QUERY_THROW );
|
|
|
|
try
|
|
{
|
|
// set the document mode to embedded as the first action on document!!!
|
|
EmbedAndReparentDoc_Impl( xDocument );
|
|
|
|
// if we have a storage to recover the document from, do not use initNew, but instead load from that storage
|
|
bool bInitNew = true;
|
|
if ( m_xRecoveryStorage.is() )
|
|
{
|
|
uno::Reference< document::XStorageBasedDocument > xDoc( xLoadable, uno::UNO_QUERY );
|
|
SAL_WARN_IF( !xDoc.is(), "embeddedobj.common", "OCommonEmbeddedObject::InitNewDocument_Impl: cannot recover from a storage when the document is not storage based!" );
|
|
if ( xDoc.is() )
|
|
{
|
|
::comphelper::NamedValueCollection aLoadArgs;
|
|
FillDefaultLoadArgs_Impl( m_xRecoveryStorage, aLoadArgs );
|
|
|
|
xDoc->loadFromStorage( m_xRecoveryStorage, aLoadArgs.getPropertyValues() );
|
|
SwitchDocToStorage_Impl( xDoc, m_xObjectStorage );
|
|
bInitNew = false;
|
|
}
|
|
}
|
|
|
|
if ( bInitNew )
|
|
{
|
|
// init document as a new
|
|
xLoadable->initNew();
|
|
}
|
|
xModel->attachResource( xModel->getURL(), m_aDocMediaDescriptor );
|
|
}
|
|
catch( const uno::Exception& )
|
|
{
|
|
if ( xDocument.is() )
|
|
{
|
|
try
|
|
{
|
|
xDocument->close( true );
|
|
}
|
|
catch( const uno::Exception& )
|
|
{
|
|
}
|
|
}
|
|
|
|
throw; // TODO
|
|
}
|
|
|
|
return xDocument;
|
|
}
|
|
|
|
|
|
uno::Reference< util::XCloseable > OCommonEmbeddedObject::LoadLink_Impl()
|
|
{
|
|
uno::Reference< util::XCloseable > xDocument( CreateDocument( m_xContext, GetDocumentServiceName(),
|
|
m_bEmbeddedScriptSupport, m_bDocumentRecoverySupport ) );
|
|
|
|
uno::Reference< frame::XLoadable > xLoadable( xDocument, uno::UNO_QUERY_THROW );
|
|
|
|
sal_Int32 nLen = 2;
|
|
uno::Sequence< beans::PropertyValue > aArgs( nLen );
|
|
|
|
aArgs[0].Name = "URL";
|
|
if(m_aLinkTempFile.is())
|
|
aArgs[0].Value <<= m_aLinkTempFile->getUri();
|
|
else
|
|
aArgs[0].Value <<= m_aLinkURL;
|
|
|
|
aArgs[1].Name = "FilterName";
|
|
aArgs[1].Value <<= m_aLinkFilterName;
|
|
|
|
if ( m_bLinkHasPassword )
|
|
{
|
|
aArgs.realloc( ++nLen );
|
|
aArgs[nLen-1].Name = "Password";
|
|
aArgs[nLen-1].Value <<= m_aLinkPassword;
|
|
}
|
|
|
|
aArgs.realloc( m_aDocMediaDescriptor.getLength() + nLen );
|
|
for ( sal_Int32 nInd = 0; nInd < m_aDocMediaDescriptor.getLength(); nInd++ )
|
|
{
|
|
aArgs[nInd+nLen].Name = m_aDocMediaDescriptor[nInd].Name;
|
|
aArgs[nInd+nLen].Value = m_aDocMediaDescriptor[nInd].Value;
|
|
}
|
|
|
|
try
|
|
{
|
|
// the document is not really an embedded one, it is a link
|
|
EmbedAndReparentDoc_Impl( xDocument );
|
|
|
|
// load the document
|
|
xLoadable->load( aArgs );
|
|
|
|
if ( !m_bLinkHasPassword )
|
|
{
|
|
// check if there is a password to cache
|
|
uno::Reference< frame::XModel > xModel( xLoadable, uno::UNO_QUERY_THROW );
|
|
const uno::Sequence< beans::PropertyValue > aProps = xModel->getArgs();
|
|
for ( beans::PropertyValue const & prop : aProps )
|
|
if ( prop.Name == "Password" && ( prop.Value >>= m_aLinkPassword ) )
|
|
{
|
|
m_bLinkHasPassword = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
catch( const uno::Exception& )
|
|
{
|
|
if ( xDocument.is() )
|
|
{
|
|
try
|
|
{
|
|
xDocument->close( true );
|
|
}
|
|
catch( const uno::Exception& )
|
|
{
|
|
}
|
|
}
|
|
|
|
throw; // TODO
|
|
}
|
|
|
|
return xDocument;
|
|
|
|
}
|
|
|
|
|
|
OUString OCommonEmbeddedObject::GetFilterName( sal_Int32 nVersion ) const
|
|
{
|
|
OUString aFilterName = GetPresetFilterName();
|
|
if ( aFilterName.isEmpty() )
|
|
{
|
|
OUString sDocumentServiceName = GetDocumentServiceName();
|
|
if (utl::ConfigManager::IsFuzzing() && nVersion == SOFFICE_FILEFORMAT_CURRENT &&
|
|
sDocumentServiceName == "com.sun.star.chart2.ChartDocument")
|
|
{
|
|
return "chart8";
|
|
}
|
|
try {
|
|
::comphelper::MimeConfigurationHelper aHelper( m_xContext );
|
|
aFilterName = aHelper.GetDefaultFilterFromServiceName(sDocumentServiceName, nVersion);
|
|
|
|
// If no filter is found, fall back to the FileFormatVersion=6200 filter, Base only has that.
|
|
if (aFilterName.isEmpty() && nVersion == SOFFICE_FILEFORMAT_CURRENT)
|
|
aFilterName = aHelper.GetDefaultFilterFromServiceName(GetDocumentServiceName(), SOFFICE_FILEFORMAT_60);
|
|
} catch( const uno::Exception& )
|
|
{}
|
|
}
|
|
|
|
return aFilterName;
|
|
}
|
|
|
|
|
|
void OCommonEmbeddedObject::FillDefaultLoadArgs_Impl( const uno::Reference< embed::XStorage >& i_rxStorage,
|
|
::comphelper::NamedValueCollection& o_rLoadArgs ) const
|
|
{
|
|
o_rLoadArgs.put( "DocumentBaseURL", GetBaseURL_Impl() );
|
|
o_rLoadArgs.put( "HierarchicalDocumentName", m_aEntryName );
|
|
o_rLoadArgs.put( "ReadOnly", m_bReadOnly );
|
|
|
|
OUString aFilterName = GetFilterName( ::comphelper::OStorageHelper::GetXStorageFormat( i_rxStorage ) );
|
|
SAL_WARN_IF( aFilterName.isEmpty(), "embeddedobj.common", "OCommonEmbeddedObject::FillDefaultLoadArgs_Impl: Wrong document service name!" );
|
|
if ( aFilterName.isEmpty() )
|
|
throw io::IOException(); // TODO: error message/code
|
|
|
|
o_rLoadArgs.put( "FilterName", aFilterName );
|
|
}
|
|
|
|
|
|
uno::Reference< util::XCloseable > OCommonEmbeddedObject::LoadDocumentFromStorage_Impl()
|
|
{
|
|
ENSURE_OR_THROW( m_xObjectStorage.is(), "no object storage" );
|
|
|
|
const uno::Reference< embed::XStorage > xSourceStorage( m_xRecoveryStorage.is() ? m_xRecoveryStorage : m_xObjectStorage );
|
|
|
|
uno::Reference< util::XCloseable > xDocument( CreateDocument( m_xContext, GetDocumentServiceName(),
|
|
m_bEmbeddedScriptSupport, m_bDocumentRecoverySupport ) );
|
|
|
|
//#i103460# ODF: take the size given from the parent frame as default
|
|
uno::Reference< chart2::XChartDocument > xChart( xDocument, uno::UNO_QUERY );
|
|
if( xChart.is() )
|
|
{
|
|
uno::Reference< embed::XVisualObject > xChartVisualObject( xChart, uno::UNO_QUERY );
|
|
if( xChartVisualObject.is() )
|
|
xChartVisualObject->setVisualAreaSize( embed::Aspects::MSOLE_CONTENT, m_aDefaultSizeForChart_In_100TH_MM );
|
|
}
|
|
|
|
uno::Reference< frame::XLoadable > xLoadable( xDocument, uno::UNO_QUERY );
|
|
uno::Reference< document::XStorageBasedDocument > xDoc( xDocument, uno::UNO_QUERY );
|
|
if ( !xDoc.is() && !xLoadable.is() )
|
|
throw uno::RuntimeException();
|
|
|
|
::comphelper::NamedValueCollection aLoadArgs;
|
|
FillDefaultLoadArgs_Impl( xSourceStorage, aLoadArgs );
|
|
|
|
uno::Reference< io::XInputStream > xTempInpStream;
|
|
if ( !xDoc.is() )
|
|
{
|
|
xTempInpStream = createTempInpStreamFromStor( xSourceStorage, m_xContext );
|
|
if ( !xTempInpStream.is() )
|
|
throw uno::RuntimeException();
|
|
|
|
OUString aTempFileURL;
|
|
try
|
|
{
|
|
// no need to let the file stay after the stream is removed since the embedded document
|
|
// can not be stored directly
|
|
uno::Reference< beans::XPropertySet > xTempStreamProps( xTempInpStream, uno::UNO_QUERY_THROW );
|
|
xTempStreamProps->getPropertyValue("Uri") >>= aTempFileURL;
|
|
}
|
|
catch( const uno::Exception& )
|
|
{
|
|
}
|
|
|
|
SAL_WARN_IF( aTempFileURL.isEmpty(), "embeddedobj.common", "Couldn't retrieve temporary file URL!" );
|
|
|
|
aLoadArgs.put( "URL", aTempFileURL );
|
|
aLoadArgs.put( "InputStream", xTempInpStream );
|
|
}
|
|
|
|
|
|
aLoadArgs.merge( m_aDocMediaDescriptor, true );
|
|
|
|
try
|
|
{
|
|
// set the document mode to embedded as the first step!!!
|
|
EmbedAndReparentDoc_Impl( xDocument );
|
|
|
|
if ( xDoc.is() )
|
|
{
|
|
xDoc->loadFromStorage( xSourceStorage, aLoadArgs.getPropertyValues() );
|
|
if ( xSourceStorage != m_xObjectStorage )
|
|
SwitchDocToStorage_Impl( xDoc, m_xObjectStorage );
|
|
}
|
|
else
|
|
xLoadable->load( aLoadArgs.getPropertyValues() );
|
|
}
|
|
catch( const uno::Exception& )
|
|
{
|
|
if ( xDocument.is() )
|
|
{
|
|
try
|
|
{
|
|
xDocument->close( true );
|
|
}
|
|
catch( const uno::Exception& )
|
|
{
|
|
DBG_UNHANDLED_EXCEPTION("embeddedobj.common");
|
|
}
|
|
}
|
|
|
|
throw; // TODO
|
|
}
|
|
|
|
return xDocument;
|
|
}
|
|
|
|
|
|
uno::Reference< io::XInputStream > OCommonEmbeddedObject::StoreDocumentToTempStream_Impl(
|
|
sal_Int32 nStorageFormat,
|
|
const OUString& aBaseURL,
|
|
const OUString& aHierarchName )
|
|
{
|
|
uno::Reference < io::XOutputStream > xTempOut(
|
|
io::TempFile::create(m_xContext),
|
|
uno::UNO_QUERY_THROW );
|
|
uno::Reference< io::XInputStream > aResult( xTempOut, uno::UNO_QUERY_THROW );
|
|
|
|
uno::Reference< frame::XStorable > xStorable;
|
|
{
|
|
osl::MutexGuard aGuard( m_aMutex );
|
|
if ( m_xDocHolder.is() )
|
|
xStorable.set( m_xDocHolder->GetComponent(), uno::UNO_QUERY );
|
|
}
|
|
|
|
if( !xStorable.is() )
|
|
throw uno::RuntimeException(); // TODO:
|
|
|
|
OUString aFilterName = GetFilterName( nStorageFormat );
|
|
|
|
SAL_WARN_IF( aFilterName.isEmpty(), "embeddedobj.common", "Wrong document service name!" );
|
|
if ( aFilterName.isEmpty() )
|
|
throw io::IOException(); // TODO:
|
|
|
|
uno::Sequence< beans::PropertyValue > aArgs( 4 );
|
|
aArgs[0].Name = "FilterName";
|
|
aArgs[0].Value <<= aFilterName;
|
|
aArgs[1].Name = "OutputStream";
|
|
aArgs[1].Value <<= xTempOut;
|
|
aArgs[2].Name = "DocumentBaseURL";
|
|
aArgs[2].Value <<= aBaseURL;
|
|
aArgs[3].Name = "HierarchicalDocumentName";
|
|
aArgs[3].Value <<= aHierarchName;
|
|
|
|
xStorable->storeToURL( "private:stream", aArgs );
|
|
try
|
|
{
|
|
xTempOut->closeOutput();
|
|
}
|
|
catch( const uno::Exception& )
|
|
{
|
|
SAL_WARN( "embeddedobj.common", "Looks like stream was closed already" );
|
|
}
|
|
|
|
return aResult;
|
|
}
|
|
|
|
|
|
void OCommonEmbeddedObject::SaveObject_Impl()
|
|
{
|
|
if ( !m_xClientSite.is() )
|
|
return;
|
|
|
|
try
|
|
{
|
|
// check whether the component is modified,
|
|
// if not there is no need for storing
|
|
uno::Reference< util::XModifiable > xModifiable( m_xDocHolder->GetComponent(), uno::UNO_QUERY );
|
|
if ( xModifiable.is() && !xModifiable->isModified() )
|
|
return;
|
|
}
|
|
catch( const uno::Exception& )
|
|
{}
|
|
|
|
try {
|
|
m_xClientSite->saveObject();
|
|
}
|
|
catch( const uno::Exception& )
|
|
{
|
|
SAL_WARN( "embeddedobj.common", "The object was not stored!" );
|
|
}
|
|
}
|
|
|
|
|
|
OUString OCommonEmbeddedObject::GetBaseURL_Impl() const
|
|
{
|
|
OUString aBaseURL;
|
|
|
|
if ( m_xClientSite.is() )
|
|
{
|
|
try
|
|
{
|
|
uno::Reference< frame::XModel > xParentModel( m_xClientSite->getComponent(), uno::UNO_QUERY_THROW );
|
|
const uno::Sequence< beans::PropertyValue > aModelProps = xParentModel->getArgs();
|
|
for ( beans::PropertyValue const & prop : aModelProps )
|
|
if ( prop.Name == "DocumentBaseURL" )
|
|
{
|
|
prop.Value >>= aBaseURL;
|
|
break;
|
|
}
|
|
}
|
|
catch( const uno::Exception& )
|
|
{}
|
|
}
|
|
|
|
if ( aBaseURL.isEmpty() )
|
|
{
|
|
for ( beans::PropertyValue const & prop : m_aDocMediaDescriptor )
|
|
if ( prop.Name == "DocumentBaseURL" )
|
|
{
|
|
prop.Value >>= aBaseURL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( aBaseURL.isEmpty() )
|
|
aBaseURL = m_aDefaultParentBaseURL;
|
|
|
|
return aBaseURL;
|
|
}
|
|
|
|
|
|
OUString OCommonEmbeddedObject::GetBaseURLFrom_Impl(
|
|
const uno::Sequence< beans::PropertyValue >& lArguments,
|
|
const uno::Sequence< beans::PropertyValue >& lObjArgs )
|
|
{
|
|
OUString aBaseURL;
|
|
|
|
for ( beans::PropertyValue const & prop : lArguments )
|
|
if ( prop.Name == "DocumentBaseURL" )
|
|
{
|
|
prop.Value >>= aBaseURL;
|
|
break;
|
|
}
|
|
|
|
if ( aBaseURL.isEmpty() )
|
|
{
|
|
for ( beans::PropertyValue const & prop : lObjArgs )
|
|
if ( prop.Name == "DefaultParentBaseURL" )
|
|
{
|
|
prop.Value >>= aBaseURL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return aBaseURL;
|
|
}
|
|
|
|
|
|
void OCommonEmbeddedObject::SwitchDocToStorage_Impl( const uno::Reference< document::XStorageBasedDocument >& xDoc, const uno::Reference< embed::XStorage >& xStorage )
|
|
{
|
|
xDoc->switchToStorage( xStorage );
|
|
|
|
uno::Reference< util::XModifiable > xModif( xDoc, uno::UNO_QUERY );
|
|
if ( xModif.is() )
|
|
xModif->setModified( false );
|
|
|
|
if ( m_xRecoveryStorage.is() )
|
|
m_xRecoveryStorage.clear();
|
|
}
|
|
|
|
namespace {
|
|
|
|
OUString getStringPropertyValue( const uno::Sequence<beans::PropertyValue>& rProps, std::u16string_view rName )
|
|
{
|
|
OUString aStr;
|
|
|
|
for (beans::PropertyValue const & prop : rProps)
|
|
{
|
|
if (prop.Name == rName)
|
|
{
|
|
prop.Value >>= aStr;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return aStr;
|
|
}
|
|
|
|
}
|
|
|
|
void OCommonEmbeddedObject::StoreDocToStorage_Impl(
|
|
const uno::Reference<embed::XStorage>& xStorage,
|
|
const uno::Sequence<beans::PropertyValue>& rMediaArgs,
|
|
const uno::Sequence<beans::PropertyValue>& rObjArgs,
|
|
sal_Int32 nStorageFormat,
|
|
const OUString& aHierarchName,
|
|
bool bAttachToTheStorage )
|
|
{
|
|
SAL_WARN_IF( !xStorage.is(), "embeddedobj.common", "No storage is provided for storing!" );
|
|
|
|
if ( !xStorage.is() )
|
|
throw uno::RuntimeException(); // TODO:
|
|
|
|
uno::Reference< document::XStorageBasedDocument > xDoc;
|
|
{
|
|
osl::MutexGuard aGuard( m_aMutex );
|
|
if ( m_xDocHolder.is() )
|
|
xDoc.set( m_xDocHolder->GetComponent(), uno::UNO_QUERY );
|
|
}
|
|
|
|
OUString aBaseURL = GetBaseURLFrom_Impl(rMediaArgs, rObjArgs);
|
|
|
|
if ( xDoc.is() )
|
|
{
|
|
OUString aFilterName = GetFilterName( nStorageFormat );
|
|
|
|
// No filter found? Try the older format, e.g. Base has only that.
|
|
if (aFilterName.isEmpty() && nStorageFormat == SOFFICE_FILEFORMAT_CURRENT)
|
|
aFilterName = GetFilterName( SOFFICE_FILEFORMAT_60 );
|
|
|
|
SAL_WARN_IF( aFilterName.isEmpty(), "embeddedobj.common", "Wrong document service name!" );
|
|
if ( aFilterName.isEmpty() )
|
|
throw io::IOException(); // TODO:
|
|
|
|
uno::Sequence<beans::PropertyValue> aArgs(5);
|
|
aArgs[0].Name = "FilterName";
|
|
aArgs[0].Value <<= aFilterName;
|
|
aArgs[1].Name = "HierarchicalDocumentName";
|
|
aArgs[1].Value <<= aHierarchName;
|
|
aArgs[2].Name = "DocumentBaseURL";
|
|
aArgs[2].Value <<= aBaseURL;
|
|
aArgs[3].Name = "SourceShellID";
|
|
aArgs[3].Value <<= getStringPropertyValue(rObjArgs, u"SourceShellID");
|
|
aArgs[4].Name = "DestinationShellID";
|
|
aArgs[4].Value <<= getStringPropertyValue(rObjArgs, u"DestinationShellID");
|
|
|
|
xDoc->storeToStorage( xStorage, aArgs );
|
|
if ( bAttachToTheStorage )
|
|
SwitchDocToStorage_Impl( xDoc, xStorage );
|
|
}
|
|
else
|
|
{
|
|
// store document to temporary stream based on temporary file
|
|
uno::Reference < io::XInputStream > xTempIn = StoreDocumentToTempStream_Impl( nStorageFormat, aBaseURL, aHierarchName );
|
|
|
|
SAL_WARN_IF( !xTempIn.is(), "embeddedobj.common", "The stream reference can not be empty!" );
|
|
|
|
// open storage based on document temporary file for reading
|
|
uno::Reference < lang::XSingleServiceFactory > xStorageFactory = embed::StorageFactory::create(m_xContext);
|
|
|
|
uno::Sequence< uno::Any > aArgs(1);
|
|
aArgs[0] <<= xTempIn;
|
|
uno::Reference< embed::XStorage > xTempStorage( xStorageFactory->createInstanceWithArguments( aArgs ),
|
|
uno::UNO_QUERY_THROW );
|
|
|
|
// object storage must be committed automatically
|
|
xTempStorage->copyToStorage( xStorage );
|
|
}
|
|
}
|
|
|
|
|
|
uno::Reference< util::XCloseable > OCommonEmbeddedObject::CreateDocFromMediaDescr_Impl(
|
|
const uno::Sequence< beans::PropertyValue >& aMedDescr )
|
|
{
|
|
uno::Reference< util::XCloseable > xDocument( CreateDocument( m_xContext, GetDocumentServiceName(),
|
|
m_bEmbeddedScriptSupport, m_bDocumentRecoverySupport ) );
|
|
|
|
uno::Reference< frame::XLoadable > xLoadable( xDocument, uno::UNO_QUERY_THROW );
|
|
|
|
try
|
|
{
|
|
// set the document mode to embedded as the first action on the document!!!
|
|
EmbedAndReparentDoc_Impl( xDocument );
|
|
|
|
xLoadable->load( addAsTemplate( aMedDescr ) );
|
|
}
|
|
catch( const uno::Exception& )
|
|
{
|
|
if ( xDocument.is() )
|
|
{
|
|
try
|
|
{
|
|
xDocument->close( true );
|
|
}
|
|
catch( const uno::Exception& )
|
|
{
|
|
}
|
|
}
|
|
|
|
throw; // TODO
|
|
}
|
|
|
|
return xDocument;
|
|
}
|
|
|
|
|
|
uno::Reference< util::XCloseable > OCommonEmbeddedObject::CreateTempDocFromLink_Impl()
|
|
{
|
|
uno::Reference< util::XCloseable > xResult;
|
|
|
|
SAL_WARN_IF( !m_bIsLinkURL, "embeddedobj.common", "The object is not a linked one!" );
|
|
|
|
uno::Sequence< beans::PropertyValue > aTempMediaDescr;
|
|
|
|
sal_Int32 nStorageFormat = SOFFICE_FILEFORMAT_CURRENT;
|
|
try {
|
|
nStorageFormat = ::comphelper::OStorageHelper::GetXStorageFormat( m_xParentStorage );
|
|
}
|
|
catch ( const beans::IllegalTypeException& )
|
|
{
|
|
// the container just has an unknown type, use current file format
|
|
}
|
|
catch ( const uno::Exception& )
|
|
{
|
|
SAL_WARN( "embeddedobj.common", "Can not retrieve storage media type!" );
|
|
}
|
|
|
|
if ( m_xDocHolder->GetComponent().is() )
|
|
{
|
|
aTempMediaDescr.realloc( 4 );
|
|
|
|
// TODO/LATER: may be private:stream should be used as target URL
|
|
OUString aTempFileURL;
|
|
uno::Reference< io::XInputStream > xTempStream = StoreDocumentToTempStream_Impl( SOFFICE_FILEFORMAT_CURRENT,
|
|
OUString(),
|
|
OUString() );
|
|
try
|
|
{
|
|
// no need to let the file stay after the stream is removed since the embedded document
|
|
// can not be stored directly
|
|
uno::Reference< beans::XPropertySet > xTempStreamProps( xTempStream, uno::UNO_QUERY_THROW );
|
|
xTempStreamProps->getPropertyValue("Uri") >>= aTempFileURL;
|
|
}
|
|
catch( const uno::Exception& )
|
|
{
|
|
}
|
|
|
|
SAL_WARN_IF( aTempFileURL.isEmpty(), "embeddedobj.common", "Couldn't retrieve temporary file URL!" );
|
|
|
|
aTempMediaDescr[0].Name = "URL";
|
|
aTempMediaDescr[0].Value <<= aTempFileURL;
|
|
aTempMediaDescr[1].Name = "InputStream";
|
|
aTempMediaDescr[1].Value <<= xTempStream;
|
|
aTempMediaDescr[2].Name = "FilterName";
|
|
aTempMediaDescr[2].Value <<= GetFilterName( nStorageFormat );
|
|
aTempMediaDescr[3].Name = "AsTemplate";
|
|
aTempMediaDescr[3].Value <<= true;
|
|
}
|
|
else
|
|
{
|
|
aTempMediaDescr.realloc( 2 );
|
|
aTempMediaDescr[0].Name = "URL";
|
|
|
|
// tdf#141529 use URL of the linked TempFile if it exists
|
|
aTempMediaDescr[0].Value <<= m_aLinkTempFile.is()
|
|
? m_aLinkTempFile->getUri()
|
|
: m_aLinkURL;
|
|
|
|
aTempMediaDescr[1].Name = "FilterName";
|
|
aTempMediaDescr[1].Value <<= m_aLinkFilterName;
|
|
}
|
|
|
|
xResult = CreateDocFromMediaDescr_Impl( aTempMediaDescr );
|
|
|
|
return xResult;
|
|
}
|
|
|
|
|
|
void SAL_CALL OCommonEmbeddedObject::setPersistentEntry(
|
|
const uno::Reference< embed::XStorage >& xStorage,
|
|
const OUString& sEntName,
|
|
sal_Int32 nEntryConnectionMode,
|
|
const uno::Sequence< beans::PropertyValue >& lArguments,
|
|
const uno::Sequence< beans::PropertyValue >& lObjArgs )
|
|
{
|
|
// the type of the object must be already set
|
|
// a kind of typedetection should be done in the factory
|
|
|
|
::osl::MutexGuard aGuard( m_aMutex );
|
|
if ( m_bDisposed )
|
|
throw lang::DisposedException(); // TODO
|
|
|
|
if ( !xStorage.is() )
|
|
throw lang::IllegalArgumentException( "No parent storage is provided!",
|
|
static_cast< ::cppu::OWeakObject* >(this),
|
|
1 );
|
|
|
|
if ( sEntName.isEmpty() )
|
|
throw lang::IllegalArgumentException( "Empty element name is provided!",
|
|
static_cast< ::cppu::OWeakObject* >(this),
|
|
2 );
|
|
|
|
// May be LOADED should be forbidden here ???
|
|
if ( ( m_nObjectState != -1 || nEntryConnectionMode == embed::EntryInitModes::NO_INIT )
|
|
&& ( m_nObjectState == -1 || nEntryConnectionMode != embed::EntryInitModes::NO_INIT ) )
|
|
{
|
|
// if the object is not loaded
|
|
// it can not get persistent representation without initialization
|
|
|
|
// if the object is loaded
|
|
// it can switch persistent representation only without initialization
|
|
|
|
throw embed::WrongStateException(
|
|
"Can't change persistent representation of activated object!",
|
|
static_cast< ::cppu::OWeakObject* >(this) );
|
|
}
|
|
|
|
if ( m_bWaitSaveCompleted )
|
|
{
|
|
if ( nEntryConnectionMode != embed::EntryInitModes::NO_INIT )
|
|
throw embed::WrongStateException(
|
|
"The object waits for saveCompleted() call!",
|
|
static_cast< ::cppu::OWeakObject* >(this) );
|
|
// saveCompleted is expected, handle it accordingly
|
|
if ( m_xNewParentStorage == xStorage && m_aNewEntryName == sEntName )
|
|
{
|
|
saveCompleted( true );
|
|
return;
|
|
}
|
|
|
|
// if a completely different entry is provided, switch first back to the old persistence in saveCompleted
|
|
// and then switch to the target persistence
|
|
bool bSwitchFurther = ( m_xParentStorage != xStorage || m_aEntryName != sEntName );
|
|
saveCompleted( false );
|
|
if ( !bSwitchFurther )
|
|
return;
|
|
}
|
|
|
|
// for now support of this interface is required to allow breaking of links and converting them to normal embedded
|
|
// objects, so the persist name must be handled correctly ( althowgh no real persist entry is used )
|
|
// OSL_ENSURE( !m_bIsLinkURL, "This method implementation must not be used for links!" );
|
|
if ( m_bIsLinkURL )
|
|
{
|
|
m_aEntryName = sEntName;
|
|
return;
|
|
}
|
|
|
|
uno::Reference< container::XNameAccess > xNameAccess( xStorage, uno::UNO_QUERY_THROW );
|
|
|
|
// detect entry existence
|
|
bool bElExists = xNameAccess->hasByName( sEntName );
|
|
|
|
m_aDocMediaDescriptor = GetValuableArgs_Impl( lArguments,
|
|
nEntryConnectionMode != embed::EntryInitModes::MEDIA_DESCRIPTOR_INIT );
|
|
|
|
m_bReadOnly = false;
|
|
for ( beans::PropertyValue const & prop : lArguments )
|
|
if ( prop.Name == "ReadOnly" )
|
|
prop.Value >>= m_bReadOnly;
|
|
|
|
// TODO: use lObjArgs for StoreVisualReplacement
|
|
for ( beans::PropertyValue const & prop : lObjArgs )
|
|
if ( prop.Name == "OutplaceDispatchInterceptor" )
|
|
{
|
|
uno::Reference< frame::XDispatchProviderInterceptor > xDispatchInterceptor;
|
|
if ( prop.Value >>= xDispatchInterceptor )
|
|
m_xDocHolder->SetOutplaceDispatchInterceptor( xDispatchInterceptor );
|
|
}
|
|
else if ( prop.Name == "DefaultParentBaseURL" )
|
|
{
|
|
prop.Value >>= m_aDefaultParentBaseURL;
|
|
}
|
|
else if ( prop.Name == "Parent" )
|
|
{
|
|
prop.Value >>= m_xParent;
|
|
}
|
|
else if ( prop.Name == "IndividualMiscStatus" )
|
|
{
|
|
sal_Int64 nMiscStatus=0;
|
|
prop.Value >>= nMiscStatus;
|
|
m_nMiscStatus |= nMiscStatus;
|
|
}
|
|
else if ( prop.Name == "CloneFrom" )
|
|
{
|
|
uno::Reference < embed::XEmbeddedObject > xObj;
|
|
prop.Value >>= xObj;
|
|
if ( xObj.is() )
|
|
{
|
|
m_bHasClonedSize = true;
|
|
m_aClonedSize = xObj->getVisualAreaSize( embed::Aspects::MSOLE_CONTENT );
|
|
m_nClonedMapUnit = xObj->getMapUnit( embed::Aspects::MSOLE_CONTENT );
|
|
}
|
|
}
|
|
else if ( prop.Name == "OutplaceFrameProperties" )
|
|
{
|
|
uno::Sequence< uno::Any > aOutFrameProps;
|
|
uno::Sequence< beans::NamedValue > aOutFramePropsTyped;
|
|
if ( prop.Value >>= aOutFrameProps )
|
|
{
|
|
m_xDocHolder->SetOutplaceFrameProperties( aOutFrameProps );
|
|
}
|
|
else if ( prop.Value >>= aOutFramePropsTyped )
|
|
{
|
|
aOutFrameProps.realloc( aOutFramePropsTyped.getLength() );
|
|
uno::Any* pProp = aOutFrameProps.getArray();
|
|
for ( const beans::NamedValue* pTypedProp = aOutFramePropsTyped.getConstArray();
|
|
pTypedProp != aOutFramePropsTyped.getConstArray() + aOutFramePropsTyped.getLength();
|
|
++pTypedProp, ++pProp
|
|
)
|
|
{
|
|
*pProp <<= *pTypedProp;
|
|
}
|
|
m_xDocHolder->SetOutplaceFrameProperties( aOutFrameProps );
|
|
}
|
|
else
|
|
SAL_WARN( "embeddedobj.common", "OCommonEmbeddedObject::setPersistentEntry: illegal type for argument 'OutplaceFrameProperties'!" );
|
|
}
|
|
else if ( prop.Name == "ModuleName" )
|
|
{
|
|
prop.Value >>= m_aModuleName;
|
|
}
|
|
else if ( prop.Name == "EmbeddedScriptSupport" )
|
|
{
|
|
OSL_VERIFY( prop.Value >>= m_bEmbeddedScriptSupport );
|
|
}
|
|
else if ( prop.Name == "DocumentRecoverySupport" )
|
|
{
|
|
OSL_VERIFY( prop.Value >>= m_bDocumentRecoverySupport );
|
|
}
|
|
else if ( prop.Name == "RecoveryStorage" )
|
|
{
|
|
OSL_VERIFY( prop.Value >>= m_xRecoveryStorage );
|
|
}
|
|
|
|
|
|
sal_Int32 nStorageMode = m_bReadOnly ? embed::ElementModes::READ : embed::ElementModes::READWRITE;
|
|
|
|
SwitchOwnPersistence( xStorage, sEntName );
|
|
|
|
if ( nEntryConnectionMode == embed::EntryInitModes::DEFAULT_INIT )
|
|
{
|
|
if ( bElExists )
|
|
{
|
|
// the initialization from existing storage allows to leave object in loaded state
|
|
m_nObjectState = embed::EmbedStates::LOADED;
|
|
}
|
|
else
|
|
{
|
|
m_xDocHolder->SetComponent( InitNewDocument_Impl(), m_bReadOnly );
|
|
if ( !m_xDocHolder->GetComponent().is() )
|
|
throw io::IOException(); // TODO: can not create document
|
|
|
|
m_nObjectState = embed::EmbedStates::RUNNING;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( ( nStorageMode & embed::ElementModes::READWRITE ) != embed::ElementModes::READWRITE )
|
|
throw io::IOException();
|
|
|
|
if ( nEntryConnectionMode == embed::EntryInitModes::NO_INIT )
|
|
{
|
|
// the document just already changed its storage to store to
|
|
// the links to OOo documents for now ignore this call
|
|
// TODO: OOo links will have persistence so it will be switched here
|
|
}
|
|
else if ( nEntryConnectionMode == embed::EntryInitModes::TRUNCATE_INIT )
|
|
{
|
|
if ( m_xRecoveryStorage.is() )
|
|
TransferMediaType( m_xRecoveryStorage, m_xObjectStorage );
|
|
|
|
// TODO:
|
|
m_xDocHolder->SetComponent( InitNewDocument_Impl(), m_bReadOnly );
|
|
|
|
if ( !m_xDocHolder->GetComponent().is() )
|
|
throw io::IOException(); // TODO: can not create document
|
|
|
|
m_nObjectState = embed::EmbedStates::RUNNING;
|
|
}
|
|
else if ( nEntryConnectionMode == embed::EntryInitModes::MEDIA_DESCRIPTOR_INIT )
|
|
{
|
|
m_xDocHolder->SetComponent( CreateDocFromMediaDescr_Impl( lArguments ), m_bReadOnly );
|
|
m_nObjectState = embed::EmbedStates::RUNNING;
|
|
}
|
|
//else if ( nEntryConnectionMode == embed::EntryInitModes::TRANSFERABLE_INIT )
|
|
//{
|
|
//TODO:
|
|
//}
|
|
else
|
|
throw lang::IllegalArgumentException( "Wrong connection mode is provided!",
|
|
static_cast< ::cppu::OWeakObject* >(this),
|
|
3 );
|
|
}
|
|
}
|
|
|
|
|
|
void SAL_CALL OCommonEmbeddedObject::storeToEntry( const uno::Reference< embed::XStorage >& xStorage,
|
|
const OUString& sEntName,
|
|
const uno::Sequence< beans::PropertyValue >& lArguments,
|
|
const uno::Sequence< beans::PropertyValue >& lObjArgs )
|
|
{
|
|
::osl::ResettableMutexGuard aGuard( m_aMutex );
|
|
if ( m_bDisposed )
|
|
throw lang::DisposedException(); // TODO
|
|
|
|
if ( m_nObjectState == -1 )
|
|
{
|
|
// the object is still not loaded
|
|
throw embed::WrongStateException( "Can't store object without persistence!",
|
|
static_cast< ::cppu::OWeakObject* >(this) );
|
|
}
|
|
|
|
if ( m_bWaitSaveCompleted )
|
|
throw embed::WrongStateException(
|
|
"The object waits for saveCompleted() call!",
|
|
static_cast< ::cppu::OWeakObject* >(this) );
|
|
|
|
// for now support of this interface is required to allow breaking of links and converting them to normal embedded
|
|
// objects, so the persist name must be handled correctly ( althowgh no real persist entry is used )
|
|
// OSL_ENSURE( !m_bIsLinkURL, "This method implementation must not be used for links!" );
|
|
if ( m_bIsLinkURL )
|
|
return;
|
|
|
|
OSL_ENSURE( m_xParentStorage.is() && m_xObjectStorage.is(), "The object has no valid persistence!" );
|
|
|
|
sal_Int32 nTargetStorageFormat = SOFFICE_FILEFORMAT_CURRENT;
|
|
sal_Int32 nOriginalStorageFormat = SOFFICE_FILEFORMAT_CURRENT;
|
|
try {
|
|
nTargetStorageFormat = ::comphelper::OStorageHelper::GetXStorageFormat( xStorage );
|
|
}
|
|
catch ( const beans::IllegalTypeException& )
|
|
{
|
|
// the container just has an unknown type, use current file format
|
|
}
|
|
catch ( const uno::Exception& )
|
|
{
|
|
SAL_WARN( "embeddedobj.common", "Can not retrieve target storage media type!" );
|
|
}
|
|
if (nTargetStorageFormat == SOFFICE_FILEFORMAT_60)
|
|
{
|
|
SAL_INFO("embeddedobj.common", "fdo#78159: Storing OOoXML as ODF");
|
|
nTargetStorageFormat = SOFFICE_FILEFORMAT_CURRENT;
|
|
// setting MediaType is done later anyway, no need to do it here
|
|
}
|
|
|
|
try
|
|
{
|
|
nOriginalStorageFormat = ::comphelper::OStorageHelper::GetXStorageFormat( m_xParentStorage );
|
|
}
|
|
catch ( const beans::IllegalTypeException& )
|
|
{
|
|
// the container just has an unknown type, use current file format
|
|
}
|
|
catch ( const uno::Exception& )
|
|
{
|
|
SAL_WARN( "embeddedobj.common", "Can not retrieve own storage media type!" );
|
|
}
|
|
|
|
bool bTryOptimization = false;
|
|
for ( beans::PropertyValue const & prop : lObjArgs )
|
|
{
|
|
// StoreVisualReplacement and VisualReplacement args have no sense here
|
|
if ( prop.Name == "CanTryOptimization" )
|
|
prop.Value >>= bTryOptimization;
|
|
}
|
|
|
|
bool bSwitchBackToLoaded = false;
|
|
|
|
// Storing to different format can be done only in running state.
|
|
if ( m_nObjectState == embed::EmbedStates::LOADED )
|
|
{
|
|
// TODO/LATER: copying is not legal for documents with relative links.
|
|
if ( nTargetStorageFormat == nOriginalStorageFormat )
|
|
{
|
|
bool bOptimizationWorks = false;
|
|
if ( bTryOptimization )
|
|
{
|
|
try
|
|
{
|
|
// try to use optimized copying
|
|
uno::Reference< embed::XOptimizedStorage > xSource( m_xParentStorage, uno::UNO_QUERY_THROW );
|
|
uno::Reference< embed::XOptimizedStorage > xTarget( xStorage, uno::UNO_QUERY_THROW );
|
|
xSource->copyElementDirectlyTo( m_aEntryName, xTarget, sEntName );
|
|
bOptimizationWorks = true;
|
|
}
|
|
catch( const uno::Exception& )
|
|
{
|
|
}
|
|
}
|
|
|
|
if ( !bOptimizationWorks )
|
|
m_xParentStorage->copyElementTo( m_aEntryName, xStorage, sEntName );
|
|
}
|
|
else
|
|
{
|
|
changeState( embed::EmbedStates::RUNNING );
|
|
bSwitchBackToLoaded = true;
|
|
}
|
|
}
|
|
|
|
if ( m_nObjectState == embed::EmbedStates::LOADED )
|
|
return;
|
|
|
|
uno::Reference< embed::XStorage > xSubStorage =
|
|
xStorage->openStorageElement( sEntName, embed::ElementModes::READWRITE );
|
|
|
|
if ( !xSubStorage.is() )
|
|
throw uno::RuntimeException(); //TODO
|
|
|
|
aGuard.clear();
|
|
// TODO/LATER: support hierarchical name for embedded objects in embedded objects
|
|
StoreDocToStorage_Impl(
|
|
xSubStorage, lArguments, lObjArgs, nTargetStorageFormat, sEntName, false );
|
|
aGuard.reset();
|
|
|
|
if ( bSwitchBackToLoaded )
|
|
changeState( embed::EmbedStates::LOADED );
|
|
|
|
// TODO: should the listener notification be done?
|
|
}
|
|
|
|
|
|
void SAL_CALL OCommonEmbeddedObject::storeAsEntry( const uno::Reference< embed::XStorage >& xStorage,
|
|
const OUString& sEntName,
|
|
const uno::Sequence< beans::PropertyValue >& lArguments,
|
|
const uno::Sequence< beans::PropertyValue >& lObjArgs )
|
|
{
|
|
// TODO: use lObjArgs
|
|
|
|
::osl::ResettableMutexGuard aGuard( m_aMutex );
|
|
if ( m_bDisposed )
|
|
throw lang::DisposedException(); // TODO
|
|
|
|
if ( m_nObjectState == -1 )
|
|
{
|
|
// the object is still not loaded
|
|
throw embed::WrongStateException( "Can't store object without persistence!",
|
|
static_cast< ::cppu::OWeakObject* >(this) );
|
|
}
|
|
|
|
if ( m_bWaitSaveCompleted )
|
|
throw embed::WrongStateException(
|
|
"The object waits for saveCompleted() call!",
|
|
static_cast< ::cppu::OWeakObject* >(this) );
|
|
|
|
// for now support of this interface is required to allow breaking of links and converting them to normal embedded
|
|
// objects, so the persist name must be handled correctly ( althowgh no real persist entry is used )
|
|
// OSL_ENSURE( !m_bIsLinkURL, "This method implementation must not be used for links!" );
|
|
if ( m_bIsLinkURL )
|
|
{
|
|
m_aNewEntryName = sEntName;
|
|
|
|
if(m_aLinkTempFile.is() && m_bLinkTempFileChanged)
|
|
{
|
|
// tdf#141529 if we have a changed copy of the original OLE data we now
|
|
// need to write it back 'over' the original OLE data
|
|
uno::Reference < ucb::XSimpleFileAccess2 > xFileAccess(ucb::SimpleFileAccess::create( m_xContext ));
|
|
uno::Reference < io::XInputStream > xTempIn = m_aLinkTempFile->getInputStream();
|
|
|
|
// This is *needed* since OTempFileService calls OTempFileService::readBytes which
|
|
// ensures the SvStream mpStream gets/is opened, *but* also sets the mnCachedPos from
|
|
// OTempFileService which still points to the end-of-file (from write-cc'ing).
|
|
uno::Reference < io::XSeekable > xSeek( xTempIn, uno::UNO_QUERY_THROW );
|
|
xSeek->seek(0);
|
|
|
|
xFileAccess->writeFile(m_aLinkURL, xTempIn);
|
|
|
|
// Do *not* close input, that would remove the temporary file too early
|
|
// xTempIn->closeInput();
|
|
|
|
// reset flag m_bLinkTempFileChanged
|
|
m_bLinkTempFileChanged = false;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
OSL_ENSURE( m_xParentStorage.is() && m_xObjectStorage.is(), "The object has no valid persistence!" );
|
|
|
|
sal_Int32 nTargetStorageFormat = SOFFICE_FILEFORMAT_CURRENT;
|
|
sal_Int32 nOriginalStorageFormat = SOFFICE_FILEFORMAT_CURRENT;
|
|
try {
|
|
nTargetStorageFormat = ::comphelper::OStorageHelper::GetXStorageFormat( xStorage );
|
|
}
|
|
catch ( const beans::IllegalTypeException& )
|
|
{
|
|
// the container just has an unknown type, use current file format
|
|
}
|
|
catch ( const uno::Exception& )
|
|
{
|
|
SAL_WARN( "embeddedobj.common", "Can not retrieve target storage media type!" );
|
|
}
|
|
if (nTargetStorageFormat == SOFFICE_FILEFORMAT_60)
|
|
{
|
|
SAL_INFO("embeddedobj.common", "fdo#78159: Storing OOoXML as ODF");
|
|
nTargetStorageFormat = SOFFICE_FILEFORMAT_CURRENT;
|
|
// setting MediaType is done later anyway, no need to do it here
|
|
}
|
|
|
|
try
|
|
{
|
|
nOriginalStorageFormat = ::comphelper::OStorageHelper::GetXStorageFormat( m_xParentStorage );
|
|
}
|
|
catch ( const beans::IllegalTypeException& )
|
|
{
|
|
// the container just has an unknown type, use current file format
|
|
}
|
|
catch ( const uno::Exception& )
|
|
{
|
|
SAL_WARN( "embeddedobj.common", "Can not retrieve own storage media type!" );
|
|
}
|
|
|
|
PostEvent_Impl( "OnSaveAs" );
|
|
|
|
bool bTryOptimization = false;
|
|
for ( beans::PropertyValue const & prop : lObjArgs )
|
|
{
|
|
// StoreVisualReplacement and VisualReplacement args have no sense here
|
|
if ( prop.Name == "CanTryOptimization" )
|
|
prop.Value >>= bTryOptimization;
|
|
}
|
|
|
|
bool bSwitchBackToLoaded = false;
|
|
|
|
// Storing to different format can be done only in running state.
|
|
if ( m_nObjectState == embed::EmbedStates::LOADED )
|
|
{
|
|
// TODO/LATER: copying is not legal for documents with relative links.
|
|
if ( nTargetStorageFormat == nOriginalStorageFormat )
|
|
{
|
|
bool bOptimizationWorks = false;
|
|
if ( bTryOptimization )
|
|
{
|
|
try
|
|
{
|
|
// try to use optimized copying
|
|
uno::Reference< embed::XOptimizedStorage > xSource( m_xParentStorage, uno::UNO_QUERY_THROW );
|
|
uno::Reference< embed::XOptimizedStorage > xTarget( xStorage, uno::UNO_QUERY_THROW );
|
|
xSource->copyElementDirectlyTo( m_aEntryName, xTarget, sEntName );
|
|
bOptimizationWorks = true;
|
|
}
|
|
catch( const uno::Exception& )
|
|
{
|
|
}
|
|
}
|
|
|
|
if ( !bOptimizationWorks )
|
|
m_xParentStorage->copyElementTo( m_aEntryName, xStorage, sEntName );
|
|
}
|
|
else
|
|
{
|
|
changeState( embed::EmbedStates::RUNNING );
|
|
bSwitchBackToLoaded = true;
|
|
}
|
|
}
|
|
|
|
uno::Reference< embed::XStorage > xSubStorage =
|
|
xStorage->openStorageElement( sEntName, embed::ElementModes::READWRITE );
|
|
|
|
if ( !xSubStorage.is() )
|
|
throw uno::RuntimeException(); //TODO
|
|
|
|
if ( m_nObjectState != embed::EmbedStates::LOADED )
|
|
{
|
|
aGuard.clear();
|
|
// TODO/LATER: support hierarchical name for embedded objects in embedded objects
|
|
StoreDocToStorage_Impl(
|
|
xSubStorage, lArguments, lObjArgs, nTargetStorageFormat, sEntName, false );
|
|
aGuard.reset();
|
|
|
|
if ( bSwitchBackToLoaded )
|
|
changeState( embed::EmbedStates::LOADED );
|
|
}
|
|
|
|
m_bWaitSaveCompleted = true;
|
|
m_xNewObjectStorage = xSubStorage;
|
|
m_xNewParentStorage = xStorage;
|
|
m_aNewEntryName = sEntName;
|
|
m_aNewDocMediaDescriptor = GetValuableArgs_Impl( lArguments, true );
|
|
|
|
// TODO: register listeners for storages above, in case they are disposed
|
|
// an exception will be thrown on saveCompleted( true )
|
|
|
|
// TODO: should the listener notification be done here or in saveCompleted?
|
|
}
|
|
|
|
|
|
void SAL_CALL OCommonEmbeddedObject::saveCompleted( sal_Bool bUseNew )
|
|
{
|
|
::osl::MutexGuard aGuard( m_aMutex );
|
|
if ( m_bDisposed )
|
|
throw lang::DisposedException(); // TODO
|
|
|
|
if ( m_nObjectState == -1 )
|
|
{
|
|
// the object is still not loaded
|
|
throw embed::WrongStateException( "Can't store object without persistence!",
|
|
static_cast< ::cppu::OWeakObject* >(this) );
|
|
}
|
|
|
|
// for now support of this interface is required to allow breaking of links and converting them to normal embedded
|
|
// objects, so the persist name must be handled correctly ( althowgh no real persist entry is used )
|
|
// OSL_ENSURE( !m_bIsLinkURL, "This method implementation must not be used for links!" );
|
|
if ( m_bIsLinkURL )
|
|
{
|
|
if ( bUseNew )
|
|
m_aEntryName = m_aNewEntryName;
|
|
m_aNewEntryName.clear();
|
|
return;
|
|
}
|
|
|
|
// it is allowed to call saveCompleted( false ) for nonstored objects
|
|
if ( !m_bWaitSaveCompleted && !bUseNew )
|
|
return;
|
|
|
|
SAL_WARN_IF( !m_bWaitSaveCompleted, "embeddedobj.common", "Unexpected saveCompleted() call!" );
|
|
if ( !m_bWaitSaveCompleted )
|
|
throw io::IOException(); // TODO: illegal call
|
|
|
|
OSL_ENSURE( m_xNewObjectStorage.is() && m_xNewParentStorage.is() , "Internal object information is broken!" );
|
|
if ( !m_xNewObjectStorage.is() || !m_xNewParentStorage.is() )
|
|
throw uno::RuntimeException(); // TODO: broken internal information
|
|
|
|
if ( bUseNew )
|
|
{
|
|
SwitchOwnPersistence( m_xNewParentStorage, m_xNewObjectStorage, m_aNewEntryName );
|
|
m_aDocMediaDescriptor = m_aNewDocMediaDescriptor;
|
|
|
|
uno::Reference< util::XModifiable > xModif( m_xDocHolder->GetComponent(), uno::UNO_QUERY );
|
|
if ( xModif.is() )
|
|
xModif->setModified( false );
|
|
|
|
PostEvent_Impl( "OnSaveAsDone");
|
|
}
|
|
else
|
|
{
|
|
try {
|
|
m_xNewObjectStorage->dispose();
|
|
}
|
|
catch ( const uno::Exception& )
|
|
{
|
|
}
|
|
}
|
|
|
|
m_xNewObjectStorage.clear();
|
|
m_xNewParentStorage.clear();
|
|
m_aNewEntryName.clear();
|
|
m_aNewDocMediaDescriptor.realloc( 0 );
|
|
m_bWaitSaveCompleted = false;
|
|
|
|
if ( bUseNew )
|
|
{
|
|
// TODO: notify listeners
|
|
|
|
if ( m_nUpdateMode == embed::EmbedUpdateModes::ALWAYS_UPDATE )
|
|
{
|
|
// TODO: update visual representation
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
sal_Bool SAL_CALL OCommonEmbeddedObject::hasEntry()
|
|
{
|
|
::osl::MutexGuard aGuard( m_aMutex );
|
|
if ( m_bDisposed )
|
|
throw lang::DisposedException(); // TODO
|
|
|
|
if ( m_bWaitSaveCompleted )
|
|
throw embed::WrongStateException(
|
|
"The object waits for saveCompleted() call!",
|
|
static_cast< ::cppu::OWeakObject* >(this) );
|
|
|
|
if ( m_xObjectStorage.is() )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
OUString SAL_CALL OCommonEmbeddedObject::getEntryName()
|
|
{
|
|
::osl::MutexGuard aGuard( m_aMutex );
|
|
if ( m_bDisposed )
|
|
throw lang::DisposedException(); // TODO
|
|
|
|
if ( m_nObjectState == -1 )
|
|
{
|
|
// the object is still not loaded
|
|
throw embed::WrongStateException( "The object persistence is not initialized!",
|
|
static_cast< ::cppu::OWeakObject* >(this) );
|
|
}
|
|
|
|
if ( m_bWaitSaveCompleted )
|
|
throw embed::WrongStateException(
|
|
"The object waits for saveCompleted() call!",
|
|
static_cast< ::cppu::OWeakObject* >(this) );
|
|
|
|
return m_aEntryName;
|
|
}
|
|
|
|
|
|
void SAL_CALL OCommonEmbeddedObject::storeOwn()
|
|
{
|
|
// during switching from Activated to Running and from Running to Loaded states the object will
|
|
// ask container to store the object, the container has to make decision
|
|
// to do so or not
|
|
|
|
::osl::ResettableMutexGuard aGuard( m_aMutex );
|
|
if ( m_bDisposed )
|
|
throw lang::DisposedException(); // TODO
|
|
|
|
if ( m_nObjectState == -1 )
|
|
{
|
|
// the object is still not loaded
|
|
throw embed::WrongStateException( "Can't store object without persistence!",
|
|
static_cast< ::cppu::OWeakObject* >(this) );
|
|
}
|
|
|
|
if ( m_bWaitSaveCompleted )
|
|
throw embed::WrongStateException(
|
|
"The object waits for saveCompleted() call!",
|
|
static_cast< ::cppu::OWeakObject* >(this) );
|
|
|
|
if ( m_bReadOnly )
|
|
throw io::IOException(); // TODO: access denied
|
|
|
|
// nothing to do, if the object is in loaded state
|
|
if ( m_nObjectState == embed::EmbedStates::LOADED )
|
|
return;
|
|
|
|
PostEvent_Impl( "OnSave" );
|
|
|
|
SAL_WARN_IF( !m_xDocHolder->GetComponent().is(), "embeddedobj.common", "If an object is activated or in running state it must have a document!" );
|
|
if ( !m_xDocHolder->GetComponent().is() )
|
|
throw uno::RuntimeException();
|
|
|
|
if ( m_bIsLinkURL )
|
|
{
|
|
// TODO: just store the document to its location
|
|
uno::Reference< frame::XStorable > xStorable( m_xDocHolder->GetComponent(), uno::UNO_QUERY_THROW );
|
|
|
|
// free the main mutex for the storing time
|
|
aGuard.clear();
|
|
|
|
xStorable->store();
|
|
|
|
aGuard.reset();
|
|
}
|
|
else
|
|
{
|
|
OSL_ENSURE( m_xParentStorage.is() && m_xObjectStorage.is(), "The object has no valid persistence!" );
|
|
|
|
if ( !m_xObjectStorage.is() )
|
|
throw io::IOException(); //TODO: access denied
|
|
|
|
sal_Int32 nStorageFormat = SOFFICE_FILEFORMAT_CURRENT;
|
|
try {
|
|
nStorageFormat = ::comphelper::OStorageHelper::GetXStorageFormat( m_xParentStorage );
|
|
}
|
|
catch ( const beans::IllegalTypeException& )
|
|
{
|
|
// the container just has an unknown type, use current file format
|
|
}
|
|
catch ( const uno::Exception& )
|
|
{
|
|
SAL_WARN( "embeddedobj.common", "Can not retrieve storage media type!" );
|
|
}
|
|
if (nStorageFormat == SOFFICE_FILEFORMAT_60)
|
|
{
|
|
SAL_INFO("embeddedobj.common", "fdo#78159: Storing OOoXML as ODF");
|
|
nStorageFormat = SOFFICE_FILEFORMAT_CURRENT;
|
|
// setting MediaType is done later anyway, no need to do it here
|
|
}
|
|
|
|
aGuard.clear();
|
|
uno::Sequence<beans::PropertyValue> aEmpty;
|
|
uno::Sequence<beans::PropertyValue> aMediaArgs(1);
|
|
aMediaArgs[0].Name = "DocumentBaseURL";
|
|
aMediaArgs[0].Value <<= GetBaseURL_Impl();
|
|
StoreDocToStorage_Impl( m_xObjectStorage, aMediaArgs, aEmpty, nStorageFormat, m_aEntryName, true );
|
|
aGuard.reset();
|
|
}
|
|
|
|
uno::Reference< util::XModifiable > xModif( m_xDocHolder->GetComponent(), uno::UNO_QUERY );
|
|
if ( xModif.is() )
|
|
xModif->setModified( false );
|
|
|
|
PostEvent_Impl( "OnSaveDone" );
|
|
}
|
|
|
|
|
|
sal_Bool SAL_CALL OCommonEmbeddedObject::isReadonly()
|
|
{
|
|
::osl::MutexGuard aGuard( m_aMutex );
|
|
if ( m_bDisposed )
|
|
throw lang::DisposedException(); // TODO
|
|
|
|
if ( m_nObjectState == -1 )
|
|
{
|
|
// the object is still not loaded
|
|
throw embed::WrongStateException( "The object persistence is not initialized!",
|
|
static_cast< ::cppu::OWeakObject* >(this) );
|
|
}
|
|
|
|
if ( m_bWaitSaveCompleted )
|
|
throw embed::WrongStateException(
|
|
"The object waits for saveCompleted() call!",
|
|
static_cast< ::cppu::OWeakObject* >(this) );
|
|
|
|
return m_bReadOnly;
|
|
}
|
|
|
|
|
|
void SAL_CALL OCommonEmbeddedObject::reload(
|
|
const uno::Sequence< beans::PropertyValue >& lArguments,
|
|
const uno::Sequence< beans::PropertyValue >& lObjArgs )
|
|
{
|
|
// TODO: use lObjArgs
|
|
// for now this method is used only to switch readonly state
|
|
|
|
::osl::MutexGuard aGuard( m_aMutex );
|
|
if ( m_bDisposed )
|
|
throw lang::DisposedException(); // TODO
|
|
|
|
if ( m_nObjectState == -1 )
|
|
{
|
|
// the object is still not loaded
|
|
throw embed::WrongStateException( "The object persistence is not initialized!",
|
|
static_cast< ::cppu::OWeakObject* >(this) );
|
|
}
|
|
|
|
if ( m_nObjectState != embed::EmbedStates::LOADED )
|
|
{
|
|
// the object is still not loaded
|
|
throw embed::WrongStateException(
|
|
"The object must be in loaded state to be reloaded!",
|
|
static_cast< ::cppu::OWeakObject* >(this) );
|
|
}
|
|
|
|
if ( m_bWaitSaveCompleted )
|
|
throw embed::WrongStateException(
|
|
"The object waits for saveCompleted() call!",
|
|
static_cast< ::cppu::OWeakObject* >(this) );
|
|
|
|
if ( m_bIsLinkURL )
|
|
{
|
|
// reload of the link
|
|
OUString aOldLinkFilter = m_aLinkFilterName;
|
|
|
|
OUString aNewLinkFilter;
|
|
for ( beans::PropertyValue const & prop : lArguments )
|
|
{
|
|
if ( prop.Name == "URL" )
|
|
{
|
|
// the new URL
|
|
prop.Value >>= m_aLinkURL;
|
|
m_aLinkFilterName.clear();
|
|
}
|
|
else if ( prop.Name == "FilterName" )
|
|
{
|
|
prop.Value >>= aNewLinkFilter;
|
|
m_aLinkFilterName.clear();
|
|
}
|
|
}
|
|
|
|
::comphelper::MimeConfigurationHelper aHelper( m_xContext );
|
|
if ( m_aLinkFilterName.isEmpty() )
|
|
{
|
|
if ( !aNewLinkFilter.isEmpty() )
|
|
m_aLinkFilterName = aNewLinkFilter;
|
|
else
|
|
{
|
|
uno::Sequence< beans::PropertyValue > aArgs( 1 );
|
|
aArgs[0].Name = "URL";
|
|
aArgs[0].Value <<= m_aLinkURL;
|
|
m_aLinkFilterName = aHelper.UpdateMediaDescriptorWithFilterName( aArgs, false );
|
|
}
|
|
}
|
|
|
|
if ( aOldLinkFilter != m_aLinkFilterName )
|
|
{
|
|
uno::Sequence< beans::NamedValue > aObject = aHelper.GetObjectPropsByFilter( m_aLinkFilterName );
|
|
|
|
// TODO/LATER: probably the document holder could be cleaned explicitly as in the destructor
|
|
m_xDocHolder.clear();
|
|
|
|
LinkInit_Impl( aObject, lArguments, lObjArgs );
|
|
}
|
|
}
|
|
|
|
m_aDocMediaDescriptor = GetValuableArgs_Impl( lArguments, true );
|
|
|
|
// TODO: use lObjArgs for StoreVisualReplacement
|
|
for ( beans::PropertyValue const & prop : lObjArgs )
|
|
if ( prop.Name == "OutplaceDispatchInterceptor" )
|
|
{
|
|
uno::Reference< frame::XDispatchProviderInterceptor > xDispatchInterceptor;
|
|
if ( prop.Value >>= xDispatchInterceptor )
|
|
m_xDocHolder->SetOutplaceDispatchInterceptor( xDispatchInterceptor );
|
|
|
|
break;
|
|
}
|
|
|
|
// TODO:
|
|
// when document allows reloading through API the object can be reloaded not only in loaded state
|
|
|
|
bool bOldReadOnlyValue = m_bReadOnly;
|
|
|
|
m_bReadOnly = false;
|
|
for ( beans::PropertyValue const & prop : lArguments )
|
|
if ( prop.Name == "ReadOnly" )
|
|
prop.Value >>= m_bReadOnly;
|
|
|
|
if ( bOldReadOnlyValue == m_bReadOnly || m_bIsLinkURL )
|
|
return;
|
|
|
|
// close own storage
|
|
try {
|
|
if ( m_xObjectStorage.is() )
|
|
m_xObjectStorage->dispose();
|
|
}
|
|
catch ( const uno::Exception& )
|
|
{
|
|
}
|
|
|
|
sal_Int32 nStorageMode = m_bReadOnly ? embed::ElementModes::READ : embed::ElementModes::READWRITE;
|
|
m_xObjectStorage = m_xParentStorage->openStorageElement( m_aEntryName, nStorageMode );
|
|
}
|
|
|
|
sal_Bool SAL_CALL OCommonEmbeddedObject::isStored()
|
|
{
|
|
if (!m_xObjectStorage.is())
|
|
return false;
|
|
|
|
return m_xObjectStorage->getElementNames().hasElements();
|
|
}
|
|
|
|
|
|
void SAL_CALL OCommonEmbeddedObject::breakLink( const uno::Reference< embed::XStorage >& xStorage,
|
|
const OUString& sEntName )
|
|
{
|
|
::osl::ResettableMutexGuard aGuard( m_aMutex );
|
|
if ( m_bDisposed )
|
|
throw lang::DisposedException(); // TODO
|
|
|
|
if (!m_bIsLinkURL || m_nObjectState == -1)
|
|
{
|
|
// it must be a linked initialized object
|
|
throw embed::WrongStateException(
|
|
"The object is not a valid linked object!",
|
|
static_cast< ::cppu::OWeakObject* >(this) );
|
|
}
|
|
// the current implementation of OOo links does not implement this method since it does not implement
|
|
// all the set of interfaces required for OOo embedded object ( XEmbedPersist is not supported ).
|
|
|
|
if ( !xStorage.is() )
|
|
throw lang::IllegalArgumentException( "No parent storage is provided!",
|
|
static_cast< ::cppu::OWeakObject* >(this),
|
|
1 );
|
|
|
|
if ( sEntName.isEmpty() )
|
|
throw lang::IllegalArgumentException( "Empty element name is provided!",
|
|
static_cast< ::cppu::OWeakObject* >(this),
|
|
2 );
|
|
|
|
if ( m_bWaitSaveCompleted )
|
|
throw embed::WrongStateException(
|
|
"The object waits for saveCompleted() call!",
|
|
static_cast< ::cppu::OWeakObject* >(this) );
|
|
|
|
uno::Reference< container::XNameAccess > xNameAccess( xStorage, uno::UNO_QUERY_THROW );
|
|
|
|
m_bReadOnly = false;
|
|
|
|
if ( m_xParentStorage != xStorage || m_aEntryName != sEntName )
|
|
SwitchOwnPersistence( xStorage, sEntName );
|
|
|
|
// for linked object it means that it becomes embedded object
|
|
// the document must switch it's persistence also
|
|
|
|
// TODO/LATER: handle the case when temp doc can not be created
|
|
// the document is a new embedded object so it must be marked as modified
|
|
uno::Reference< util::XCloseable > xDocument = CreateTempDocFromLink_Impl();
|
|
try
|
|
{
|
|
if(m_xDocHolder.is() && m_xDocHolder->GetComponent().is())
|
|
{
|
|
// tdf#141528 m_xDocHolder->GetComponent() may be not set, so add it
|
|
// to the try path to not get thrown out of the local context to the next
|
|
// higher try...catch on the stack. To make breakLink work it is
|
|
// *necessary* to execute the code below that resets the linked state,
|
|
// esp. the *.clear stuff and resetting m_bIsLink.
|
|
uno::Reference< util::XModifiable > xModif( m_xDocHolder->GetComponent(), uno::UNO_QUERY_THROW );
|
|
|
|
// all other locations in this file check for xModif.is(), so do it here, too
|
|
if ( xModif.is() )
|
|
xModif->setModified( true );
|
|
}
|
|
}
|
|
catch( const uno::Exception& )
|
|
{}
|
|
|
|
m_xDocHolder->SetComponent( xDocument, m_bReadOnly );
|
|
SAL_WARN_IF( !m_xDocHolder->GetComponent().is(), "embeddedobj.common", "If document can't be created, an exception must be thrown!" );
|
|
|
|
if ( m_nObjectState == embed::EmbedStates::LOADED )
|
|
{
|
|
// the state is changed and can not be switched to loaded state back without saving
|
|
m_nObjectState = embed::EmbedStates::RUNNING;
|
|
StateChangeNotification_Impl( false, embed::EmbedStates::LOADED, m_nObjectState, aGuard );
|
|
}
|
|
else if ( m_nObjectState == embed::EmbedStates::ACTIVE )
|
|
m_xDocHolder->Show();
|
|
|
|
// tdf#141529 reset all stuff involved in linked state, including
|
|
// the OLE content copied to the temp file
|
|
m_bIsLinkURL = false;
|
|
m_aLinkTempFile.clear();
|
|
m_aLinkFilterName.clear();
|
|
m_aLinkURL.clear();
|
|
}
|
|
|
|
|
|
sal_Bool SAL_CALL OCommonEmbeddedObject::isLink()
|
|
{
|
|
::osl::MutexGuard aGuard( m_aMutex );
|
|
if ( m_bDisposed )
|
|
throw lang::DisposedException(); // TODO
|
|
|
|
return m_bIsLinkURL;
|
|
}
|
|
|
|
|
|
OUString SAL_CALL OCommonEmbeddedObject::getLinkURL()
|
|
{
|
|
::osl::MutexGuard aGuard( m_aMutex );
|
|
if ( m_bDisposed )
|
|
throw lang::DisposedException(); // TODO
|
|
|
|
if ( !m_bIsLinkURL )
|
|
throw embed::WrongStateException(
|
|
"The object is not a link object!",
|
|
static_cast< ::cppu::OWeakObject* >(this) );
|
|
|
|
return m_aLinkURL;
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|