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 <osl/diagnose.h>
21 #include <com/sun/star/io/IOException.hpp>
22 #include <com/sun/star/io/NotConnectedException.hpp>
23 #include <com/sun/star/io/TempFile.hpp>
24 #include <comphelper/storagehelper.hxx>
25 #include <unotools/tempfile.hxx>
27 #include "switchpersistencestream.hxx"
29 using namespace ::com::sun::star
;
31 struct SPStreamData_Impl
33 bool m_bInStreamBased
;
35 // the streams below are not visible from outside so there is no need to remember position
37 // original stream related members
38 uno::Reference
< io::XTruncate
> m_xOrigTruncate
;
39 uno::Reference
< io::XSeekable
> m_xOrigSeekable
;
40 uno::Reference
< io::XInputStream
> m_xOrigInStream
;
41 uno::Reference
< io::XOutputStream
> m_xOrigOutStream
;
42 comphelper::ByteReader
* m_pByteReader
;
49 uno::Reference
< io::XTruncate
> xOrigTruncate
,
50 uno::Reference
< io::XSeekable
> xOrigSeekable
,
51 uno::Reference
< io::XInputStream
> xOrigInStream
,
52 uno::Reference
< io::XOutputStream
> xOrigOutStream
,
55 : m_bInStreamBased( bInStreamBased
)
56 , m_xOrigTruncate(std::move( xOrigTruncate
))
57 , m_xOrigSeekable(std::move( xOrigSeekable
))
58 , m_xOrigInStream(std::move( xOrigInStream
))
59 , m_xOrigOutStream(std::move( xOrigOutStream
))
60 , m_pByteReader(dynamic_cast<comphelper::ByteReader
*>(m_xOrigInStream
.get()))
61 , m_bInOpen( bInOpen
)
62 , m_bOutOpen( bOutOpen
)
67 SwitchablePersistenceStream::SwitchablePersistenceStream(
68 const uno::Reference
< io::XStream
>& xStream
)
70 SwitchPersistenceTo( xStream
);
73 SwitchablePersistenceStream::SwitchablePersistenceStream(
74 const uno::Reference
< io::XInputStream
>& xInputStream
)
76 SwitchPersistenceTo( xInputStream
);
79 SwitchablePersistenceStream::~SwitchablePersistenceStream()
84 void SwitchablePersistenceStream::SwitchPersistenceTo( const uno::Reference
< io::XStream
>& xStream
)
86 uno::Reference
< io::XTruncate
> xNewTruncate( xStream
, uno::UNO_QUERY_THROW
);
87 uno::Reference
< io::XSeekable
> xNewSeekable( xStream
, uno::UNO_QUERY_THROW
);
88 uno::Reference
< io::XInputStream
> xNewInStream
= xStream
->getInputStream();
89 uno::Reference
< io::XOutputStream
> xNewOutStream
= xStream
->getOutputStream();
90 if ( !xNewInStream
.is() || !xNewOutStream
.is() )
91 throw uno::RuntimeException();
95 bool bOutOpen
= false;
97 if ( m_pStreamData
&& m_pStreamData
->m_xOrigSeekable
.is() )
99 // check that the length is the same
100 if ( m_pStreamData
->m_xOrigSeekable
->getLength() != xNewSeekable
->getLength() )
101 throw uno::RuntimeException();
103 // get the current position
104 nPos
= m_pStreamData
->m_xOrigSeekable
->getPosition();
105 bInOpen
= m_pStreamData
->m_bInOpen
;
106 bOutOpen
= m_pStreamData
->m_bOutOpen
;
109 xNewSeekable
->seek( nPos
);
113 m_pStreamData
.reset( new SPStreamData_Impl( false,
114 xNewTruncate
, xNewSeekable
, xNewInStream
, xNewOutStream
,
115 bInOpen
, bOutOpen
) );
118 void SwitchablePersistenceStream::SwitchPersistenceTo( const uno::Reference
< io::XInputStream
>& xInputStream
)
120 uno::Reference
< io::XTruncate
> xNewTruncate
;
121 uno::Reference
< io::XSeekable
> xNewSeekable( xInputStream
, uno::UNO_QUERY_THROW
);
122 uno::Reference
< io::XOutputStream
> xNewOutStream
;
123 if ( !xInputStream
.is() )
124 throw uno::RuntimeException();
127 bool bInOpen
= false;
128 bool bOutOpen
= false;
130 if ( m_pStreamData
&& m_pStreamData
->m_xOrigSeekable
.is() )
132 // check that the length is the same
133 if ( m_pStreamData
->m_xOrigSeekable
->getLength() != xNewSeekable
->getLength() )
134 throw uno::RuntimeException();
136 // get the current position
137 nPos
= m_pStreamData
->m_xOrigSeekable
->getPosition();
138 bInOpen
= m_pStreamData
->m_bInOpen
;
139 bOutOpen
= m_pStreamData
->m_bOutOpen
;
142 xNewSeekable
->seek( nPos
);
146 m_pStreamData
.reset( new SPStreamData_Impl( true,
147 xNewTruncate
, xNewSeekable
, xInputStream
, xNewOutStream
,
148 bInOpen
, bOutOpen
) );
152 void SwitchablePersistenceStream::CopyAndSwitchPersistenceTo( const uno::Reference
< io::XStream
>& xStream
)
154 uno::Reference
< io::XStream
> xTargetStream
= xStream
;
155 uno::Reference
< io::XSeekable
> xTargetSeek
;
157 if ( !xTargetStream
.is() )
159 xTargetStream
.set( new utl::TempFileFastService
);
160 xTargetSeek
.set( xTargetStream
, uno::UNO_QUERY_THROW
);
164 // the provided stream must be empty
165 xTargetSeek
.set( xTargetStream
, uno::UNO_QUERY_THROW
);
166 if ( xTargetSeek
->getLength() )
167 throw io::IOException(u
"provided stream not empty"_ustr
);
170 uno::Reference
< io::XTruncate
> xTargetTruncate( xTargetStream
, uno::UNO_QUERY_THROW
);
171 uno::Reference
< io::XInputStream
> xTargetInStream
= xTargetStream
->getInputStream();
172 uno::Reference
< io::XOutputStream
> xTargetOutStream
= xTargetStream
->getOutputStream();
173 if ( !xTargetInStream
.is() || !xTargetOutStream
.is() )
174 throw uno::RuntimeException();
176 if ( !m_pStreamData
->m_xOrigInStream
.is() || !m_pStreamData
->m_xOrigSeekable
.is() )
177 throw uno::RuntimeException();
179 sal_Int64 nPos
= m_pStreamData
->m_xOrigSeekable
->getPosition();
180 m_pStreamData
->m_xOrigSeekable
->seek( 0 );
181 ::comphelper::OStorageHelper::CopyInputToOutput( m_pStreamData
->m_xOrigInStream
, xTargetOutStream
);
182 xTargetOutStream
->flush();
183 xTargetSeek
->seek( nPos
);
185 bool bInOpen
= m_pStreamData
->m_bInOpen
;
186 bool bOutOpen
= m_pStreamData
->m_bOutOpen
;
190 m_pStreamData
.reset( new SPStreamData_Impl( false,
191 xTargetTruncate
, xTargetSeek
, xTargetInStream
, xTargetOutStream
,
192 bInOpen
, bOutOpen
) );
195 void SwitchablePersistenceStream::CloseAll_Impl()
197 m_pStreamData
.reset();
201 uno::Reference
< io::XInputStream
> SAL_CALL
SwitchablePersistenceStream::getInputStream( )
203 std::scoped_lock
aGuard( m_aMutex
);
206 m_pStreamData
->m_bInOpen
= true;
207 return static_cast< io::XInputStream
* >( this );
210 uno::Reference
< io::XOutputStream
> SAL_CALL
SwitchablePersistenceStream::getOutputStream( )
212 std::scoped_lock
aGuard( m_aMutex
);
215 m_pStreamData
->m_bOutOpen
= true;
216 return static_cast< io::XOutputStream
* >( this );
219 // css::io::XInputStream
220 ::sal_Int32 SAL_CALL
SwitchablePersistenceStream::readBytes( uno::Sequence
< ::sal_Int8
>& aData
, ::sal_Int32 nBytesToRead
)
222 std::scoped_lock
aGuard( m_aMutex
);
224 if ( !m_pStreamData
)
225 throw io::NotConnectedException();
227 // the original stream data should be provided
228 if ( !m_pStreamData
->m_xOrigInStream
.is() )
229 throw uno::RuntimeException();
231 return m_pStreamData
->m_xOrigInStream
->readBytes( aData
, nBytesToRead
);
234 ::sal_Int32 SAL_CALL
SwitchablePersistenceStream::readSomeBytes( uno::Sequence
< ::sal_Int8
>& aData
, ::sal_Int32 nMaxBytesToRead
)
236 std::scoped_lock
aGuard( m_aMutex
);
238 if ( !m_pStreamData
)
239 throw io::NotConnectedException();
241 // the original stream data should be provided
242 if ( !m_pStreamData
->m_xOrigInStream
.is() )
243 throw uno::RuntimeException();
245 return m_pStreamData
->m_xOrigInStream
->readBytes( aData
, nMaxBytesToRead
);
248 ::sal_Int32
SwitchablePersistenceStream::readSomeBytes( sal_Int8
* aData
, sal_Int32 nBytesToRead
)
250 std::scoped_lock
aGuard( m_aMutex
);
252 if ( !m_pStreamData
)
253 throw io::NotConnectedException();
255 // the original stream data should be provided
256 if ( !m_pStreamData
->m_xOrigInStream
.is() )
257 throw uno::RuntimeException();
259 return m_pStreamData
->m_pByteReader
->readSomeBytes( aData
, nBytesToRead
);
262 void SAL_CALL
SwitchablePersistenceStream::skipBytes( ::sal_Int32 nBytesToSkip
)
264 std::scoped_lock
aGuard( m_aMutex
);
266 if ( !m_pStreamData
)
267 throw io::NotConnectedException();
269 // the original stream data should be provided
270 if ( !m_pStreamData
->m_xOrigInStream
.is() )
271 throw uno::RuntimeException();
273 m_pStreamData
->m_xOrigInStream
->skipBytes( nBytesToSkip
);
276 ::sal_Int32 SAL_CALL
SwitchablePersistenceStream::available( )
278 std::scoped_lock
aGuard( m_aMutex
);
280 if ( !m_pStreamData
)
281 throw io::NotConnectedException();
283 // the original stream data should be provided
284 if ( !m_pStreamData
->m_xOrigInStream
.is() )
285 throw uno::RuntimeException();
287 return m_pStreamData
->m_xOrigInStream
->available();
290 void SAL_CALL
SwitchablePersistenceStream::closeInput()
292 std::scoped_lock
aGuard( m_aMutex
);
294 if ( !m_pStreamData
)
295 throw io::NotConnectedException();
297 m_pStreamData
->m_bInOpen
= false;
298 if ( !m_pStreamData
->m_bOutOpen
)
302 // css::io::XOutputStream
303 void SAL_CALL
SwitchablePersistenceStream::writeBytes( const uno::Sequence
< ::sal_Int8
>& aData
)
305 std::scoped_lock
aGuard( m_aMutex
);
307 if ( !m_pStreamData
)
308 throw io::NotConnectedException();
310 if ( m_pStreamData
->m_bInStreamBased
)
311 throw io::IOException();
313 // the original stream data should be provided
314 if ( !m_pStreamData
->m_xOrigOutStream
.is() )
315 throw uno::RuntimeException();
317 m_pStreamData
->m_xOrigOutStream
->writeBytes( aData
);
320 void SAL_CALL
SwitchablePersistenceStream::flush( )
322 std::scoped_lock
aGuard( m_aMutex
);
324 if ( !m_pStreamData
|| m_pStreamData
->m_bInStreamBased
)
326 OSL_FAIL( "flush() is not acceptable!" );
328 // in future throw exception, for now some code might call flush() on closed stream
329 // since file ucp implementation allows it
330 // throw io::NotConnectedException();
333 // the original stream data should be provided
334 if ( !m_pStreamData
->m_xOrigOutStream
.is() )
335 throw uno::RuntimeException();
337 m_pStreamData
->m_xOrigOutStream
->flush();
340 void SAL_CALL
SwitchablePersistenceStream::closeOutput( )
342 std::scoped_lock
aGuard( m_aMutex
);
344 if ( !m_pStreamData
)
345 throw io::NotConnectedException();
347 m_pStreamData
->m_bOutOpen
= false;
348 if ( !m_pStreamData
->m_bInOpen
)
352 // css::io::XTruncate
353 void SAL_CALL
SwitchablePersistenceStream::truncate( )
355 std::scoped_lock
aGuard( m_aMutex
);
357 if ( !m_pStreamData
)
358 throw io::NotConnectedException();
360 if ( m_pStreamData
->m_bInStreamBased
)
361 throw io::IOException();
363 // the original stream data should be provided
364 if ( !m_pStreamData
->m_xOrigTruncate
.is() )
365 throw uno::RuntimeException();
367 m_pStreamData
->m_xOrigTruncate
->truncate();
370 // css::io::XSeekable
371 void SAL_CALL
SwitchablePersistenceStream::seek( ::sal_Int64 location
)
373 std::scoped_lock
aGuard( m_aMutex
);
375 if ( !m_pStreamData
)
376 throw io::NotConnectedException();
378 // the original stream data should be provided
379 if ( !m_pStreamData
->m_xOrigSeekable
.is() )
380 throw uno::RuntimeException();
382 m_pStreamData
->m_xOrigSeekable
->seek( location
);
385 ::sal_Int64 SAL_CALL
SwitchablePersistenceStream::getPosition( )
387 std::scoped_lock
aGuard( m_aMutex
);
389 if ( !m_pStreamData
)
390 throw io::NotConnectedException();
392 // the original stream data should be provided
393 if ( !m_pStreamData
->m_xOrigSeekable
.is() )
394 throw uno::RuntimeException();
396 return m_pStreamData
->m_xOrigSeekable
->getPosition();
399 ::sal_Int64 SAL_CALL
SwitchablePersistenceStream::getLength( )
401 std::scoped_lock
aGuard( m_aMutex
);
403 if ( !m_pStreamData
)
404 throw io::NotConnectedException();
406 // the original stream data should be provided
407 if ( !m_pStreamData
->m_xOrigSeekable
.is() )
408 throw uno::RuntimeException();
410 return m_pStreamData
->m_xOrigSeekable
->getLength();
413 void SAL_CALL
SwitchablePersistenceStream::waitForCompletion()
415 if ( !m_pStreamData
)
416 throw io::NotConnectedException();
418 uno::Reference
< io::XAsyncOutputMonitor
> asyncOutputMonitor( m_pStreamData
->m_xOrigOutStream
, uno::UNO_QUERY
);
419 if ( asyncOutputMonitor
.is() )
420 asyncOutputMonitor
->waitForCompletion();
423 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */