1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 "oox/helper/binaryinputstream.hxx"
34 #include "oox/helper/binaryoutputstream.hxx"
35 #include "oox/helper/containerhelper.hxx"
36 #include "oox/helper/helper.hxx"
41 // ============================================================================
43 using namespace ::com::sun::star::beans
;
44 using namespace ::com::sun::star::container
;
45 using namespace ::com::sun::star::embed
;
46 using namespace ::com::sun::star::io
;
47 using namespace ::com::sun::star::lang
;
48 using namespace ::com::sun::star::uno
;
50 // ============================================================================
54 typedef ::cppu::WeakImplHelper2
< XSeekable
, XOutputStream
> OleOutputStreamBase
;
56 /** Implementation of an OLE storage output stream that inserts itself into the
57 storage when it is closed.
59 class OleOutputStream
: public OleOutputStreamBase
62 explicit OleOutputStream(
63 const Reference
< XComponentContext
>& rxContext
,
64 const Reference
< XNameContainer
>& rxStorage
,
65 const OUString
& rElementName
);
66 virtual ~OleOutputStream();
68 virtual void SAL_CALL
seek( sal_Int64 nPos
) throw( IllegalArgumentException
, IOException
, RuntimeException
);
69 virtual sal_Int64 SAL_CALL
getPosition() throw( IOException
, RuntimeException
);
70 virtual sal_Int64 SAL_CALL
getLength() throw( IOException
, RuntimeException
);
72 virtual void SAL_CALL
writeBytes( const Sequence
< sal_Int8
>& rData
) throw( NotConnectedException
, BufferSizeExceededException
, IOException
, RuntimeException
);
73 virtual void SAL_CALL
flush() throw( NotConnectedException
, BufferSizeExceededException
, IOException
, RuntimeException
);
74 virtual void SAL_CALL
closeOutput() throw( NotConnectedException
, BufferSizeExceededException
, IOException
, RuntimeException
);
77 void ensureSeekable() const throw( IOException
);
78 void ensureConnected() const throw( NotConnectedException
);
81 Reference
< XNameContainer
> mxStorage
;
82 Reference
< XStream
> mxTempFile
;
83 Reference
< XOutputStream
> mxOutStrm
;
84 Reference
< XSeekable
> mxSeekable
;
85 OUString maElementName
;
88 // ----------------------------------------------------------------------------
90 OleOutputStream::OleOutputStream( const Reference
< XComponentContext
>& rxContext
,
91 const Reference
< XNameContainer
>& rxStorage
, const OUString
& rElementName
) :
92 mxStorage( rxStorage
),
93 maElementName( rElementName
)
97 mxTempFile
.set( TempFile::create(rxContext
), UNO_QUERY_THROW
);
98 mxOutStrm
= mxTempFile
->getOutputStream();
99 mxSeekable
.set( mxOutStrm
, UNO_QUERY
);
101 catch(const Exception
& )
106 OleOutputStream::~OleOutputStream()
110 void SAL_CALL
OleOutputStream::seek( sal_Int64 nPos
) throw( IllegalArgumentException
, IOException
, RuntimeException
)
113 mxSeekable
->seek( nPos
);
116 sal_Int64 SAL_CALL
OleOutputStream::getPosition() throw( IOException
, RuntimeException
)
119 return mxSeekable
->getPosition();
122 sal_Int64 SAL_CALL
OleOutputStream::getLength() throw( IOException
, RuntimeException
)
125 return mxSeekable
->getLength();
128 void SAL_CALL
OleOutputStream::writeBytes( const Sequence
< sal_Int8
>& rData
) throw( NotConnectedException
, BufferSizeExceededException
, IOException
, RuntimeException
)
131 mxOutStrm
->writeBytes( rData
);
134 void SAL_CALL
OleOutputStream::flush() throw( NotConnectedException
, BufferSizeExceededException
, IOException
, RuntimeException
)
140 void SAL_CALL
OleOutputStream::closeOutput() throw( NotConnectedException
, BufferSizeExceededException
, IOException
, RuntimeException
)
144 // remember the class members
145 Reference
< XOutputStream
> xOutStrm
= mxOutStrm
;
146 Reference
< XSeekable
> xSeekable
= mxSeekable
;
147 // reset all class members
150 // close stream (and let it throw something if needed)
151 xOutStrm
->closeOutput();
152 // on success, insert the stream into the OLE storage (must be seeked back before)
153 xSeekable
->seek( 0 );
154 if( !ContainerHelper::insertByName( mxStorage
, maElementName
, Any( mxTempFile
) ) )
158 void OleOutputStream::ensureSeekable() const throw( IOException
)
160 if( !mxSeekable
.is() )
164 void OleOutputStream::ensureConnected() const throw( NotConnectedException
)
166 if( !mxOutStrm
.is() )
167 throw NotConnectedException();
172 // ============================================================================
174 OleStorage::OleStorage( const Reference
< XComponentContext
>& rxContext
,
175 const Reference
< XInputStream
>& rxInStream
, bool bBaseStreamAccess
) :
176 StorageBase( rxInStream
, bBaseStreamAccess
),
177 mxContext( rxContext
),
180 OSL_ENSURE( mxContext
.is(), "OleStorage::OleStorage - missing component context" );
181 initStorage( rxInStream
);
184 OleStorage::OleStorage( const Reference
< XComponentContext
>& rxContext
,
185 const Reference
< XStream
>& rxOutStream
, bool bBaseStreamAccess
) :
186 StorageBase( rxOutStream
, bBaseStreamAccess
),
187 mxContext( rxContext
),
190 OSL_ENSURE( mxContext
.is(), "OleStorage::OleStorage - missing component context" );
191 initStorage( rxOutStream
);
194 OleStorage::OleStorage( const OleStorage
& rParentStorage
,
195 const Reference
< XNameContainer
>& rxStorage
, const OUString
& rElementName
, bool bReadOnly
) :
196 StorageBase( rParentStorage
, rElementName
, bReadOnly
),
197 mxContext( rParentStorage
.mxContext
),
198 mxStorage( rxStorage
),
199 mpParentStorage( &rParentStorage
)
201 OSL_ENSURE( mxStorage
.is(), "OleStorage::OleStorage - missing substorage elements" );
204 OleStorage::OleStorage( const OleStorage
& rParentStorage
,
205 const Reference
< XStream
>& rxOutStream
, const OUString
& rElementName
) :
206 StorageBase( rParentStorage
, rElementName
, false ),
207 mxContext( rParentStorage
.mxContext
),
208 mpParentStorage( &rParentStorage
)
210 initStorage( rxOutStream
);
213 OleStorage::~OleStorage()
217 // ----------------------------------------------------------------------------
219 void OleStorage::initStorage( const Reference
< XInputStream
>& rxInStream
)
221 // if stream is not seekable, create temporary copy
222 Reference
< XInputStream
> xInStrm
= rxInStream
;
223 if( !Reference
< XSeekable
>( xInStrm
, UNO_QUERY
).is() ) try
225 Reference
< XStream
> xTempFile( TempFile::create(mxContext
), UNO_QUERY_THROW
);
227 Reference
< XOutputStream
> xOutStrm( xTempFile
->getOutputStream(), UNO_SET_THROW
);
228 /* Pass false to both binary stream objects to keep the UNO
229 streams alive. Life time of these streams is controlled by the
230 tempfile implementation. */
231 BinaryXOutputStream
aOutStrm( xOutStrm
, false );
232 BinaryXInputStream
aInStrm( xInStrm
, false );
233 aInStrm
.copyToStream( aOutStrm
);
234 } // scope closes output stream of tempfile
235 xInStrm
= xTempFile
->getInputStream();
237 catch(const Exception
& )
239 OSL_FAIL( "OleStorage::initStorage - cannot create temporary copy of input stream" );
242 // create base storage object
243 if( xInStrm
.is() ) try
245 Reference
< XMultiServiceFactory
> xFactory( mxContext
->getServiceManager(), UNO_QUERY_THROW
);
246 Sequence
< Any
> aArgs( 2 );
247 aArgs
[ 0 ] <<= xInStrm
;
248 aArgs
[ 1 ] <<= true; // true = do not create a copy of the input stream
249 mxStorage
.set( xFactory
->createInstanceWithArguments("com.sun.star.embed.OLESimpleStorage", aArgs
), UNO_QUERY_THROW
);
251 catch(const Exception
& )
256 void OleStorage::initStorage( const Reference
< XStream
>& rxOutStream
)
258 // create base storage object
259 if( rxOutStream
.is() ) try
261 Reference
< XMultiServiceFactory
> xFactory( mxContext
->getServiceManager(), UNO_QUERY_THROW
);
262 Sequence
< Any
> aArgs( 2 );
263 aArgs
[ 0 ] <<= rxOutStream
;
264 aArgs
[ 1 ] <<= true; // true = do not create a copy of the stream
265 mxStorage
.set( xFactory
->createInstanceWithArguments("com.sun.star.embed.OLESimpleStorage", aArgs
), UNO_QUERY_THROW
);
267 catch(const Exception
& )
272 // StorageBase interface ------------------------------------------------------
274 bool OleStorage::implIsStorage() const
276 if( mxStorage
.is() ) try
278 /* If this is not an OLE storage, hasElements() of the OLESimpleStorage
279 implementation throws an exception. But we do not return the result
280 of hasElements(), because an empty storage is a valid storage too. */
281 mxStorage
->hasElements();
284 catch(const Exception
& )
290 Reference
< XStorage
> OleStorage::implGetXStorage() const
292 OSL_FAIL( "OleStorage::getXStorage - not implemented" );
293 return Reference
< XStorage
>();
296 void OleStorage::implGetElementNames( ::std::vector
< OUString
>& orElementNames
) const
298 Sequence
< OUString
> aNames
;
299 if( mxStorage
.is() ) try
301 aNames
= mxStorage
->getElementNames();
302 if( aNames
.getLength() > 0 )
303 orElementNames
.insert( orElementNames
.end(), aNames
.getConstArray(), aNames
.getConstArray() + aNames
.getLength() );
305 catch(const Exception
& )
310 StorageRef
OleStorage::implOpenSubStorage( const OUString
& rElementName
, bool bCreateMissing
)
312 StorageRef xSubStorage
;
313 if( mxStorage
.is() && !rElementName
.isEmpty() )
317 Reference
< XNameContainer
> xSubElements( mxStorage
->getByName( rElementName
), UNO_QUERY_THROW
);
318 xSubStorage
.reset( new OleStorage( *this, xSubElements
, rElementName
, true ) );
320 catch(const Exception
& )
324 /* The OLESimpleStorage API implementation seems to be buggy in the
325 area of writable inplace substorage (sometimes it overwrites other
326 unrelated streams with zero bytes). We go the save way and create a
327 new OLE storage based on a temporary file. All operations are
328 performed on this clean storage. On committing, the storage will be
329 completely re-inserted into the parent storage. */
330 if( !isReadOnly() && (bCreateMissing
|| xSubStorage
.get()) ) try
332 // create new storage based on a temp file
333 Reference
< XStream
> xTempFile( TempFile::create(mxContext
), UNO_QUERY_THROW
);
334 StorageRef
xTempStorage( new OleStorage( *this, xTempFile
, rElementName
) );
335 // copy existing substorage into temp storage
336 if( xSubStorage
.get() )
337 xSubStorage
->copyStorageToStorage( *xTempStorage
);
338 // return the temp storage to caller
339 xSubStorage
= xTempStorage
;
341 catch(const Exception
& )
348 Reference
< XInputStream
> OleStorage::implOpenInputStream( const OUString
& rElementName
)
350 Reference
< XInputStream
> xInStream
;
351 if( mxStorage
.is() ) try
353 xInStream
.set( mxStorage
->getByName( rElementName
), UNO_QUERY
);
355 catch(const Exception
& )
361 Reference
< XOutputStream
> OleStorage::implOpenOutputStream( const OUString
& rElementName
)
363 Reference
< XOutputStream
> xOutStream
;
364 if( mxStorage
.is() && !rElementName
.isEmpty() )
365 xOutStream
.set( new OleOutputStream( mxContext
, mxStorage
, rElementName
) );
369 void OleStorage::implCommit() const
373 // commit this storage (finalizes the file this storage is based on)
374 Reference
< XTransactedObject
>( mxStorage
, UNO_QUERY_THROW
)->commit();
375 // re-insert this storage into the parent storage
376 if( mpParentStorage
)
378 if( mpParentStorage
->mxStorage
->hasByName( getName() ) )
380 // replaceByName() does not work (#i109539#)
381 mpParentStorage
->mxStorage
->removeByName( getName() );
382 Reference
< XTransactedObject
>( mpParentStorage
->mxStorage
, UNO_QUERY_THROW
)->commit();
384 mpParentStorage
->mxStorage
->insertByName( getName(), Any( mxStorage
) );
385 // this requires another commit(), which will be performed by the parent storage
388 catch(const Exception
& )
393 // ============================================================================
398 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */