bump product version to 5.0.4.1
[LibreOffice.git] / oox / source / ole / olestorage.cxx
blob296898c63433216c23ef2a056a954f4104f34333
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 "oox/ole/olestorage.hxx"
22 #include <com/sun/star/beans/PropertyValue.hpp>
23 #include <com/sun/star/container/XNameContainer.hpp>
24 #include <com/sun/star/embed/XTransactedObject.hpp>
25 #include <com/sun/star/io/TempFile.hpp>
26 #include <com/sun/star/io/XInputStream.hpp>
27 #include <com/sun/star/io/XOutputStream.hpp>
28 #include <com/sun/star/io/XSeekable.hpp>
29 #include <com/sun/star/io/XStream.hpp>
30 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
31 #include <com/sun/star/uno/XComponentContext.hpp>
32 #include <cppuhelper/implbase2.hxx>
33 #include <osl/diagnose.h>
34 #include "oox/helper/binaryinputstream.hxx"
35 #include "oox/helper/binaryoutputstream.hxx"
36 #include "oox/helper/containerhelper.hxx"
37 #include "oox/helper/helper.hxx"
39 namespace oox {
40 namespace ole {
42 using namespace ::com::sun::star::beans;
43 using namespace ::com::sun::star::container;
44 using namespace ::com::sun::star::embed;
45 using namespace ::com::sun::star::io;
46 using namespace ::com::sun::star::lang;
47 using namespace ::com::sun::star::uno;
49 namespace {
51 typedef ::cppu::WeakImplHelper2< XSeekable, XOutputStream > OleOutputStreamBase;
53 /** Implementation of an OLE storage output stream that inserts itself into the
54 storage when it is closed.
56 class OleOutputStream : public OleOutputStreamBase
58 public:
59 explicit OleOutputStream(
60 const Reference< XComponentContext >& rxContext,
61 const Reference< XNameContainer >& rxStorage,
62 const OUString& rElementName );
63 virtual ~OleOutputStream();
65 virtual void SAL_CALL seek( sal_Int64 nPos ) throw( IllegalArgumentException, IOException, RuntimeException, std::exception ) SAL_OVERRIDE;
66 virtual sal_Int64 SAL_CALL getPosition() throw( IOException, RuntimeException, std::exception ) SAL_OVERRIDE;
67 virtual sal_Int64 SAL_CALL getLength() throw( IOException, RuntimeException, std::exception ) SAL_OVERRIDE;
69 virtual void SAL_CALL writeBytes( const Sequence< sal_Int8 >& rData ) throw( NotConnectedException, BufferSizeExceededException, IOException, RuntimeException, std::exception ) SAL_OVERRIDE;
70 virtual void SAL_CALL flush() throw( NotConnectedException, BufferSizeExceededException, IOException, RuntimeException, std::exception ) SAL_OVERRIDE;
71 virtual void SAL_CALL closeOutput() throw( NotConnectedException, BufferSizeExceededException, IOException, RuntimeException, std::exception ) SAL_OVERRIDE;
73 private:
74 void ensureSeekable() const throw( IOException );
75 void ensureConnected() const throw( NotConnectedException );
77 private:
78 Reference< XNameContainer > mxStorage;
79 Reference< XStream > mxTempFile;
80 Reference< XOutputStream > mxOutStrm;
81 Reference< XSeekable > mxSeekable;
82 OUString maElementName;
85 OleOutputStream::OleOutputStream( const Reference< XComponentContext >& rxContext,
86 const Reference< XNameContainer >& rxStorage, const OUString& rElementName ) :
87 mxStorage( rxStorage ),
88 maElementName( rElementName )
90 try
92 mxTempFile.set( TempFile::create(rxContext), UNO_QUERY_THROW );
93 mxOutStrm = mxTempFile->getOutputStream();
94 mxSeekable.set( mxOutStrm, UNO_QUERY );
96 catch(const Exception& )
101 OleOutputStream::~OleOutputStream()
105 void SAL_CALL OleOutputStream::seek( sal_Int64 nPos ) throw( IllegalArgumentException, IOException, RuntimeException, std::exception )
107 ensureSeekable();
108 mxSeekable->seek( nPos );
111 sal_Int64 SAL_CALL OleOutputStream::getPosition() throw( IOException, RuntimeException, std::exception )
113 ensureSeekable();
114 return mxSeekable->getPosition();
117 sal_Int64 SAL_CALL OleOutputStream::getLength() throw( IOException, RuntimeException, std::exception )
119 ensureSeekable();
120 return mxSeekable->getLength();
123 void SAL_CALL OleOutputStream::writeBytes( const Sequence< sal_Int8 >& rData ) throw( NotConnectedException, BufferSizeExceededException, IOException, RuntimeException, std::exception )
125 ensureConnected();
126 mxOutStrm->writeBytes( rData );
129 void SAL_CALL OleOutputStream::flush() throw( NotConnectedException, BufferSizeExceededException, IOException, RuntimeException, std::exception )
131 ensureConnected();
132 mxOutStrm->flush();
135 void SAL_CALL OleOutputStream::closeOutput() throw( NotConnectedException, BufferSizeExceededException, IOException, RuntimeException, std::exception )
137 ensureConnected();
138 ensureSeekable();
139 // remember the class members
140 Reference< XOutputStream > xOutStrm = mxOutStrm;
141 Reference< XSeekable > xSeekable = mxSeekable;
142 // reset all class members
143 mxOutStrm.clear();
144 mxSeekable.clear();
145 // close stream (and let it throw something if needed)
146 xOutStrm->closeOutput();
147 // on success, insert the stream into the OLE storage (must be seek-ed back before)
148 xSeekable->seek( 0 );
149 if( !ContainerHelper::insertByName( mxStorage, maElementName, Any( mxTempFile ) ) )
150 throw IOException();
153 void OleOutputStream::ensureSeekable() const throw( IOException )
155 if( !mxSeekable.is() )
156 throw IOException();
159 void OleOutputStream::ensureConnected() const throw( NotConnectedException )
161 if( !mxOutStrm.is() )
162 throw NotConnectedException();
165 } // namespace
167 OleStorage::OleStorage( const Reference< XComponentContext >& rxContext,
168 const Reference< XInputStream >& rxInStream, bool bBaseStreamAccess ) :
169 StorageBase( rxInStream, bBaseStreamAccess ),
170 mxContext( rxContext ),
171 mpParentStorage( 0 )
173 OSL_ENSURE( mxContext.is(), "OleStorage::OleStorage - missing component context" );
174 initStorage( rxInStream );
177 OleStorage::OleStorage( const Reference< XComponentContext >& rxContext,
178 const Reference< XStream >& rxOutStream, bool bBaseStreamAccess ) :
179 StorageBase( rxOutStream, bBaseStreamAccess ),
180 mxContext( rxContext ),
181 mpParentStorage( 0 )
183 OSL_ENSURE( mxContext.is(), "OleStorage::OleStorage - missing component context" );
184 initStorage( rxOutStream );
187 OleStorage::OleStorage( const OleStorage& rParentStorage,
188 const Reference< XNameContainer >& rxStorage, const OUString& rElementName, bool bReadOnly ) :
189 StorageBase( rParentStorage, rElementName, bReadOnly ),
190 mxContext( rParentStorage.mxContext ),
191 mxStorage( rxStorage ),
192 mpParentStorage( &rParentStorage )
194 OSL_ENSURE( mxStorage.is(), "OleStorage::OleStorage - missing substorage elements" );
197 OleStorage::OleStorage( const OleStorage& rParentStorage,
198 const Reference< XStream >& rxOutStream, const OUString& rElementName ) :
199 StorageBase( rParentStorage, rElementName, false ),
200 mxContext( rParentStorage.mxContext ),
201 mpParentStorage( &rParentStorage )
203 initStorage( rxOutStream );
206 OleStorage::~OleStorage()
210 void OleStorage::initStorage( const Reference< XInputStream >& rxInStream )
212 // if stream is not seekable, create temporary copy
213 Reference< XInputStream > xInStrm = rxInStream;
214 if( !Reference< XSeekable >( xInStrm, UNO_QUERY ).is() ) try
216 Reference< XStream > xTempFile( TempFile::create(mxContext), UNO_QUERY_THROW );
218 Reference< XOutputStream > xOutStrm( xTempFile->getOutputStream(), UNO_SET_THROW );
219 /* Pass false to both binary stream objects to keep the UNO
220 streams alive. Life time of these streams is controlled by the
221 tempfile implementation. */
222 BinaryXOutputStream aOutStrm( xOutStrm, false );
223 BinaryXInputStream aInStrm( xInStrm, false );
224 aInStrm.copyToStream( aOutStrm );
225 } // scope closes output stream of tempfile
226 xInStrm = xTempFile->getInputStream();
228 catch(const Exception& )
230 OSL_FAIL( "OleStorage::initStorage - cannot create temporary copy of input stream" );
233 // create base storage object
234 if( xInStrm.is() ) try
236 Reference< XMultiServiceFactory > xFactory( mxContext->getServiceManager(), UNO_QUERY_THROW );
237 Sequence< Any > aArgs( 2 );
238 aArgs[ 0 ] <<= xInStrm;
239 aArgs[ 1 ] <<= true; // true = do not create a copy of the input stream
240 mxStorage.set( xFactory->createInstanceWithArguments("com.sun.star.embed.OLESimpleStorage", aArgs ), UNO_QUERY_THROW );
242 catch(const Exception& )
247 void OleStorage::initStorage( const Reference< XStream >& rxOutStream )
249 // create base storage object
250 if( rxOutStream.is() ) try
252 Reference< XMultiServiceFactory > xFactory( mxContext->getServiceManager(), UNO_QUERY_THROW );
253 Sequence< Any > aArgs( 2 );
254 aArgs[ 0 ] <<= rxOutStream;
255 aArgs[ 1 ] <<= true; // true = do not create a copy of the stream
256 mxStorage.set( xFactory->createInstanceWithArguments("com.sun.star.embed.OLESimpleStorage", aArgs ), UNO_QUERY_THROW );
258 catch(const Exception& )
263 // StorageBase interface ------------------------------------------------------
265 bool OleStorage::implIsStorage() const
267 if( mxStorage.is() ) try
269 /* If this is not an OLE storage, hasElements() of the OLESimpleStorage
270 implementation throws an exception. But we do not return the result
271 of hasElements(), because an empty storage is a valid storage too. */
272 (void)mxStorage->hasElements();
273 return true;
275 catch(const Exception& )
278 return false;
281 Reference< XStorage > OleStorage::implGetXStorage() const
283 OSL_FAIL( "OleStorage::getXStorage - not implemented" );
284 return Reference< XStorage >();
287 void OleStorage::implGetElementNames( ::std::vector< OUString >& orElementNames ) const
289 Sequence< OUString > aNames;
290 if( mxStorage.is() ) try
292 aNames = mxStorage->getElementNames();
293 if( aNames.getLength() > 0 )
294 orElementNames.insert( orElementNames.end(), aNames.getConstArray(), aNames.getConstArray() + aNames.getLength() );
296 catch(const Exception& )
301 StorageRef OleStorage::implOpenSubStorage( const OUString& rElementName, bool bCreateMissing )
303 StorageRef xSubStorage;
304 if( mxStorage.is() && !rElementName.isEmpty() )
308 Reference< XNameContainer > xSubElements( mxStorage->getByName( rElementName ), UNO_QUERY_THROW );
309 xSubStorage.reset( new OleStorage( *this, xSubElements, rElementName, true ) );
311 catch(const Exception& )
315 /* The OLESimpleStorage API implementation seems to be buggy in the
316 area of writable inplace substorage (sometimes it overwrites other
317 unrelated streams with zero bytes). We go the save way and create a
318 new OLE storage based on a temporary file. All operations are
319 performed on this clean storage. On committing, the storage will be
320 completely re-inserted into the parent storage. */
321 if( !isReadOnly() && (bCreateMissing || xSubStorage.get()) ) try
323 // create new storage based on a temp file
324 Reference< XStream > xTempFile( TempFile::create(mxContext), UNO_QUERY_THROW );
325 StorageRef xTempStorage( new OleStorage( *this, xTempFile, rElementName ) );
326 // copy existing substorage into temp storage
327 if( xSubStorage.get() )
328 xSubStorage->copyStorageToStorage( *xTempStorage );
329 // return the temp storage to caller
330 xSubStorage = xTempStorage;
332 catch(const Exception& )
336 return xSubStorage;
339 Reference< XInputStream > OleStorage::implOpenInputStream( const OUString& rElementName )
341 Reference< XInputStream > xInStream;
342 if( mxStorage.is() ) try
344 xInStream.set( mxStorage->getByName( rElementName ), UNO_QUERY );
346 catch(const Exception& )
349 return xInStream;
352 Reference< XOutputStream > OleStorage::implOpenOutputStream( const OUString& rElementName )
354 Reference< XOutputStream > xOutStream;
355 if( mxStorage.is() && !rElementName.isEmpty() )
356 xOutStream.set( new OleOutputStream( mxContext, mxStorage, rElementName ) );
357 return xOutStream;
360 void OleStorage::implCommit() const
364 // commit this storage (finalizes the file this storage is based on)
365 Reference< XTransactedObject >( mxStorage, UNO_QUERY_THROW )->commit();
366 // re-insert this storage into the parent storage
367 if( mpParentStorage )
369 if( mpParentStorage->mxStorage->hasByName( getName() ) )
371 // replaceByName() does not work (#i109539#)
372 mpParentStorage->mxStorage->removeByName( getName() );
373 Reference< XTransactedObject >( mpParentStorage->mxStorage, UNO_QUERY_THROW )->commit();
375 mpParentStorage->mxStorage->insertByName( getName(), Any( mxStorage ) );
376 // this requires another commit(), which will be performed by the parent storage
379 catch(const Exception& )
384 } // namespace ole
385 } // namespace oox
387 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */