| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_io.hxx" |
| |
| |
| #include <string.h> |
| #include <osl/mutex.hxx> |
| #include <osl/diagnose.h> |
| |
| #include <rtl/unload.h> |
| |
| #include <uno/mapping.hxx> |
| |
| #include <cppuhelper/factory.hxx> |
| #include <cppuhelper/implbase3.hxx> |
| #include <cppuhelper/implementationentry.hxx> |
| |
| #include <rtl/textenc.h> |
| #include <rtl/tencinfo.h> |
| |
| #include <com/sun/star/io/XTextInputStream.hpp> |
| #include <com/sun/star/io/XActiveDataSink.hpp> |
| #include <com/sun/star/lang/XServiceInfo.hpp> |
| |
| |
| #define IMPLEMENTATION_NAME "com.sun.star.comp.io.TextInputStream" |
| #define SERVICE_NAME "com.sun.star.io.TextInputStream" |
| |
| using namespace ::osl; |
| using namespace ::rtl; |
| using namespace ::cppu; |
| using namespace ::com::sun::star::uno; |
| using namespace ::com::sun::star::lang; |
| using namespace ::com::sun::star::io; |
| using namespace ::com::sun::star::registry; |
| |
| namespace io_TextInputStream |
| { |
| rtl_StandardModuleCount g_moduleCount = MODULE_COUNT_INIT; |
| |
| //=========================================================================== |
| // Implementation XTextInputStream |
| |
| typedef WeakImplHelper3< XTextInputStream, XActiveDataSink, XServiceInfo > TextInputStreamHelper; |
| class OCommandEnvironment; |
| |
| #define INITIAL_UNICODE_BUFFER_CAPACITY 0x100 |
| #define READ_BYTE_COUNT 0x100 |
| |
| class OTextInputStream : public TextInputStreamHelper |
| { |
| Reference< XInputStream > mxStream; |
| |
| // Encoding |
| OUString mEncoding; |
| sal_Bool mbEncodingInitialized; |
| rtl_TextToUnicodeConverter mConvText2Unicode; |
| rtl_TextToUnicodeContext mContextText2Unicode; |
| Sequence<sal_Int8> mSeqSource; |
| |
| // Internal buffer for characters that are already converted successfully |
| sal_Unicode* mpBuffer; |
| sal_Int32 mnBufferSize; |
| sal_Int32 mnCharsInBuffer; |
| sal_Bool mbReachedEOF; |
| |
| void implResizeBuffer( void ); |
| OUString implReadString( const Sequence< sal_Unicode >& Delimiters, |
| sal_Bool bRemoveDelimiter, sal_Bool bFindLineEnd ) |
| throw(IOException, RuntimeException); |
| sal_Int32 implReadNext() throw(IOException, RuntimeException); |
| |
| public: |
| OTextInputStream(); |
| virtual ~OTextInputStream(); |
| |
| // Methods XTextInputStream |
| virtual OUString SAL_CALL readLine( ) |
| throw(IOException, RuntimeException); |
| virtual OUString SAL_CALL readString( const Sequence< sal_Unicode >& Delimiters, sal_Bool bRemoveDelimiter ) |
| throw(IOException, RuntimeException); |
| virtual sal_Bool SAL_CALL isEOF( ) |
| throw(IOException, RuntimeException); |
| virtual void SAL_CALL setEncoding( const OUString& Encoding ) throw(RuntimeException); |
| |
| // Methods XInputStream |
| virtual sal_Int32 SAL_CALL readBytes( Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead ) |
| throw(NotConnectedException, BufferSizeExceededException, IOException, RuntimeException); |
| virtual sal_Int32 SAL_CALL readSomeBytes( Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead ) |
| throw(NotConnectedException, BufferSizeExceededException, IOException, RuntimeException); |
| virtual void SAL_CALL skipBytes( sal_Int32 nBytesToSkip ) |
| throw(NotConnectedException, BufferSizeExceededException, IOException, RuntimeException); |
| virtual sal_Int32 SAL_CALL available( ) |
| throw(NotConnectedException, IOException, RuntimeException); |
| virtual void SAL_CALL closeInput( ) |
| throw(NotConnectedException, IOException, RuntimeException); |
| |
| // Methods XActiveDataSink |
| virtual void SAL_CALL setInputStream( const Reference< XInputStream >& aStream ) |
| throw(RuntimeException); |
| virtual Reference< XInputStream > SAL_CALL getInputStream() |
| throw(RuntimeException); |
| |
| // Methods XServiceInfo |
| virtual OUString SAL_CALL getImplementationName() throw(); |
| virtual Sequence< OUString > SAL_CALL getSupportedServiceNames(void) throw(); |
| virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) throw(); |
| }; |
| |
| OTextInputStream::OTextInputStream() |
| : mSeqSource( READ_BYTE_COUNT ), mpBuffer( NULL ), mnBufferSize( 0 ) |
| , mnCharsInBuffer( 0 ), mbReachedEOF( sal_False ) |
| { |
| g_moduleCount.modCnt.acquire( &g_moduleCount.modCnt ); |
| mbEncodingInitialized = false; |
| } |
| |
| OTextInputStream::~OTextInputStream() |
| { |
| if( mbEncodingInitialized ) |
| { |
| rtl_destroyUnicodeToTextContext( mConvText2Unicode, mContextText2Unicode ); |
| rtl_destroyUnicodeToTextConverter( mConvText2Unicode ); |
| } |
| g_moduleCount.modCnt.release( &g_moduleCount.modCnt ); |
| } |
| |
| void OTextInputStream::implResizeBuffer( void ) |
| { |
| sal_Int32 mnNewBufferSize = mnBufferSize * 2; |
| sal_Unicode* pNewBuffer = new sal_Unicode[ mnNewBufferSize ]; |
| memcpy( pNewBuffer, mpBuffer, mnCharsInBuffer * sizeof( sal_Unicode ) ); |
| mpBuffer = pNewBuffer; |
| mnBufferSize = mnNewBufferSize; |
| } |
| |
| |
| //=========================================================================== |
| // XTextInputStream |
| |
| OUString OTextInputStream::readLine( ) |
| throw(IOException, RuntimeException) |
| { |
| static Sequence< sal_Unicode > aDummySeq; |
| return implReadString( aDummySeq, sal_True, sal_True ); |
| } |
| |
| OUString OTextInputStream::readString( const Sequence< sal_Unicode >& Delimiters, sal_Bool bRemoveDelimiter ) |
| throw(IOException, RuntimeException) |
| { |
| return implReadString( Delimiters, bRemoveDelimiter, sal_False ); |
| } |
| |
| sal_Bool OTextInputStream::isEOF() |
| throw(IOException, RuntimeException) |
| { |
| sal_Bool bRet = sal_False; |
| if( mnCharsInBuffer == 0 && mbReachedEOF ) |
| bRet = sal_True; |
| return bRet; |
| } |
| |
| |
| OUString OTextInputStream::implReadString( const Sequence< sal_Unicode >& Delimiters, |
| sal_Bool bRemoveDelimiter, sal_Bool bFindLineEnd ) |
| throw(IOException, RuntimeException) |
| { |
| OUString aRetStr; |
| if( !mbEncodingInitialized ) |
| { |
| OUString aUtf8Str( RTL_CONSTASCII_USTRINGPARAM("utf8") ); |
| setEncoding( aUtf8Str ); |
| } |
| if( !mbEncodingInitialized ) |
| return aRetStr; |
| |
| if( !mpBuffer ) |
| { |
| mnBufferSize = INITIAL_UNICODE_BUFFER_CAPACITY; |
| mpBuffer = new sal_Unicode[ mnBufferSize ]; |
| } |
| |
| // Only for bFindLineEnd |
| sal_Unicode cLineEndChar1 = 0x0D; |
| sal_Unicode cLineEndChar2 = 0x0A; |
| |
| sal_Int32 nBufferReadPos = 0; |
| sal_Int32 nCopyLen = 0; |
| sal_Bool bFound = sal_False; |
| sal_Bool bFoundFirstLineEndChar = sal_False; |
| sal_Unicode cFirstLineEndChar = 0; |
| const sal_Unicode* pDelims = Delimiters.getConstArray(); |
| const sal_Int32 nDelimCount = Delimiters.getLength(); |
| while( !bFound ) |
| { |
| // Still characters available? |
| if( nBufferReadPos == mnCharsInBuffer ) |
| { |
| // Already reached EOF? Then we can't read any more |
| if( mbReachedEOF ) |
| break; |
| |
| // No, so read new characters |
| if( !implReadNext() ) |
| break; |
| } |
| |
| // Now there should be characters available |
| // (otherwise the loop should have been breaked before) |
| sal_Unicode c = mpBuffer[ nBufferReadPos++ ]; |
| |
| if( bFindLineEnd ) |
| { |
| if( bFoundFirstLineEndChar ) |
| { |
| bFound = sal_True; |
| nCopyLen = nBufferReadPos - 2; |
| if( c == cLineEndChar1 || c == cLineEndChar2 ) |
| { |
| // Same line end char -> new line break |
| if( c == cFirstLineEndChar ) |
| { |
| nBufferReadPos--; |
| } |
| } |
| else |
| { |
| // No second line end char |
| nBufferReadPos--; |
| } |
| } |
| else if( c == cLineEndChar1 || c == cLineEndChar2 ) |
| { |
| bFoundFirstLineEndChar = sal_True; |
| cFirstLineEndChar = c; |
| } |
| } |
| else |
| { |
| for( sal_Int32 i = 0 ; i < nDelimCount ; i++ ) |
| { |
| if( c == pDelims[ i ] ) |
| { |
| bFound = sal_True; |
| nCopyLen = nBufferReadPos; |
| if( bRemoveDelimiter ) |
| nCopyLen--; |
| } |
| } |
| } |
| } |
| |
| // Nothing found? Return all |
| if( !nCopyLen && !bFound && mbReachedEOF ) |
| nCopyLen = nBufferReadPos; |
| |
| // Create string |
| if( nCopyLen ) |
| aRetStr = OUString( mpBuffer, nCopyLen ); |
| |
| // Copy rest of buffer |
| memmove( mpBuffer, mpBuffer + nBufferReadPos, |
| (mnCharsInBuffer - nBufferReadPos) * sizeof( sal_Unicode ) ); |
| mnCharsInBuffer -= nBufferReadPos; |
| |
| return aRetStr; |
| } |
| |
| |
| sal_Int32 OTextInputStream::implReadNext() |
| throw(IOException, RuntimeException) |
| { |
| sal_Int32 nFreeBufferSize = mnBufferSize - mnCharsInBuffer; |
| if( nFreeBufferSize < READ_BYTE_COUNT ) |
| implResizeBuffer(); |
| nFreeBufferSize = mnBufferSize - mnCharsInBuffer; |
| |
| try |
| { |
| sal_Int32 nBytesToRead = READ_BYTE_COUNT; |
| sal_Int32 nRead = mxStream->readSomeBytes( mSeqSource, nBytesToRead ); |
| sal_Int32 nTotalRead = nRead; |
| if( nRead < nBytesToRead ) |
| mbReachedEOF = sal_True; |
| |
| // Try to convert |
| sal_uInt32 uiInfo; |
| sal_Size nSrcCvtBytes = 0; |
| sal_Size nTargetCount = 0; |
| sal_Size nSourceCount = 0; |
| while( sal_True ) |
| { |
| const sal_Int8 *pbSource = mSeqSource.getConstArray(); |
| |
| // All invalid characters are transformed to the unicode undefined char |
| nTargetCount += rtl_convertTextToUnicode( |
| mConvText2Unicode, |
| mContextText2Unicode, |
| (const sal_Char*) &( pbSource[nSourceCount] ), |
| nTotalRead - nSourceCount, |
| mpBuffer + mnCharsInBuffer + nTargetCount, |
| nFreeBufferSize - nTargetCount, |
| RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_DEFAULT | |
| RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT | |
| RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT, |
| &uiInfo, |
| &nSrcCvtBytes ); |
| nSourceCount += nSrcCvtBytes; |
| |
| sal_Bool bCont = sal_False; |
| if( uiInfo & RTL_TEXTTOUNICODE_INFO_DESTBUFFERTOSMALL ) |
| { |
| implResizeBuffer(); |
| bCont = sal_True; |
| } |
| |
| if( uiInfo & RTL_TEXTTOUNICODE_INFO_SRCBUFFERTOSMALL ) |
| { |
| // read next byte |
| static Sequence< sal_Int8 > aOneByteSeq( 1 ); |
| nRead = mxStream->readSomeBytes( aOneByteSeq, 1 ); |
| if( nRead == 0 ) |
| { |
| mbReachedEOF = sal_True; |
| break; |
| } |
| |
| sal_Int32 nOldLen = mSeqSource.getLength(); |
| nTotalRead++; |
| if( nTotalRead > nOldLen ) |
| { |
| mSeqSource.realloc( nTotalRead ); |
| } |
| mSeqSource.getArray()[ nOldLen ] = aOneByteSeq.getConstArray()[ 0 ]; |
| pbSource = mSeqSource.getConstArray(); |
| bCont = sal_True; |
| } |
| |
| if( bCont ) |
| continue; |
| break; |
| } |
| |
| mnCharsInBuffer += nTargetCount; |
| return nTargetCount; |
| } |
| catch( NotConnectedException& ) |
| { |
| throw IOException(); |
| //throw IOException( L"OTextInputStream::implReadString failed" ); |
| } |
| catch( BufferSizeExceededException& ) |
| { |
| throw IOException(); |
| } |
| } |
| |
| void OTextInputStream::setEncoding( const OUString& Encoding ) |
| throw(RuntimeException) |
| { |
| OString aOEncodingStr = OUStringToOString( Encoding, RTL_TEXTENCODING_ASCII_US ); |
| rtl_TextEncoding encoding = rtl_getTextEncodingFromMimeCharset( aOEncodingStr.getStr() ); |
| if( RTL_TEXTENCODING_DONTKNOW == encoding ) |
| return; |
| |
| mbEncodingInitialized = true; |
| mConvText2Unicode = rtl_createTextToUnicodeConverter( encoding ); |
| mContextText2Unicode = rtl_createTextToUnicodeContext( mConvText2Unicode ); |
| mEncoding = Encoding; |
| } |
| |
| //=========================================================================== |
| // XInputStream |
| |
| sal_Int32 OTextInputStream::readBytes( Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead ) |
| throw(NotConnectedException, BufferSizeExceededException, IOException, RuntimeException) |
| { |
| return mxStream->readBytes( aData, nBytesToRead ); |
| } |
| |
| sal_Int32 OTextInputStream::readSomeBytes( Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead ) |
| throw(NotConnectedException, BufferSizeExceededException, IOException, RuntimeException) |
| { |
| return mxStream->readSomeBytes( aData, nMaxBytesToRead ); |
| } |
| |
| void OTextInputStream::skipBytes( sal_Int32 nBytesToSkip ) |
| throw(NotConnectedException, BufferSizeExceededException, IOException, RuntimeException) |
| { |
| mxStream->skipBytes( nBytesToSkip ); |
| } |
| |
| sal_Int32 OTextInputStream::available( ) |
| throw(NotConnectedException, IOException, RuntimeException) |
| { |
| return mxStream->available(); |
| } |
| |
| void OTextInputStream::closeInput( ) |
| throw(NotConnectedException, IOException, RuntimeException) |
| { |
| mxStream->closeInput(); |
| } |
| |
| |
| //=========================================================================== |
| // XActiveDataSink |
| |
| void OTextInputStream::setInputStream( const Reference< XInputStream >& aStream ) |
| throw(RuntimeException) |
| { |
| mxStream = aStream; |
| } |
| |
| Reference< XInputStream > OTextInputStream::getInputStream() |
| throw(RuntimeException) |
| { |
| return mxStream; |
| } |
| |
| |
| Reference< XInterface > SAL_CALL TextInputStream_CreateInstance( const Reference< XComponentContext > &) |
| { |
| return Reference < XInterface >( ( OWeakObject * ) new OTextInputStream() ); |
| } |
| |
| OUString TextInputStream_getImplementationName() |
| { |
| return OUString( RTL_CONSTASCII_USTRINGPARAM( IMPLEMENTATION_NAME ) ); |
| } |
| |
| Sequence< OUString > TextInputStream_getSupportedServiceNames() |
| { |
| static Sequence < OUString > *pNames = 0; |
| if( ! pNames ) |
| { |
| MutexGuard guard( Mutex::getGlobalMutex() ); |
| if( !pNames ) |
| { |
| static Sequence< OUString > seqNames(1); |
| seqNames.getArray()[0] = OUString( RTL_CONSTASCII_USTRINGPARAM( SERVICE_NAME ) ); |
| pNames = &seqNames; |
| } |
| } |
| return *pNames; |
| } |
| |
| OUString OTextInputStream::getImplementationName() throw() |
| { |
| return TextInputStream_getImplementationName(); |
| } |
| |
| sal_Bool OTextInputStream::supportsService(const OUString& ServiceName) throw() |
| { |
| Sequence< OUString > aSNL = getSupportedServiceNames(); |
| const OUString * pArray = aSNL.getConstArray(); |
| |
| for( sal_Int32 i = 0; i < aSNL.getLength(); i++ ) |
| if( pArray[i] == ServiceName ) |
| return sal_True; |
| |
| return sal_False; |
| } |
| |
| Sequence< OUString > OTextInputStream::getSupportedServiceNames(void) throw() |
| { |
| return TextInputStream_getSupportedServiceNames(); |
| } |
| |
| } |
| |
| using namespace io_TextInputStream; |
| |
| static struct ImplementationEntry g_entries[] = |
| { |
| { |
| TextInputStream_CreateInstance, TextInputStream_getImplementationName , |
| TextInputStream_getSupportedServiceNames, createSingleComponentFactory , |
| &g_moduleCount.modCnt , 0 |
| }, |
| { 0, 0, 0, 0, 0, 0 } |
| }; |
| |
| extern "C" |
| { |
| sal_Bool SAL_CALL component_canUnload( TimeValue *pTime ) |
| { |
| return g_moduleCount.canUnload( &g_moduleCount , pTime ); |
| } |
| |
| //================================================================================================== |
| void SAL_CALL component_getImplementationEnvironment( |
| const sal_Char ** ppEnvTypeName, uno_Environment ** ) |
| { |
| *ppEnvTypeName = CPPU_CURRENT_LANGUAGE_BINDING_NAME; |
| } |
| //================================================================================================== |
| void * SAL_CALL component_getFactory( |
| const sal_Char * pImplName, void * pServiceManager, void * pRegistryKey ) |
| { |
| return component_getFactoryHelper( pImplName, pServiceManager, pRegistryKey , g_entries ); |
| } |
| } |
| |
| |