1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_package.hxx"
30 #include <osl/diagnose.h>
31 
32 #include <comphelper/storagehelper.hxx>
33 #include <switchpersistencestream.hxx>
34 
35 using namespace ::com::sun::star;
36 
37 // ========================================================================
38 struct SPStreamData_Impl
39 {
40 	uno::Reference< lang::XMultiServiceFactory > m_xFactory;
41 
42 	sal_Bool m_bInStreamBased;
43 
44 	// the streams below are not visible from outside so there is no need to remember position
45 
46 	// original stream related members
47 	uno::Reference< io::XStream > m_xOrigStream;
48 	uno::Reference< io::XTruncate > m_xOrigTruncate;
49 	uno::Reference< io::XSeekable > m_xOrigSeekable;
50 	uno::Reference< io::XInputStream > m_xOrigInStream;
51 	uno::Reference< io::XOutputStream > m_xOrigOutStream;
52 
53 	sal_Bool m_bInOpen;
54 	sal_Bool m_bOutOpen;
55 
56 
57 	SPStreamData_Impl(
58 			const uno::Reference< lang::XMultiServiceFactory >& xFactory,
59 			sal_Bool bInStreamBased,
60 			const uno::Reference< io::XStream >& xOrigStream,
61 			const uno::Reference< io::XTruncate >& xOrigTruncate,
62 			const uno::Reference< io::XSeekable >& xOrigSeekable,
63 			const uno::Reference< io::XInputStream >& xOrigInStream,
64 			const uno::Reference< io::XOutputStream >& xOrigOutStream,
65 			sal_Bool bInOpen,
66 			sal_Bool bOutOpen )
67 	: m_xFactory( xFactory )
68 	, m_bInStreamBased( bInStreamBased )
69 	, m_xOrigStream( xOrigStream )
70 	, m_xOrigTruncate( xOrigTruncate )
71 	, m_xOrigSeekable( xOrigSeekable )
72 	, m_xOrigInStream( xOrigInStream )
73 	, m_xOrigOutStream( xOrigOutStream )
74 	, m_bInOpen( bInOpen )
75 	, m_bOutOpen( bOutOpen )
76 	{
77 	}
78 };
79 
80 // ========================================================================
81 // ------------------------------------------------------------------------
82 SwitchablePersistenceStream::SwitchablePersistenceStream(
83 		const uno::Reference< lang::XMultiServiceFactory >& xFactory,
84 		const uno::Reference< io::XStream >& xStream )
85 : m_xFactory( xFactory )
86 , m_pStreamData( NULL )
87 {
88 	SwitchPersistenceTo( xStream );
89 }
90 
91 // ------------------------------------------------------------------------
92 SwitchablePersistenceStream::SwitchablePersistenceStream(
93 		const uno::Reference< lang::XMultiServiceFactory >& xFactory,
94 		const uno::Reference< io::XInputStream >& xInputStream )
95 : m_xFactory( xFactory )
96 , m_pStreamData( NULL )
97 {
98 	SwitchPersistenceTo( xInputStream );
99 }
100 
101 // ------------------------------------------------------------------------
102 SwitchablePersistenceStream::~SwitchablePersistenceStream()
103 {
104 	CloseAll_Impl();
105 }
106 
107 // ------------------------------------------------------------------------
108 void SwitchablePersistenceStream::SwitchPersistenceTo( const uno::Reference< io::XStream >& xStream )
109 {
110 	uno::Reference< io::XTruncate > xNewTruncate( xStream, uno::UNO_QUERY_THROW );
111 	uno::Reference< io::XSeekable > xNewSeekable( xStream, uno::UNO_QUERY_THROW );
112 	uno::Reference< io::XInputStream > xNewInStream = xStream->getInputStream();
113 	uno::Reference< io::XOutputStream > xNewOutStream = xStream->getOutputStream();
114 	if ( !xNewInStream.is() || !xNewOutStream.is() )
115 		throw uno::RuntimeException();
116 
117 	sal_Int64 nPos = 0;
118 	sal_Bool bInOpen = sal_False;
119 	sal_Bool bOutOpen = sal_False;
120 
121 	if ( m_pStreamData && m_pStreamData->m_xOrigSeekable.is() )
122 	{
123 		// check that the length is the same
124 		if ( m_pStreamData->m_xOrigSeekable->getLength() != xNewSeekable->getLength() )
125 			throw uno::RuntimeException();
126 
127 		// get the current position
128 		nPos = m_pStreamData->m_xOrigSeekable->getPosition();
129 		bInOpen = m_pStreamData->m_bInOpen;
130 		bOutOpen = m_pStreamData->m_bOutOpen;
131 	}
132 
133 	xNewSeekable->seek( nPos );
134 
135 	CloseAll_Impl();
136 
137 	m_pStreamData = new SPStreamData_Impl( m_xFactory, sal_False,
138 											xStream, xNewTruncate, xNewSeekable, xNewInStream, xNewOutStream,
139 											bInOpen, bOutOpen );
140 }
141 
142 // ------------------------------------------------------------------------
143 void SwitchablePersistenceStream::SwitchPersistenceTo( const uno::Reference< io::XInputStream >& xInputStream )
144 {
145 	uno::Reference< io::XStream > xNewStream;
146 	uno::Reference< io::XTruncate > xNewTruncate;
147 	uno::Reference< io::XSeekable > xNewSeekable( xInputStream, uno::UNO_QUERY_THROW );
148 	uno::Reference< io::XOutputStream > xNewOutStream;
149 	if ( !xInputStream.is() )
150 		throw uno::RuntimeException();
151 
152 	sal_Int64 nPos = 0;
153 	sal_Bool bInOpen = sal_False;
154 	sal_Bool bOutOpen = sal_False;
155 
156 	if ( m_pStreamData && m_pStreamData->m_xOrigSeekable.is() )
157 	{
158 		// check that the length is the same
159 		if ( m_pStreamData->m_xOrigSeekable->getLength() != xNewSeekable->getLength() )
160 			throw uno::RuntimeException();
161 
162 		// get the current position
163 		nPos = m_pStreamData->m_xOrigSeekable->getPosition();
164 		bInOpen = m_pStreamData->m_bInOpen;
165 		bOutOpen = m_pStreamData->m_bOutOpen;
166 	}
167 
168 	xNewSeekable->seek( nPos );
169 
170 	CloseAll_Impl();
171 
172 	m_pStreamData = new SPStreamData_Impl( m_xFactory, sal_True,
173 											xNewStream, xNewTruncate, xNewSeekable, xInputStream, xNewOutStream,
174 											bInOpen, bOutOpen );
175 
176 }
177 
178 // ------------------------------------------------------------------------
179 void SwitchablePersistenceStream::CopyAndSwitchPersistenceTo( const uno::Reference< io::XStream >& xStream )
180 {
181 	uno::Reference< io::XStream > xTargetStream = xStream;
182 	uno::Reference< io::XSeekable > xTargetSeek;
183 
184 	if ( !xTargetStream.is() )
185 	{
186 		xTargetStream = uno::Reference < io::XStream >(
187 			m_xFactory->createInstance( ::rtl::OUString::createFromAscii( "com.sun.star.io.TempFile" ) ),
188 			uno::UNO_QUERY_THROW );
189 
190 		xTargetSeek = uno::Reference< io::XSeekable >( xTargetStream, uno::UNO_QUERY_THROW );
191 	}
192 	else
193 	{
194 		// the provided stream must be empty
195 		xTargetSeek = uno::Reference< io::XSeekable >( xTargetStream, uno::UNO_QUERY_THROW );
196 		if ( xTargetSeek->getLength() )
197 			throw io::IOException();
198 	}
199 
200 	uno::Reference< io::XTruncate > xTargetTruncate( xTargetStream, uno::UNO_QUERY_THROW );
201 	uno::Reference< io::XInputStream > xTargetInStream = xTargetStream->getInputStream();
202 	uno::Reference< io::XOutputStream > xTargetOutStream = xTargetStream->getOutputStream();
203 	if ( !xTargetInStream.is() || !xTargetOutStream.is() )
204 		throw uno::RuntimeException();
205 
206 	if ( !m_pStreamData->m_xOrigInStream.is() || !m_pStreamData->m_xOrigSeekable.is() )
207 		throw uno::RuntimeException();
208 
209 	sal_Int64 nPos = m_pStreamData->m_xOrigSeekable->getPosition();
210 	m_pStreamData->m_xOrigSeekable->seek( 0 );
211 	::comphelper::OStorageHelper::CopyInputToOutput( m_pStreamData->m_xOrigInStream, xTargetOutStream );
212 	xTargetOutStream->flush();
213 	xTargetSeek->seek( nPos );
214 
215 	sal_Bool bInOpen = m_pStreamData->m_bInOpen;
216 	sal_Bool bOutOpen = m_pStreamData->m_bOutOpen;
217 
218 	CloseAll_Impl();
219 
220 	m_pStreamData = new SPStreamData_Impl( m_xFactory, sal_False,
221 										xTargetStream, xTargetTruncate, xTargetSeek, xTargetInStream, xTargetOutStream,
222 										bInOpen, bOutOpen );
223 }
224 
225 // ------------------------------------------------------------------------
226 void SwitchablePersistenceStream::CloseAll_Impl()
227 {
228 	if ( m_pStreamData )
229 	{
230 		delete m_pStreamData;
231 		m_pStreamData = NULL;
232 	}
233 }
234 
235 // com::sun::star::io::XStream
236 // ------------------------------------------------------------------------
237 uno::Reference< io::XInputStream > SAL_CALL SwitchablePersistenceStream::getInputStream(  )
238 	throw (uno::RuntimeException)
239 {
240 	::osl::MutexGuard aGuard( m_aMutex );
241 
242     if ( m_pStreamData )
243         m_pStreamData->m_bInOpen = sal_True;
244 	return static_cast< io::XInputStream* >( this );
245 }
246 
247 
248 // ------------------------------------------------------------------------
249 uno::Reference< io::XOutputStream > SAL_CALL SwitchablePersistenceStream::getOutputStream(  )
250 	throw (uno::RuntimeException)
251 {
252 	::osl::MutexGuard aGuard( m_aMutex );
253 
254     if ( m_pStreamData )
255         m_pStreamData->m_bOutOpen = sal_True;
256 	return static_cast< io::XOutputStream* >( this );
257 }
258 
259 
260 
261 // com::sun::star::io::XInputStream
262 // ------------------------------------------------------------------------
263 ::sal_Int32 SAL_CALL SwitchablePersistenceStream::readBytes( uno::Sequence< ::sal_Int8 >& aData, ::sal_Int32 nBytesToRead )
264 	throw (io::NotConnectedException, io::BufferSizeExceededException, io::IOException, uno::RuntimeException)
265 {
266 	::osl::MutexGuard aGuard( m_aMutex );
267 
268 	if ( !m_pStreamData )
269 		throw io::NotConnectedException();
270 
271 	// the original stream data should be provided
272 	if ( !m_pStreamData->m_xOrigInStream.is() )
273 		throw uno::RuntimeException();
274 
275 	return m_pStreamData->m_xOrigInStream->readBytes( aData, nBytesToRead );
276 }
277 
278 
279 // ------------------------------------------------------------------------
280 ::sal_Int32 SAL_CALL SwitchablePersistenceStream::readSomeBytes( uno::Sequence< ::sal_Int8 >& aData, ::sal_Int32 nMaxBytesToRead )
281 	throw (io::NotConnectedException, io::BufferSizeExceededException, io::IOException, uno::RuntimeException)
282 {
283 	::osl::MutexGuard aGuard( m_aMutex );
284 
285 	if ( !m_pStreamData )
286 		throw io::NotConnectedException();
287 
288 	// the original stream data should be provided
289 	if ( !m_pStreamData->m_xOrigInStream.is() )
290 		throw uno::RuntimeException();
291 
292 	return m_pStreamData->m_xOrigInStream->readBytes( aData, nMaxBytesToRead );
293 }
294 
295 // ------------------------------------------------------------------------
296 void SAL_CALL SwitchablePersistenceStream::skipBytes( ::sal_Int32 nBytesToSkip )
297 	throw (io::NotConnectedException, io::BufferSizeExceededException, io::IOException, uno::RuntimeException)
298 {
299 	::osl::MutexGuard aGuard( m_aMutex );
300 
301 	if ( !m_pStreamData )
302 		throw io::NotConnectedException();
303 
304 	// the original stream data should be provided
305 	if ( !m_pStreamData->m_xOrigInStream.is() )
306 		throw uno::RuntimeException();
307 
308 	m_pStreamData->m_xOrigInStream->skipBytes( nBytesToSkip );
309 }
310 
311 
312 // ------------------------------------------------------------------------
313 ::sal_Int32 SAL_CALL SwitchablePersistenceStream::available(  )
314 	throw (io::NotConnectedException, io::IOException, uno::RuntimeException)
315 {
316 	::osl::MutexGuard aGuard( m_aMutex );
317 
318 	if ( !m_pStreamData )
319 		throw io::NotConnectedException();
320 
321 	// the original stream data should be provided
322 	if ( !m_pStreamData->m_xOrigInStream.is() )
323 		throw uno::RuntimeException();
324 
325 	return m_pStreamData->m_xOrigInStream->available();
326 }
327 
328 
329 // ------------------------------------------------------------------------
330 void SAL_CALL SwitchablePersistenceStream::closeInput()
331 	throw (io::NotConnectedException, io::IOException, uno::RuntimeException)
332 {
333 	::osl::MutexGuard aGuard( m_aMutex );
334 
335 	if ( !m_pStreamData )
336 		throw io::NotConnectedException();
337 
338 	m_pStreamData->m_bInOpen = sal_False;
339 	if ( !m_pStreamData->m_bOutOpen )
340 		CloseAll_Impl();
341 }
342 
343 
344 
345 // com::sun::star::io::XOutputStream
346 // ------------------------------------------------------------------------
347 void SAL_CALL SwitchablePersistenceStream::writeBytes( const uno::Sequence< ::sal_Int8 >& aData )
348 	throw (io::NotConnectedException, io::BufferSizeExceededException, io::IOException, uno::RuntimeException)
349 {
350 	::osl::MutexGuard aGuard( m_aMutex );
351 
352 	if ( !m_pStreamData )
353 		throw io::NotConnectedException();
354 
355 	if ( m_pStreamData->m_bInStreamBased )
356 		throw io::IOException();
357 
358 	// the original stream data should be provided
359 	if ( !m_pStreamData->m_xOrigOutStream.is() )
360 		throw uno::RuntimeException();
361 
362 	m_pStreamData->m_xOrigOutStream->writeBytes( aData );
363 }
364 
365 
366 // ------------------------------------------------------------------------
367 void SAL_CALL SwitchablePersistenceStream::flush(  )
368 	throw (io::NotConnectedException, io::BufferSizeExceededException, io::IOException, uno::RuntimeException)
369 {
370 	::osl::MutexGuard aGuard( m_aMutex );
371 
372 	if ( !m_pStreamData || m_pStreamData->m_bInStreamBased )
373 	{
374 		OSL_ENSURE( sal_False, "flush() is not acceptable!\n" );
375 		return;
376 		// in future throw exception, for now some code might call flush() on closed stream
377 		// since file ucp implementation allows it
378 		// throw io::NotConnectedException();
379 	}
380 
381 	// the original stream data should be provided
382 	if ( !m_pStreamData->m_xOrigOutStream.is() )
383 		throw uno::RuntimeException();
384 
385 	m_pStreamData->m_xOrigOutStream->flush();
386 }
387 
388 
389 // ------------------------------------------------------------------------
390 void SAL_CALL SwitchablePersistenceStream::closeOutput(  )
391 	throw (io::NotConnectedException, io::BufferSizeExceededException, io::IOException, uno::RuntimeException)
392 {
393 	::osl::MutexGuard aGuard( m_aMutex );
394 
395 	if ( !m_pStreamData )
396 		throw io::NotConnectedException();
397 
398 	m_pStreamData->m_bOutOpen = sal_False;
399 	if ( !m_pStreamData->m_bInOpen )
400 		CloseAll_Impl();
401 }
402 
403 
404 
405 // com::sun::star::io::XTruncate
406 // ------------------------------------------------------------------------
407 void SAL_CALL SwitchablePersistenceStream::truncate(  )
408 	throw (io::IOException, uno::RuntimeException)
409 {
410 	::osl::MutexGuard aGuard( m_aMutex );
411 
412 	if ( !m_pStreamData )
413 		throw io::NotConnectedException();
414 
415 	if ( m_pStreamData->m_bInStreamBased )
416 		throw io::IOException();
417 
418 	// the original stream data should be provided
419 	if ( !m_pStreamData->m_xOrigTruncate.is() )
420 		throw uno::RuntimeException();
421 
422 	m_pStreamData->m_xOrigTruncate->truncate();
423 }
424 
425 
426 // com::sun::star::io::XSeekable
427 // ------------------------------------------------------------------------
428 void SAL_CALL SwitchablePersistenceStream::seek( ::sal_Int64 location )
429 	throw (lang::IllegalArgumentException, io::IOException, uno::RuntimeException)
430 {
431 	::osl::MutexGuard aGuard( m_aMutex );
432 
433 	if ( !m_pStreamData )
434 		throw io::NotConnectedException();
435 
436 	// the original stream data should be provided
437 	if ( !m_pStreamData->m_xOrigSeekable.is() )
438 		throw uno::RuntimeException();
439 
440 	m_pStreamData->m_xOrigSeekable->seek( location );
441 }
442 
443 
444 // ------------------------------------------------------------------------
445 ::sal_Int64 SAL_CALL SwitchablePersistenceStream::getPosition(  )
446 	throw (io::IOException, uno::RuntimeException)
447 {
448 	::osl::MutexGuard aGuard( m_aMutex );
449 
450 	if ( !m_pStreamData )
451 		throw io::NotConnectedException();
452 
453 	// the original stream data should be provided
454 	if ( !m_pStreamData->m_xOrigSeekable.is() )
455 		throw uno::RuntimeException();
456 
457 	return m_pStreamData->m_xOrigSeekable->getPosition();
458 }
459 
460 
461 // ------------------------------------------------------------------------
462 ::sal_Int64 SAL_CALL SwitchablePersistenceStream::getLength(  )
463 	throw (io::IOException, uno::RuntimeException)
464 {
465 	::osl::MutexGuard aGuard( m_aMutex );
466 
467 	if ( !m_pStreamData )
468 		throw io::NotConnectedException();
469 
470 	// the original stream data should be provided
471 	if ( !m_pStreamData->m_xOrigSeekable.is() )
472 		throw uno::RuntimeException();
473 
474 	return m_pStreamData->m_xOrigSeekable->getLength();
475 }
476 
477 // ------------------------------------------------------------------------
478 void SAL_CALL SwitchablePersistenceStream::waitForCompletion()
479 	throw (::com::sun::star::io::IOException, ::com::sun::star::uno::RuntimeException)
480 {
481 	if ( !m_pStreamData )
482 		throw io::NotConnectedException();
483 
484 	uno::Reference< io::XAsyncOutputMonitor > asyncOutputMonitor( m_pStreamData->m_xOrigOutStream, uno::UNO_QUERY );
485 	if ( asyncOutputMonitor.is() )
486 		asyncOutputMonitor->waitForCompletion();
487 }
488 
489