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
;
48 uno::Reference
< io::XTruncate
> xOrigTruncate
,
49 uno::Reference
< io::XSeekable
> xOrigSeekable
,
50 uno::Reference
< io::XInputStream
> xOrigInStream
,
51 uno::Reference
< io::XOutputStream
> xOrigOutStream
,
54 : m_bInStreamBased( bInStreamBased
)
55 , m_xOrigTruncate(std::move( xOrigTruncate
))
56 , m_xOrigSeekable(std::move( xOrigSeekable
))
57 , m_xOrigInStream(std::move( xOrigInStream
))
58 , m_xOrigOutStream(std::move( xOrigOutStream
))
59 , m_bInOpen( bInOpen
)
60 , m_bOutOpen( bOutOpen
)
65 SwitchablePersistenceStream::SwitchablePersistenceStream(
66 const uno::Reference
< io::XStream
>& xStream
)
68 SwitchPersistenceTo( xStream
);
71 SwitchablePersistenceStream::SwitchablePersistenceStream(
72 const uno::Reference
< io::XInputStream
>& xInputStream
)
74 SwitchPersistenceTo( xInputStream
);
77 SwitchablePersistenceStream::~SwitchablePersistenceStream()
82 void SwitchablePersistenceStream::SwitchPersistenceTo( const uno::Reference
< io::XStream
>& xStream
)
84 uno::Reference
< io::XTruncate
> xNewTruncate( xStream
, uno::UNO_QUERY_THROW
);
85 uno::Reference
< io::XSeekable
> xNewSeekable( xStream
, uno::UNO_QUERY_THROW
);
86 uno::Reference
< io::XInputStream
> xNewInStream
= xStream
->getInputStream();
87 uno::Reference
< io::XOutputStream
> xNewOutStream
= xStream
->getOutputStream();
88 if ( !xNewInStream
.is() || !xNewOutStream
.is() )
89 throw uno::RuntimeException();
93 bool bOutOpen
= false;
95 if ( m_pStreamData
&& m_pStreamData
->m_xOrigSeekable
.is() )
97 // check that the length is the same
98 if ( m_pStreamData
->m_xOrigSeekable
->getLength() != xNewSeekable
->getLength() )
99 throw uno::RuntimeException();
101 // get the current position
102 nPos
= m_pStreamData
->m_xOrigSeekable
->getPosition();
103 bInOpen
= m_pStreamData
->m_bInOpen
;
104 bOutOpen
= m_pStreamData
->m_bOutOpen
;
107 xNewSeekable
->seek( nPos
);
111 m_pStreamData
.reset( new SPStreamData_Impl( false,
112 xNewTruncate
, xNewSeekable
, xNewInStream
, xNewOutStream
,
113 bInOpen
, bOutOpen
) );
116 void SwitchablePersistenceStream::SwitchPersistenceTo( const uno::Reference
< io::XInputStream
>& xInputStream
)
118 uno::Reference
< io::XTruncate
> xNewTruncate
;
119 uno::Reference
< io::XSeekable
> xNewSeekable( xInputStream
, uno::UNO_QUERY_THROW
);
120 uno::Reference
< io::XOutputStream
> xNewOutStream
;
121 if ( !xInputStream
.is() )
122 throw uno::RuntimeException();
125 bool bInOpen
= false;
126 bool bOutOpen
= false;
128 if ( m_pStreamData
&& m_pStreamData
->m_xOrigSeekable
.is() )
130 // check that the length is the same
131 if ( m_pStreamData
->m_xOrigSeekable
->getLength() != xNewSeekable
->getLength() )
132 throw uno::RuntimeException();
134 // get the current position
135 nPos
= m_pStreamData
->m_xOrigSeekable
->getPosition();
136 bInOpen
= m_pStreamData
->m_bInOpen
;
137 bOutOpen
= m_pStreamData
->m_bOutOpen
;
140 xNewSeekable
->seek( nPos
);
144 m_pStreamData
.reset( new SPStreamData_Impl( true,
145 xNewTruncate
, xNewSeekable
, xInputStream
, xNewOutStream
,
146 bInOpen
, bOutOpen
) );
150 void SwitchablePersistenceStream::CopyAndSwitchPersistenceTo( const uno::Reference
< io::XStream
>& xStream
)
152 uno::Reference
< io::XStream
> xTargetStream
= xStream
;
153 uno::Reference
< io::XSeekable
> xTargetSeek
;
155 if ( !xTargetStream
.is() )
157 xTargetStream
.set( new utl::TempFileFastService
);
158 xTargetSeek
.set( xTargetStream
, uno::UNO_QUERY_THROW
);
162 // the provided stream must be empty
163 xTargetSeek
.set( xTargetStream
, uno::UNO_QUERY_THROW
);
164 if ( xTargetSeek
->getLength() )
165 throw io::IOException("provided stream not empty");
168 uno::Reference
< io::XTruncate
> xTargetTruncate( xTargetStream
, uno::UNO_QUERY_THROW
);
169 uno::Reference
< io::XInputStream
> xTargetInStream
= xTargetStream
->getInputStream();
170 uno::Reference
< io::XOutputStream
> xTargetOutStream
= xTargetStream
->getOutputStream();
171 if ( !xTargetInStream
.is() || !xTargetOutStream
.is() )
172 throw uno::RuntimeException();
174 if ( !m_pStreamData
->m_xOrigInStream
.is() || !m_pStreamData
->m_xOrigSeekable
.is() )
175 throw uno::RuntimeException();
177 sal_Int64 nPos
= m_pStreamData
->m_xOrigSeekable
->getPosition();
178 m_pStreamData
->m_xOrigSeekable
->seek( 0 );
179 ::comphelper::OStorageHelper::CopyInputToOutput( m_pStreamData
->m_xOrigInStream
, xTargetOutStream
);
180 xTargetOutStream
->flush();
181 xTargetSeek
->seek( nPos
);
183 bool bInOpen
= m_pStreamData
->m_bInOpen
;
184 bool bOutOpen
= m_pStreamData
->m_bOutOpen
;
188 m_pStreamData
.reset( new SPStreamData_Impl( false,
189 xTargetTruncate
, xTargetSeek
, xTargetInStream
, xTargetOutStream
,
190 bInOpen
, bOutOpen
) );
193 void SwitchablePersistenceStream::CloseAll_Impl()
195 m_pStreamData
.reset();
199 uno::Reference
< io::XInputStream
> SAL_CALL
SwitchablePersistenceStream::getInputStream( )
201 std::scoped_lock
aGuard( m_aMutex
);
204 m_pStreamData
->m_bInOpen
= true;
205 return static_cast< io::XInputStream
* >( this );
208 uno::Reference
< io::XOutputStream
> SAL_CALL
SwitchablePersistenceStream::getOutputStream( )
210 std::scoped_lock
aGuard( m_aMutex
);
213 m_pStreamData
->m_bOutOpen
= true;
214 return static_cast< io::XOutputStream
* >( this );
217 // css::io::XInputStream
218 ::sal_Int32 SAL_CALL
SwitchablePersistenceStream::readBytes( uno::Sequence
< ::sal_Int8
>& aData
, ::sal_Int32 nBytesToRead
)
220 std::scoped_lock
aGuard( m_aMutex
);
222 if ( !m_pStreamData
)
223 throw io::NotConnectedException();
225 // the original stream data should be provided
226 if ( !m_pStreamData
->m_xOrigInStream
.is() )
227 throw uno::RuntimeException();
229 return m_pStreamData
->m_xOrigInStream
->readBytes( aData
, nBytesToRead
);
232 ::sal_Int32 SAL_CALL
SwitchablePersistenceStream::readSomeBytes( uno::Sequence
< ::sal_Int8
>& aData
, ::sal_Int32 nMaxBytesToRead
)
234 std::scoped_lock
aGuard( m_aMutex
);
236 if ( !m_pStreamData
)
237 throw io::NotConnectedException();
239 // the original stream data should be provided
240 if ( !m_pStreamData
->m_xOrigInStream
.is() )
241 throw uno::RuntimeException();
243 return m_pStreamData
->m_xOrigInStream
->readBytes( aData
, nMaxBytesToRead
);
246 void SAL_CALL
SwitchablePersistenceStream::skipBytes( ::sal_Int32 nBytesToSkip
)
248 std::scoped_lock
aGuard( m_aMutex
);
250 if ( !m_pStreamData
)
251 throw io::NotConnectedException();
253 // the original stream data should be provided
254 if ( !m_pStreamData
->m_xOrigInStream
.is() )
255 throw uno::RuntimeException();
257 m_pStreamData
->m_xOrigInStream
->skipBytes( nBytesToSkip
);
260 ::sal_Int32 SAL_CALL
SwitchablePersistenceStream::available( )
262 std::scoped_lock
aGuard( m_aMutex
);
264 if ( !m_pStreamData
)
265 throw io::NotConnectedException();
267 // the original stream data should be provided
268 if ( !m_pStreamData
->m_xOrigInStream
.is() )
269 throw uno::RuntimeException();
271 return m_pStreamData
->m_xOrigInStream
->available();
274 void SAL_CALL
SwitchablePersistenceStream::closeInput()
276 std::scoped_lock
aGuard( m_aMutex
);
278 if ( !m_pStreamData
)
279 throw io::NotConnectedException();
281 m_pStreamData
->m_bInOpen
= false;
282 if ( !m_pStreamData
->m_bOutOpen
)
286 // css::io::XOutputStream
287 void SAL_CALL
SwitchablePersistenceStream::writeBytes( const uno::Sequence
< ::sal_Int8
>& aData
)
289 std::scoped_lock
aGuard( m_aMutex
);
291 if ( !m_pStreamData
)
292 throw io::NotConnectedException();
294 if ( m_pStreamData
->m_bInStreamBased
)
295 throw io::IOException();
297 // the original stream data should be provided
298 if ( !m_pStreamData
->m_xOrigOutStream
.is() )
299 throw uno::RuntimeException();
301 m_pStreamData
->m_xOrigOutStream
->writeBytes( aData
);
304 void SAL_CALL
SwitchablePersistenceStream::flush( )
306 std::scoped_lock
aGuard( m_aMutex
);
308 if ( !m_pStreamData
|| m_pStreamData
->m_bInStreamBased
)
310 OSL_FAIL( "flush() is not acceptable!" );
312 // in future throw exception, for now some code might call flush() on closed stream
313 // since file ucp implementation allows it
314 // throw io::NotConnectedException();
317 // the original stream data should be provided
318 if ( !m_pStreamData
->m_xOrigOutStream
.is() )
319 throw uno::RuntimeException();
321 m_pStreamData
->m_xOrigOutStream
->flush();
324 void SAL_CALL
SwitchablePersistenceStream::closeOutput( )
326 std::scoped_lock
aGuard( m_aMutex
);
328 if ( !m_pStreamData
)
329 throw io::NotConnectedException();
331 m_pStreamData
->m_bOutOpen
= false;
332 if ( !m_pStreamData
->m_bInOpen
)
336 // css::io::XTruncate
337 void SAL_CALL
SwitchablePersistenceStream::truncate( )
339 std::scoped_lock
aGuard( m_aMutex
);
341 if ( !m_pStreamData
)
342 throw io::NotConnectedException();
344 if ( m_pStreamData
->m_bInStreamBased
)
345 throw io::IOException();
347 // the original stream data should be provided
348 if ( !m_pStreamData
->m_xOrigTruncate
.is() )
349 throw uno::RuntimeException();
351 m_pStreamData
->m_xOrigTruncate
->truncate();
354 // css::io::XSeekable
355 void SAL_CALL
SwitchablePersistenceStream::seek( ::sal_Int64 location
)
357 std::scoped_lock
aGuard( m_aMutex
);
359 if ( !m_pStreamData
)
360 throw io::NotConnectedException();
362 // the original stream data should be provided
363 if ( !m_pStreamData
->m_xOrigSeekable
.is() )
364 throw uno::RuntimeException();
366 m_pStreamData
->m_xOrigSeekable
->seek( location
);
369 ::sal_Int64 SAL_CALL
SwitchablePersistenceStream::getPosition( )
371 std::scoped_lock
aGuard( m_aMutex
);
373 if ( !m_pStreamData
)
374 throw io::NotConnectedException();
376 // the original stream data should be provided
377 if ( !m_pStreamData
->m_xOrigSeekable
.is() )
378 throw uno::RuntimeException();
380 return m_pStreamData
->m_xOrigSeekable
->getPosition();
383 ::sal_Int64 SAL_CALL
SwitchablePersistenceStream::getLength( )
385 std::scoped_lock
aGuard( m_aMutex
);
387 if ( !m_pStreamData
)
388 throw io::NotConnectedException();
390 // the original stream data should be provided
391 if ( !m_pStreamData
->m_xOrigSeekable
.is() )
392 throw uno::RuntimeException();
394 return m_pStreamData
->m_xOrigSeekable
->getLength();
397 void SAL_CALL
SwitchablePersistenceStream::waitForCompletion()
399 if ( !m_pStreamData
)
400 throw io::NotConnectedException();
402 uno::Reference
< io::XAsyncOutputMonitor
> asyncOutputMonitor( m_pStreamData
->m_xOrigOutStream
, uno::UNO_QUERY
);
403 if ( asyncOutputMonitor
.is() )
404 asyncOutputMonitor
->waitForCompletion();
407 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */