1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_io.hxx"
26 
27 
28 #include <string.h>
29 #include <osl/mutex.hxx>
30 #include <osl/diagnose.h>
31 
32 #include <rtl/unload.h>
33 
34 #include <uno/mapping.hxx>
35 
36 #include <cppuhelper/factory.hxx>
37 #include <cppuhelper/implbase3.hxx>
38 #include <cppuhelper/implementationentry.hxx>
39 
40 #include <rtl/textenc.h>
41 #include <rtl/tencinfo.h>
42 
43 #include <com/sun/star/io/XTextInputStream.hpp>
44 #include <com/sun/star/io/XActiveDataSink.hpp>
45 #include <com/sun/star/lang/XServiceInfo.hpp>
46 
47 #include "io/dllapi.h"
48 
49 
50 #define IMPLEMENTATION_NAME "com.sun.star.comp.io.TextInputStream"
51 #define SERVICE_NAME "com.sun.star.io.TextInputStream"
52 
53 using namespace ::osl;
54 using namespace ::rtl;
55 using namespace ::cppu;
56 using namespace ::com::sun::star::uno;
57 using namespace ::com::sun::star::lang;
58 using namespace ::com::sun::star::io;
59 using namespace ::com::sun::star::registry;
60 
61 namespace io_TextInputStream
62 {
63 	rtl_StandardModuleCount g_moduleCount = MODULE_COUNT_INIT;
64 
65 //===========================================================================
66 // Implementation XTextInputStream
67 
68 typedef WeakImplHelper3< XTextInputStream, XActiveDataSink, XServiceInfo > TextInputStreamHelper;
69 class OCommandEnvironment;
70 
71 #define INITIAL_UNICODE_BUFFER_CAPACITY		0x100
72 #define READ_BYTE_COUNT						0x100
73 
74 class OTextInputStream : public TextInputStreamHelper
75 {
76 	Reference< XInputStream > mxStream;
77 
78 	// Encoding
79 	OUString mEncoding;
80 	sal_Bool mbEncodingInitialized;
81 	rtl_TextToUnicodeConverter 	mConvText2Unicode;
82 	rtl_TextToUnicodeContext   	mContextText2Unicode;
83 	Sequence<sal_Int8>			mSeqSource;
84 
85 	// Internal buffer for characters that are already converted successfully
86 	sal_Unicode* mpBuffer;
87 	sal_Int32 mnBufferSize;
88 	sal_Int32 mnCharsInBuffer;
89 	sal_Bool mbReachedEOF;
90 
91 	void implResizeBuffer( void );
92 	OUString implReadString( const Sequence< sal_Unicode >& Delimiters,
93 		sal_Bool bRemoveDelimiter, sal_Bool bFindLineEnd )
94 			throw(IOException, RuntimeException);
95 	sal_Int32 implReadNext() throw(IOException, RuntimeException);
96 
97 public:
98 	OTextInputStream();
99 	virtual ~OTextInputStream();
100 
101     // Methods XTextInputStream
102     virtual OUString SAL_CALL readLine(  )
103 		throw(IOException, RuntimeException);
104     virtual OUString SAL_CALL readString( const Sequence< sal_Unicode >& Delimiters, sal_Bool bRemoveDelimiter )
105 		throw(IOException, RuntimeException);
106     virtual sal_Bool SAL_CALL isEOF(  )
107 		throw(IOException, RuntimeException);
108     virtual void SAL_CALL setEncoding( const OUString& Encoding ) throw(RuntimeException);
109 
110     // Methods XInputStream
111     virtual sal_Int32 SAL_CALL readBytes( Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead )
112 		throw(NotConnectedException, BufferSizeExceededException, IOException, RuntimeException);
113     virtual sal_Int32 SAL_CALL readSomeBytes( Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead )
114 		throw(NotConnectedException, BufferSizeExceededException, IOException, RuntimeException);
115     virtual void SAL_CALL skipBytes( sal_Int32 nBytesToSkip )
116 		throw(NotConnectedException, BufferSizeExceededException, IOException, RuntimeException);
117     virtual sal_Int32 SAL_CALL available(  )
118 		throw(NotConnectedException, IOException, RuntimeException);
119     virtual void SAL_CALL closeInput(  )
120 		throw(NotConnectedException, IOException, RuntimeException);
121 
122     // Methods XActiveDataSink
123     virtual void SAL_CALL setInputStream( const Reference< XInputStream >& aStream )
124 		throw(RuntimeException);
125     virtual Reference< XInputStream > SAL_CALL getInputStream()
126 		throw(RuntimeException);
127 
128 	// Methods XServiceInfo
129         virtual OUString              SAL_CALL getImplementationName() throw();
130         virtual Sequence< OUString >  SAL_CALL getSupportedServiceNames(void) throw();
131         virtual sal_Bool              SAL_CALL supportsService(const OUString& ServiceName) throw();
132 };
133 
134 OTextInputStream::OTextInputStream()
135 	: mSeqSource( READ_BYTE_COUNT ), mpBuffer( NULL ), mnBufferSize( 0 )
136 	, mnCharsInBuffer( 0 ), mbReachedEOF( sal_False )
137 {
138 	g_moduleCount.modCnt.acquire( &g_moduleCount.modCnt );
139 	mbEncodingInitialized = false;
140 }
141 
142 OTextInputStream::~OTextInputStream()
143 {
144 	if( mbEncodingInitialized )
145 	{
146 		rtl_destroyUnicodeToTextContext( mConvText2Unicode, mContextText2Unicode );
147 		rtl_destroyUnicodeToTextConverter( mConvText2Unicode );
148 	}
149 	g_moduleCount.modCnt.release( &g_moduleCount.modCnt );
150 }
151 
152 void OTextInputStream::implResizeBuffer( void )
153 {
154 	sal_Int32 mnNewBufferSize = mnBufferSize * 2;
155 	sal_Unicode* pNewBuffer = new sal_Unicode[ mnNewBufferSize ];
156 	memcpy( pNewBuffer, mpBuffer, mnCharsInBuffer * sizeof( sal_Unicode ) );
157 	mpBuffer = pNewBuffer;
158 	mnBufferSize = mnNewBufferSize;
159 }
160 
161 
162 //===========================================================================
163 // XTextInputStream
164 
165 OUString OTextInputStream::readLine(  )
166 	throw(IOException, RuntimeException)
167 {
168 	static Sequence< sal_Unicode > aDummySeq;
169 	return implReadString( aDummySeq, sal_True, sal_True );
170 }
171 
172 OUString OTextInputStream::readString( const Sequence< sal_Unicode >& Delimiters, sal_Bool bRemoveDelimiter )
173 		throw(IOException, RuntimeException)
174 {
175 	return implReadString( Delimiters, bRemoveDelimiter, sal_False );
176 }
177 
178 sal_Bool OTextInputStream::isEOF()
179 	throw(IOException, RuntimeException)
180 {
181 	sal_Bool bRet = sal_False;
182 	if( mnCharsInBuffer == 0 && mbReachedEOF )
183 		bRet = sal_True;
184 	return bRet;
185 }
186 
187 
188 OUString OTextInputStream::implReadString( const Sequence< sal_Unicode >& Delimiters,
189 										   sal_Bool bRemoveDelimiter, sal_Bool bFindLineEnd )
190 		throw(IOException, RuntimeException)
191 {
192 	OUString aRetStr;
193 	if( !mbEncodingInitialized )
194 	{
195 		OUString aUtf8Str( RTL_CONSTASCII_USTRINGPARAM("utf8") );
196 		setEncoding( aUtf8Str );
197 	}
198 	if( !mbEncodingInitialized )
199 		return aRetStr;
200 
201 	if( !mpBuffer )
202 	{
203 		mnBufferSize = INITIAL_UNICODE_BUFFER_CAPACITY;
204 		mpBuffer = new sal_Unicode[ mnBufferSize ];
205 	}
206 
207 	// Only for bFindLineEnd
208 	sal_Unicode cLineEndChar1 = 0x0D;
209 	sal_Unicode cLineEndChar2 = 0x0A;
210 
211 	sal_Int32 nBufferReadPos = 0;
212 	sal_Int32 nCopyLen = 0;
213 	sal_Bool bFound = sal_False;
214 	sal_Bool bFoundFirstLineEndChar = sal_False;
215 	sal_Unicode cFirstLineEndChar = 0;
216 	const sal_Unicode* pDelims = Delimiters.getConstArray();
217 	const sal_Int32 nDelimCount = Delimiters.getLength();
218 	while( !bFound )
219 	{
220 		// Still characters available?
221 		if( nBufferReadPos == mnCharsInBuffer )
222 		{
223 			// Already reached EOF? Then we can't read any more
224 			if( mbReachedEOF )
225 				break;
226 
227 			// No, so read new characters
228 			if( !implReadNext() )
229 				break;
230 		}
231 
232 		// Now there should be characters available
233 		// (otherwise the loop should have been breaked before)
234 		sal_Unicode	c = mpBuffer[ nBufferReadPos++ ];
235 
236 		if( bFindLineEnd )
237 		{
238 			if( bFoundFirstLineEndChar )
239 			{
240 				bFound = sal_True;
241 				nCopyLen = nBufferReadPos - 2;
242 				if( c == cLineEndChar1 || c == cLineEndChar2 )
243 				{
244 					// Same line end char -> new line break
245 					if( c == cFirstLineEndChar )
246 					{
247 						nBufferReadPos--;
248 					}
249 				}
250                 else
251 				{
252                     // No second line end char
253 					nBufferReadPos--;
254 				}
255 			}
256 			else if( c == cLineEndChar1 || c == cLineEndChar2 )
257 			{
258 				bFoundFirstLineEndChar = sal_True;
259 				cFirstLineEndChar = c;
260 			}
261 		}
262 		else
263 		{
264 			for( sal_Int32 i = 0 ; i < nDelimCount ; i++ )
265 			{
266 				if( c == pDelims[ i ] )
267 				{
268 					bFound = sal_True;
269 					nCopyLen = nBufferReadPos;
270 					if( bRemoveDelimiter )
271 						nCopyLen--;
272 				}
273 			}
274 		}
275 	}
276 
277 	// Nothing found? Return all
278 	if( !nCopyLen && !bFound && mbReachedEOF )
279 		nCopyLen = nBufferReadPos;
280 
281 	// Create string
282 	if( nCopyLen )
283 		aRetStr = OUString( mpBuffer, nCopyLen );
284 
285 	// Copy rest of buffer
286 	memmove( mpBuffer, mpBuffer + nBufferReadPos,
287 		(mnCharsInBuffer - nBufferReadPos) * sizeof( sal_Unicode ) );
288 	mnCharsInBuffer -= nBufferReadPos;
289 
290 	return aRetStr;
291 }
292 
293 
294 sal_Int32 OTextInputStream::implReadNext()
295 		throw(IOException, RuntimeException)
296 {
297 	sal_Int32 nFreeBufferSize = mnBufferSize - mnCharsInBuffer;
298 	if( nFreeBufferSize < READ_BYTE_COUNT )
299 		implResizeBuffer();
300 	nFreeBufferSize = mnBufferSize - mnCharsInBuffer;
301 
302 	try
303 	{
304 		sal_Int32 nBytesToRead = READ_BYTE_COUNT;
305 		sal_Int32 nRead = mxStream->readSomeBytes( mSeqSource, nBytesToRead );
306 		sal_Int32 nTotalRead = nRead;
307 		if( nRead < nBytesToRead )
308 			mbReachedEOF = sal_True;
309 
310 		// Try to convert
311 		sal_uInt32 uiInfo;
312 		sal_Size nSrcCvtBytes = 0;
313 		sal_Size nTargetCount = 0;
314 		sal_Size nSourceCount = 0;
315 		while( sal_True )
316 		{
317 			const sal_Int8 *pbSource = mSeqSource.getConstArray();
318 
319 			// All invalid characters are transformed to the unicode undefined char
320 			nTargetCount += rtl_convertTextToUnicode(
321 								mConvText2Unicode,
322 								mContextText2Unicode,
323 								(const sal_Char*) &( pbSource[nSourceCount] ),
324 								nTotalRead - nSourceCount,
325 								mpBuffer + mnCharsInBuffer + nTargetCount,
326 								nFreeBufferSize - nTargetCount,
327 								RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_DEFAULT   |
328 								RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT |
329 								RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT,
330 								&uiInfo,
331 								&nSrcCvtBytes );
332 			nSourceCount += nSrcCvtBytes;
333 
334 			sal_Bool bCont = sal_False;
335 			if( uiInfo & RTL_TEXTTOUNICODE_INFO_DESTBUFFERTOSMALL )
336 			{
337 				implResizeBuffer();
338 				bCont = sal_True;
339 			}
340 
341 			if( uiInfo & RTL_TEXTTOUNICODE_INFO_SRCBUFFERTOSMALL )
342 			{
343 				// read next byte
344 				static Sequence< sal_Int8 > aOneByteSeq( 1 );
345 				nRead = mxStream->readSomeBytes( aOneByteSeq, 1 );
346 				if( nRead == 0 )
347 				{
348 					mbReachedEOF = sal_True;
349 					break;
350 				}
351 
352 				sal_Int32 nOldLen = mSeqSource.getLength();
353 				nTotalRead++;
354 				if( nTotalRead > nOldLen )
355 				{
356 					mSeqSource.realloc( nTotalRead );
357 				}
358 				mSeqSource.getArray()[ nOldLen ] = aOneByteSeq.getConstArray()[ 0 ];
359 				pbSource = mSeqSource.getConstArray();
360 				bCont = sal_True;
361 			}
362 
363 			if( bCont )
364 				continue;
365 			break;
366 		}
367 
368 		mnCharsInBuffer += nTargetCount;
369 		return nTargetCount;
370 	}
371 	catch( NotConnectedException& )
372 	{
373 		throw IOException();
374 		//throw IOException( L"OTextInputStream::implReadString failed" );
375 	}
376 	catch( BufferSizeExceededException& )
377 	{
378 		throw IOException();
379 	}
380 }
381 
382 void OTextInputStream::setEncoding( const OUString& Encoding )
383 	throw(RuntimeException)
384 {
385 	OString aOEncodingStr = OUStringToOString( Encoding, RTL_TEXTENCODING_ASCII_US );
386 	rtl_TextEncoding encoding = rtl_getTextEncodingFromMimeCharset( aOEncodingStr.getStr() );
387 	if( RTL_TEXTENCODING_DONTKNOW == encoding )
388 		return;
389 
390 	mbEncodingInitialized = true;
391 	mConvText2Unicode = rtl_createTextToUnicodeConverter( encoding );
392 	mContextText2Unicode = rtl_createTextToUnicodeContext( mConvText2Unicode );
393 	mEncoding = Encoding;
394 }
395 
396 //===========================================================================
397 // XInputStream
398 
399 sal_Int32 OTextInputStream::readBytes( Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead )
400 	throw(NotConnectedException, BufferSizeExceededException, IOException, RuntimeException)
401 {
402 	return mxStream->readBytes( aData, nBytesToRead );
403 }
404 
405 sal_Int32 OTextInputStream::readSomeBytes( Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead )
406 	throw(NotConnectedException, BufferSizeExceededException, IOException, RuntimeException)
407 {
408 	return mxStream->readSomeBytes( aData, nMaxBytesToRead );
409 }
410 
411 void OTextInputStream::skipBytes( sal_Int32 nBytesToSkip )
412 	throw(NotConnectedException, BufferSizeExceededException, IOException, RuntimeException)
413 {
414 	mxStream->skipBytes( nBytesToSkip );
415 }
416 
417 sal_Int32 OTextInputStream::available(  )
418 	throw(NotConnectedException, IOException, RuntimeException)
419 {
420 	return mxStream->available();
421 }
422 
423 void OTextInputStream::closeInput(  )
424 	throw(NotConnectedException, IOException, RuntimeException)
425 {
426 	mxStream->closeInput();
427 }
428 
429 
430 //===========================================================================
431 // XActiveDataSink
432 
433 void OTextInputStream::setInputStream( const Reference< XInputStream >& aStream )
434 	throw(RuntimeException)
435 {
436 	mxStream = aStream;
437 }
438 
439 Reference< XInputStream > OTextInputStream::getInputStream()
440 	throw(RuntimeException)
441 {
442 	return mxStream;
443 }
444 
445 
446 Reference< XInterface > SAL_CALL TextInputStream_CreateInstance( const Reference< XComponentContext > &)
447 {
448 	return Reference < XInterface >( ( OWeakObject * ) new OTextInputStream() );
449 }
450 
451 OUString TextInputStream_getImplementationName()
452 {
453 	return OUString( RTL_CONSTASCII_USTRINGPARAM( IMPLEMENTATION_NAME ) );
454 }
455 
456 Sequence< OUString > TextInputStream_getSupportedServiceNames()
457 {
458 	static Sequence < OUString > *pNames = 0;
459 	if( ! pNames )
460 	{
461 		MutexGuard guard( Mutex::getGlobalMutex() );
462 		if( !pNames )
463 		{
464 			static Sequence< OUString > seqNames(1);
465 			seqNames.getArray()[0] = OUString( RTL_CONSTASCII_USTRINGPARAM( SERVICE_NAME ) );
466 			pNames = &seqNames;
467 		}
468 	}
469 	return *pNames;
470 }
471 
472 OUString OTextInputStream::getImplementationName() throw()
473 {
474 	return TextInputStream_getImplementationName();
475 }
476 
477 sal_Bool OTextInputStream::supportsService(const OUString& ServiceName) throw()
478 {
479 	Sequence< OUString > aSNL = getSupportedServiceNames();
480 	const OUString * pArray = aSNL.getConstArray();
481 
482 	for( sal_Int32 i = 0; i < aSNL.getLength(); i++ )
483 		if( pArray[i] == ServiceName )
484 			return sal_True;
485 
486 	return sal_False;
487 }
488 
489 Sequence< OUString > OTextInputStream::getSupportedServiceNames(void) throw()
490 {
491 	return TextInputStream_getSupportedServiceNames();
492 }
493 
494 }
495 
496 using namespace io_TextInputStream;
497 
498 static struct ImplementationEntry g_entries[] =
499 {
500 	{
501 		TextInputStream_CreateInstance, TextInputStream_getImplementationName ,
502 		TextInputStream_getSupportedServiceNames, createSingleComponentFactory ,
503 		&g_moduleCount.modCnt , 0
504 	},
505 	{ 0, 0, 0, 0, 0, 0 }
506 };
507 
508 extern "C"
509 {
510 IO_DLLPUBLIC sal_Bool SAL_CALL component_canUnload( TimeValue *pTime )
511 {
512 	return g_moduleCount.canUnload( &g_moduleCount , pTime );
513 }
514 
515 //==================================================================================================
516 IO_DLLPUBLIC void SAL_CALL component_getImplementationEnvironment(
517 	const sal_Char ** ppEnvTypeName, uno_Environment ** )
518 {
519 	*ppEnvTypeName = CPPU_CURRENT_LANGUAGE_BINDING_NAME;
520 }
521 //==================================================================================================
522 IO_DLLPUBLIC void * SAL_CALL component_getFactory(
523 	const sal_Char * pImplName, void * pServiceManager, void * pRegistryKey )
524 {
525 	return component_getFactoryHelper( pImplName, pServiceManager, pRegistryKey , g_entries );
526 }
527 }
528 
529 
530