/**************************************************************
 * 
 * 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 <map>
#include <vector>

#include <com/sun/star/io/XMarkableStream.hpp>
#include <com/sun/star/io/XOutputStream.hpp>
#include <com/sun/star/io/XInputStream.hpp>
#include <com/sun/star/io/XActiveDataSource.hpp>
#include <com/sun/star/io/XActiveDataSink.hpp>
#include <com/sun/star/io/XConnectable.hpp>
#include <com/sun/star/lang/XServiceInfo.hpp>

#include <cppuhelper/factory.hxx>
#include <cppuhelper/weak.hxx>      // OWeakObject
#include <cppuhelper/implbase5.hxx>

#include <osl/mutex.hxx>
#include <rtl/ustrbuf.hxx>

#include <string.h>


using namespace ::std;
using namespace ::rtl;
using namespace ::cppu;
using namespace ::osl;
using namespace ::com::sun::star::io;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::lang;

#include "streamhelper.hxx"
#include "factreg.hxx"

namespace io_stm {

/***********************
*
* OMarkableOutputStream.
*
* This object allows to set marks in an outputstream. It is allowed to jump back to the marks and
* rewrite the some bytes.
*
*         The object must buffer the data since the last mark set. Flush will not
*         have any effect. As soon as the last mark has been removed, the object may write the data
*         through to the chained object.
*
**********************/
class OMarkableOutputStream :
	public WeakImplHelper5< XOutputStream ,
	                        XActiveDataSource ,
	                        XMarkableStream ,
	                        XConnectable,
                            XServiceInfo
                          >
{
public:
	OMarkableOutputStream(  );
	~OMarkableOutputStream();

public: // XOutputStream
    virtual void SAL_CALL writeBytes(const Sequence< sal_Int8 >& aData)
		throw (	NotConnectedException,
				BufferSizeExceededException,
				RuntimeException);
    virtual void SAL_CALL flush(void)
		throw (	NotConnectedException,
				BufferSizeExceededException,
				RuntimeException);
    virtual void SAL_CALL closeOutput(void)
		throw (	NotConnectedException,
				BufferSizeExceededException,
				RuntimeException);

public: // XMarkable
    virtual sal_Int32 SAL_CALL createMark(void)
		throw (IOException, RuntimeException);
    virtual void SAL_CALL deleteMark(sal_Int32 Mark)
		throw (IOException,
			   IllegalArgumentException,
			   RuntimeException);
    virtual void SAL_CALL jumpToMark(sal_Int32 nMark)
		throw (IOException,
			   IllegalArgumentException,
			   RuntimeException);
    virtual void SAL_CALL jumpToFurthest(void)
		throw (IOException, RuntimeException);
    virtual sal_Int32 SAL_CALL offsetToMark(sal_Int32 nMark)
		throw (IOException,
			   IllegalArgumentException,
			   RuntimeException);

public: // XActiveDataSource
    virtual void SAL_CALL setOutputStream(const Reference < XOutputStream > & aStream)
		throw (RuntimeException);
    virtual Reference < XOutputStream > SAL_CALL getOutputStream(void)
		throw (RuntimeException);

public: // XConnectable
    virtual void SAL_CALL setPredecessor(const Reference < XConnectable > & aPredecessor)
		throw (RuntimeException);
    virtual Reference < XConnectable > SAL_CALL getPredecessor(void) throw (RuntimeException);
    virtual void SAL_CALL setSuccessor(const Reference < XConnectable >& aSuccessor)
		throw (RuntimeException);
    virtual Reference<  XConnectable >  SAL_CALL getSuccessor(void) throw (RuntimeException);

public: // XServiceInfo
    OUString                     SAL_CALL getImplementationName() throw ();
    Sequence< OUString >         SAL_CALL getSupportedServiceNames(void) throw ();
    sal_Bool                        SAL_CALL supportsService(const OUString& ServiceName) throw ();

private:
	// helper methods
	void checkMarksAndFlush() throw( NotConnectedException, BufferSizeExceededException);

	Reference< XConnectable > m_succ;
	Reference< XConnectable > m_pred;

	Reference< XOutputStream >  m_output;
	sal_Bool m_bValidStream;

	IRingBuffer *m_pBuffer;
	map<sal_Int32,sal_Int32,less< sal_Int32 > > m_mapMarks;
	sal_Int32 m_nCurrentPos;
	sal_Int32 m_nCurrentMark;

	Mutex m_mutex;
};

OMarkableOutputStream::OMarkableOutputStream( )
{
	g_moduleCount.modCnt.acquire( &g_moduleCount.modCnt );
	m_pBuffer = new MemRingBuffer;
	m_nCurrentPos = 0;
	m_nCurrentMark = 0;
}

OMarkableOutputStream::~OMarkableOutputStream()
{
	delete m_pBuffer;
	g_moduleCount.modCnt.release( &g_moduleCount.modCnt );
}


// XOutputStream
void OMarkableOutputStream::writeBytes(const Sequence< sal_Int8 >& aData)
	throw (	NotConnectedException,
			BufferSizeExceededException,
			RuntimeException)
{
	if( m_bValidStream ) {
		if( m_mapMarks.empty() && ( m_pBuffer->getSize() == 0 ) ) {
			// no mark and  buffer active, simple write through
			m_output->writeBytes( aData );
		}
		else {
			MutexGuard guard( m_mutex );
			// new data must be buffered
			try
			{
				m_pBuffer->writeAt( m_nCurrentPos , aData );
				m_nCurrentPos += aData.getLength();
			}
			catch( IRingBuffer_OutOfBoundsException & )
			{
				throw BufferSizeExceededException();
			}
			catch( IRingBuffer_OutOfMemoryException & )
			{
				throw BufferSizeExceededException();
			}
			checkMarksAndFlush();
		}
	}
	else {
		throw NotConnectedException();
	}
}

void OMarkableOutputStream::flush(void)
	throw (	NotConnectedException,
			BufferSizeExceededException,
			RuntimeException)
{
    Reference< XOutputStream > output;
    {
        MutexGuard guard( m_mutex );
        output = m_output;
    }

	// Markable cannot flush buffered data, because the data may get rewritten,
    // however one can forward the flush to the chained stream to give it
    // a chance to write data buffered in the chained stream.
    if( output.is() )
    {
        output->flush();
    }
}

void OMarkableOutputStream::closeOutput(void)
	throw (	NotConnectedException,
			BufferSizeExceededException,
			RuntimeException)
{
	if( m_bValidStream ) {
		MutexGuard guard( m_mutex );
		// all marks must be cleared and all

		if( ! m_mapMarks.empty() )
		{
			m_mapMarks.clear();
	 	}
		m_nCurrentPos = m_pBuffer->getSize();
 	 	checkMarksAndFlush();

		m_output->closeOutput();

		setOutputStream( Reference< XOutputStream > () );
		setPredecessor( Reference < XConnectable >() );
		setSuccessor( Reference< XConnectable > () );
	}
	else {
		throw NotConnectedException();
	}
}


sal_Int32 OMarkableOutputStream::createMark(void)
	throw ( IOException,
			RuntimeException)
{
	MutexGuard guard( m_mutex );
	sal_Int32 nMark = m_nCurrentMark;

	m_mapMarks[nMark] = m_nCurrentPos;

	m_nCurrentMark ++;
	return nMark;
}

void OMarkableOutputStream::deleteMark(sal_Int32 Mark)
	throw( IOException,
		   IllegalArgumentException,
		   RuntimeException)
{
	MutexGuard guard( m_mutex );
	map<sal_Int32,sal_Int32,less<sal_Int32> >::iterator ii = m_mapMarks.find( Mark );

	if( ii == m_mapMarks.end() ) {
        OUStringBuffer buf( 128 );
        buf.appendAscii( "MarkableOutputStream::deleteMark unknown mark (" );
        buf.append( Mark );
        buf.appendAscii( ")");
	    throw IllegalArgumentException( buf.makeStringAndClear(), *this, 0);
	}
	else {
		m_mapMarks.erase( ii );
		checkMarksAndFlush();
	}
}

void OMarkableOutputStream::jumpToMark(sal_Int32 nMark)
	throw (IOException,
		   IllegalArgumentException,
		   RuntimeException)
{
	MutexGuard guard( m_mutex );
	map<sal_Int32,sal_Int32,less<sal_Int32> >::iterator ii = m_mapMarks.find( nMark );

	if( ii == m_mapMarks.end() ) {
        OUStringBuffer buf( 128 );
        buf.appendAscii( "MarkableOutputStream::jumpToMark unknown mark (" );
        buf.append( nMark );
        buf.appendAscii( ")");
	    throw IllegalArgumentException( buf.makeStringAndClear(), *this, 0);
	}
	else {
		m_nCurrentPos = (*ii).second;
	}
}

void OMarkableOutputStream::jumpToFurthest(void)
	throw (IOException,
		   RuntimeException)
{
	MutexGuard guard( m_mutex );
	m_nCurrentPos = m_pBuffer->getSize();
	checkMarksAndFlush();
}

sal_Int32 OMarkableOutputStream::offsetToMark(sal_Int32 nMark)
	throw (IOException,
		   IllegalArgumentException,
		   RuntimeException)
{

	MutexGuard guard( m_mutex );
	map<sal_Int32,sal_Int32,less<sal_Int32> >::const_iterator ii = m_mapMarks.find( nMark );

	if( ii == m_mapMarks.end() )
	{
        OUStringBuffer buf( 128 );
        buf.appendAscii( "MarkableOutputStream::offsetToMark unknown mark (" );
        buf.append( nMark );
        buf.appendAscii( ")");
	    throw IllegalArgumentException( buf.makeStringAndClear(), *this, 0);
	}
	return m_nCurrentPos - (*ii).second;
}



// XActiveDataSource2
void OMarkableOutputStream::setOutputStream(const Reference < XOutputStream >& aStream)
	throw (RuntimeException)
{
	if( m_output != aStream ) {
		m_output = aStream;

		Reference < XConnectable > succ( m_output , UNO_QUERY );
		setSuccessor( succ );
	}
	m_bValidStream = m_output.is();
}

Reference< XOutputStream > OMarkableOutputStream::getOutputStream(void)	throw (RuntimeException)
{
	return m_output;
}



void OMarkableOutputStream::setSuccessor( const Reference< XConnectable > &r )
	throw (RuntimeException)
{
     /// if the references match, nothing needs to be done
     if( m_succ != r ) {
         /// store the reference for later use
         m_succ = r;

         if( m_succ.is() ) {
              m_succ->setPredecessor( Reference < XConnectable > (
				  SAL_STATIC_CAST( XConnectable * , this  ) ) );
         }
     }
}
Reference <XConnectable > OMarkableOutputStream::getSuccessor()		throw (RuntimeException)
{
	return m_succ;
}


// XDataSource
void OMarkableOutputStream::setPredecessor( const Reference< XConnectable > &r )
	throw (RuntimeException)
{
	if( r != m_pred ) {
		m_pred = r;
		if( m_pred.is() ) {
			m_pred->setSuccessor( Reference < XConnectable > (
				SAL_STATIC_CAST ( XConnectable * , this ) ) );
		}
	}
}
Reference < XConnectable > OMarkableOutputStream::getPredecessor() throw (RuntimeException)
{
	return m_pred;
}


// private methods

void OMarkableOutputStream::checkMarksAndFlush() throw( 	NotConnectedException,
															BufferSizeExceededException)
{
	map<sal_Int32,sal_Int32,less<sal_Int32> >::iterator ii;

	// find the smallest mark
	sal_Int32 nNextFound = m_nCurrentPos;
	for( ii = m_mapMarks.begin() ; ii != m_mapMarks.end() ; ii ++ ) {
		if( (*ii).second <= nNextFound )  {
			nNextFound = (*ii).second;
		}
	}

	if( nNextFound ) {
		// some data must be released !
		m_nCurrentPos -= nNextFound;
		for( ii = m_mapMarks.begin() ; ii != m_mapMarks.end() ; ii ++ ) {
			(*ii).second -= nNextFound;
		}

		Sequence<sal_Int8> seq(nNextFound);
		m_pBuffer->readAt( 0 , seq , nNextFound );
		m_pBuffer->forgetFromStart( nNextFound );

		// now write data through to streams
		m_output->writeBytes( seq );
	}
	else {
		// nothing to do. There is a mark or the current cursor position, that prevents
		// releasing data !
	}
}


// XServiceInfo
OUString OMarkableOutputStream::getImplementationName() throw ()
{
    return OMarkableOutputStream_getImplementationName();
}

// XServiceInfo
sal_Bool OMarkableOutputStream::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;
}

// XServiceInfo
Sequence< OUString > OMarkableOutputStream::getSupportedServiceNames(void) throw ()
{
    return OMarkableOutputStream_getSupportedServiceNames();
}




/*------------------------
*
* external binding
*
*------------------------*/
Reference< XInterface > SAL_CALL OMarkableOutputStream_CreateInstance( const Reference < XComponentContext > & ) throw(Exception)
{
	OMarkableOutputStream *p = new OMarkableOutputStream( );

	return Reference < XInterface > ( ( OWeakObject * ) p );
}

OUString 	OMarkableOutputStream_getImplementationName()
{
	return OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.comp.io.stm.MarkableOutputStream" ));
}

Sequence<OUString> OMarkableOutputStream_getSupportedServiceNames(void)
{
	Sequence<OUString> aRet(1);
	aRet.getArray()[0] = OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.io.MarkableOutputStream" ) );

	return aRet;
}






//------------------------------------------------
//
// XMarkableInputStream
//
//------------------------------------------------

class OMarkableInputStream :
	public WeakImplHelper5
    <
			 XInputStream,
			 XActiveDataSink,
			 XMarkableStream,
			 XConnectable,
			 XServiceInfo
    >
{
public:
	OMarkableInputStream(  );
	~OMarkableInputStream();


public: // XInputStream
    virtual sal_Int32 SAL_CALL readBytes(Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead)
		throw (	NotConnectedException,
				BufferSizeExceededException,
				RuntimeException) ;
    virtual sal_Int32 SAL_CALL readSomeBytes(Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead)
		throw (	NotConnectedException,
				BufferSizeExceededException,
				RuntimeException);
    virtual void SAL_CALL skipBytes(sal_Int32 nBytesToSkip)
		throw (	NotConnectedException,
				BufferSizeExceededException,
				RuntimeException);

    virtual sal_Int32 SAL_CALL available(void)
		throw ( NotConnectedException,
				RuntimeException);
    virtual void SAL_CALL closeInput(void) throw (NotConnectedException, RuntimeException);

public: // XMarkable
    virtual sal_Int32 SAL_CALL createMark(void)
		throw (IOException, RuntimeException);
    virtual void SAL_CALL deleteMark(sal_Int32 Mark)
		throw (IOException, IllegalArgumentException, RuntimeException);
    virtual void SAL_CALL jumpToMark(sal_Int32 nMark)
		throw (IOException, IllegalArgumentException, RuntimeException);
    virtual void SAL_CALL jumpToFurthest(void)
		throw (IOException, RuntimeException);
    virtual sal_Int32 SAL_CALL offsetToMark(sal_Int32 nMark)
		throw (IOException, IllegalArgumentException,RuntimeException);

public: // XActiveDataSink
    virtual void SAL_CALL setInputStream(const Reference < XInputStream > & aStream)
		throw (RuntimeException);
    virtual Reference < XInputStream > SAL_CALL getInputStream(void)
		throw (RuntimeException);

public: // XConnectable
    virtual void SAL_CALL setPredecessor(const Reference < XConnectable > & aPredecessor)
		throw (RuntimeException);
    virtual Reference < XConnectable > SAL_CALL getPredecessor(void)
		throw (RuntimeException);
    virtual void SAL_CALL setSuccessor(const Reference < XConnectable > & aSuccessor)
		throw (RuntimeException);
    virtual Reference < XConnectable > SAL_CALL getSuccessor(void) throw (RuntimeException);

public: // XServiceInfo
    OUString                     SAL_CALL getImplementationName() throw ();
    Sequence< OUString >         SAL_CALL getSupportedServiceNames(void) throw ();
    sal_Bool                         SAL_CALL  supportsService(const OUString& ServiceName) throw ();

private:
	void checkMarksAndFlush();

	Reference < XConnectable > 	m_succ;
	Reference < XConnectable > 	m_pred;

	Reference< XInputStream > m_input;
	sal_Bool m_bValidStream;

	IRingBuffer *m_pBuffer;
	map<sal_Int32,sal_Int32,less< sal_Int32 > > m_mapMarks;
	sal_Int32 m_nCurrentPos;
	sal_Int32 m_nCurrentMark;

	Mutex m_mutex;
};

OMarkableInputStream::OMarkableInputStream()
{
	g_moduleCount.modCnt.acquire( &g_moduleCount.modCnt );
	m_nCurrentPos = 0;
	m_nCurrentMark = 0;
	m_pBuffer = new MemRingBuffer;
}


OMarkableInputStream::~OMarkableInputStream()
{
	if( m_pBuffer ) {
		delete m_pBuffer;
	}
	g_moduleCount.modCnt.release( &g_moduleCount.modCnt );
}




// XInputStream

sal_Int32 OMarkableInputStream::readBytes(Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead)
	throw (	NotConnectedException,
			BufferSizeExceededException,
			RuntimeException)
{
	sal_Int32 nBytesRead;

	if( m_bValidStream ) {
		MutexGuard guard( m_mutex );
		if( m_mapMarks.empty() && ! m_pBuffer->getSize() ) {
			// normal read !
			nBytesRead = m_input->readBytes( aData, nBytesToRead );
		}
		else {
			// read from buffer
			sal_Int32 nRead;

			// read enough bytes into buffer
			if( m_pBuffer->getSize() - m_nCurrentPos < nBytesToRead  ) {
				sal_Int32 nToRead = nBytesToRead - ( m_pBuffer->getSize() - m_nCurrentPos );
				nRead = m_input->readBytes( aData , nToRead );

				OSL_ASSERT( aData.getLength() == nRead );

				try
				{
					m_pBuffer->writeAt( m_pBuffer->getSize() , aData );
				}
				catch( IRingBuffer_OutOfMemoryException & ) {
					throw BufferSizeExceededException();
				}
				catch( IRingBuffer_OutOfBoundsException & ) {
					throw BufferSizeExceededException();
				}

				if( nRead < nToRead ) {
					nBytesToRead = nBytesToRead - (nToRead-nRead);
				}
			}

			OSL_ASSERT( m_pBuffer->getSize() - m_nCurrentPos >= nBytesToRead  );

			m_pBuffer->readAt( m_nCurrentPos , aData , nBytesToRead );

			m_nCurrentPos += nBytesToRead;
			nBytesRead = nBytesToRead;
		}
	}
	else {
		throw NotConnectedException(
            OUString( RTL_CONSTASCII_USTRINGPARAM("MarkableInputStream::readBytes NotConnectedException")) ,
            *this );
	}
	return nBytesRead;
}


sal_Int32 OMarkableInputStream::readSomeBytes(Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead)
	throw (	NotConnectedException,
			BufferSizeExceededException,
			RuntimeException)
{

	sal_Int32 nBytesRead;
	if( m_bValidStream ) {
		MutexGuard guard( m_mutex );
		if( m_mapMarks.empty() && ! m_pBuffer->getSize() ) {
			// normal read !
			nBytesRead = m_input->readSomeBytes( aData, nMaxBytesToRead );
		}
		else {
			// read from buffer
			sal_Int32 nRead = 0;
			sal_Int32 nInBuffer = m_pBuffer->getSize() - m_nCurrentPos;
			sal_Int32 nAdditionalBytesToRead = Min(nMaxBytesToRead-nInBuffer,m_input->available());
			nAdditionalBytesToRead = Max(0 , nAdditionalBytesToRead );
			
			// read enough bytes into buffer 
			if( 0 == nInBuffer ) {
				nRead = m_input->readSomeBytes( aData , nMaxBytesToRead );	
			}
			else if( nAdditionalBytesToRead ) {
				nRead = m_input->readBytes( aData , nAdditionalBytesToRead );				
			}
			
			if( nRead ) {
				aData.realloc( nRead );
				try
				{
					m_pBuffer->writeAt( m_pBuffer->getSize() , aData );	
				}
				catch( IRingBuffer_OutOfMemoryException & )
				{
					throw BufferSizeExceededException();
				}
				catch( IRingBuffer_OutOfBoundsException &  )
				{
					throw BufferSizeExceededException();
				}
			}
			
			nBytesRead = Min( nMaxBytesToRead , nInBuffer + nRead );
			
			// now take everything from buffer !
			m_pBuffer->readAt( m_nCurrentPos , aData , nBytesRead );

			m_nCurrentPos += nBytesRead;
		}
	}
	else
	{
		throw NotConnectedException(
            OUString( RTL_CONSTASCII_USTRINGPARAM("MarkableInputStream::readSomeBytes NotConnectedException")) ,
            *this );	
	}
	return nBytesRead;


}


void OMarkableInputStream::skipBytes(sal_Int32 nBytesToSkip)
	throw (	NotConnectedException, 
			BufferSizeExceededException, 
			RuntimeException)
{
	if ( nBytesToSkip < 0 )
		throw BufferSizeExceededException(
			::rtl::OUString::createFromAscii( "precondition not met: XInputStream::skipBytes: non-negative integer required!" ),
			*this
		);

	// this method is blocking 
	sal_Int32 nRead;
	Sequence<sal_Int8> seqDummy( nBytesToSkip );
	
	nRead = readBytes( seqDummy , nBytesToSkip );
}

sal_Int32 OMarkableInputStream::available(void) throw (NotConnectedException, RuntimeException)
{
	sal_Int32 nAvail;
	if( m_bValidStream ) {
		MutexGuard guard( m_mutex );
		nAvail = m_input->available() + ( m_pBuffer->getSize() - m_nCurrentPos );
	}
	else
	{
		throw NotConnectedException(
            OUString( RTL_CONSTASCII_USTRINGPARAM( "MarkableInputStream::available NotConnectedException" ) ) ,
            *this );
	}

	return nAvail;
}


void OMarkableInputStream::closeInput(void) throw (NotConnectedException, RuntimeException) 
{
	if( m_bValidStream ) {
		MutexGuard guard( m_mutex );

		m_input->closeInput();
 
		setInputStream( Reference< XInputStream > () );
		setPredecessor( Reference< XConnectable > () );
		setSuccessor( Reference< XConnectable >() );
		
		delete m_pBuffer;
		m_pBuffer = 0;
		m_nCurrentPos = 0;
		m_nCurrentMark = 0;
	}
	else {
		throw NotConnectedException(
            OUString( RTL_CONSTASCII_USTRINGPARAM( "MarkableInputStream::closeInput NotConnectedException" ) ) ,
            *this );
	}
}

// XMarkable

sal_Int32 OMarkableInputStream::createMark(void) 			throw (IOException, RuntimeException)
{
	MutexGuard guard( m_mutex );
	sal_Int32 nMark = m_nCurrentMark;
		
	m_mapMarks[nMark] = m_nCurrentPos;
	
	m_nCurrentMark ++;
	return nMark;
}

void OMarkableInputStream::deleteMark(sal_Int32 Mark) 		throw (IOException, IllegalArgumentException, RuntimeException)
{
	MutexGuard guard( m_mutex );
	map<sal_Int32,sal_Int32,less<sal_Int32> >::iterator ii = m_mapMarks.find( Mark );
	
	if( ii == m_mapMarks.end() ) {
        OUStringBuffer buf( 128 );
        buf.appendAscii( "MarkableInputStream::deleteMark unknown mark (" );
        buf.append( Mark );
        buf.appendAscii( ")");
	    throw IllegalArgumentException( buf.makeStringAndClear(), *this , 0 );
	}
	else {
		m_mapMarks.erase( ii );
		checkMarksAndFlush();		
	}		
}

void OMarkableInputStream::jumpToMark(sal_Int32 nMark)
	throw (IOException,
		   IllegalArgumentException, 
		   RuntimeException)
{
	MutexGuard guard( m_mutex );
	map<sal_Int32,sal_Int32,less<sal_Int32> >::iterator ii = m_mapMarks.find( nMark );
	
	if( ii == m_mapMarks.end() )
	{
        OUStringBuffer buf( 128 );
        buf.appendAscii( "MarkableInputStream::jumpToMark unknown mark (" );
        buf.append( nMark );
        buf.appendAscii( ")");
	    throw IllegalArgumentException( buf.makeStringAndClear(), *this , 0 );
	}
	else
	{
		m_nCurrentPos = (*ii).second;
	}	
}

void OMarkableInputStream::jumpToFurthest(void) 		throw (IOException, RuntimeException)
{
	MutexGuard guard( m_mutex );
	m_nCurrentPos = m_pBuffer->getSize();	
	checkMarksAndFlush();	
}

sal_Int32 OMarkableInputStream::offsetToMark(sal_Int32 nMark)
 	throw (IOException, 
		   IllegalArgumentException,
		   RuntimeException)
{
	MutexGuard guard( m_mutex );
	map<sal_Int32,sal_Int32,less<sal_Int32> >::const_iterator ii = m_mapMarks.find( nMark );
	
	if( ii == m_mapMarks.end() )
	{
        OUStringBuffer buf( 128 );
        buf.appendAscii( "MarkableInputStream::offsetToMark unknown mark (" );
        buf.append( nMark );
        buf.appendAscii( ")");
	    throw IllegalArgumentException( buf.makeStringAndClear(), *this , 0 );
	}
	return m_nCurrentPos - (*ii).second;
}







// XActiveDataSource
void OMarkableInputStream::setInputStream(const Reference< XInputStream > & aStream)
	throw (RuntimeException)
{
	
	if( m_input != aStream ) {
		m_input = aStream;
		
		Reference < XConnectable >  pred( m_input , UNO_QUERY );
		setPredecessor( pred );
	}
	
	m_bValidStream = m_input.is();	
	
}

Reference< XInputStream > OMarkableInputStream::getInputStream(void) throw (RuntimeException) 
{
	return m_input;
}



// XDataSink
void OMarkableInputStream::setSuccessor( const Reference< XConnectable > &r )
	throw (RuntimeException)
{
     /// if the references match, nothing needs to be done
     if( m_succ != r ) {
         /// store the reference for later use
         m_succ = r;

         if( m_succ.is() ) { 
              /// set this instance as the sink !
              m_succ->setPredecessor( Reference< XConnectable > (
				  SAL_STATIC_CAST( XConnectable * , this ) ) );
         }
     }
}

Reference < XConnectable >  OMarkableInputStream::getSuccessor() throw (RuntimeException)
{
	return m_succ;	
}


// XDataSource
void OMarkableInputStream::setPredecessor( const Reference < XConnectable >  &r )
	throw (RuntimeException)
{
	if( r != m_pred ) {
		m_pred = r;
		if( m_pred.is() ) {
			m_pred->setSuccessor( Reference< XConnectable > (
				SAL_STATIC_CAST( XConnectable * , this ) ) );
		}
	}	
}
Reference< XConnectable >  OMarkableInputStream::getPredecessor() throw (RuntimeException)
{
	return m_pred;
}




void OMarkableInputStream::checkMarksAndFlush()
{
	map<sal_Int32,sal_Int32,less<sal_Int32> >::iterator ii;
	
	// find the smallest mark
	sal_Int32 nNextFound = m_nCurrentPos;
	for( ii = m_mapMarks.begin() ; ii != m_mapMarks.end() ; ii ++ ) {
		if( (*ii).second <= nNextFound )  {
			nNextFound = (*ii).second;	
		}
	}

	if( nNextFound ) {
		// some data must be released !
		m_nCurrentPos -= nNextFound;
		for( ii = m_mapMarks.begin() ; ii != m_mapMarks.end() ; ii ++ ) {
			(*ii).second -= nNextFound;
		}
				
		m_pBuffer->forgetFromStart( nNextFound );

	}
	else {
		// nothing to do. There is a mark or the current cursor position, that prevents
		// releasing data !
	}		
}



// XServiceInfo
OUString OMarkableInputStream::getImplementationName() throw () 
{
    return OMarkableInputStream_getImplementationName();
}

// XServiceInfo
sal_Bool OMarkableInputStream::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;
}

// XServiceInfo
Sequence< OUString > OMarkableInputStream::getSupportedServiceNames(void) throw () 
{
    return OMarkableInputStream_getSupportedServiceNames();
}


/*------------------------
*
* external binding
*
*------------------------*/
Reference < XInterface > SAL_CALL OMarkableInputStream_CreateInstance(
	const Reference < XComponentContext > & ) throw(Exception)
{	
	OMarkableInputStream *p = new OMarkableInputStream( ); 
	return Reference< XInterface > ( (OWeakObject * ) p );
}

OUString 	OMarkableInputStream_getImplementationName()
{
	return OUString(RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.comp.io.stm.MarkableInputStream" ));
}

Sequence<OUString> OMarkableInputStream_getSupportedServiceNames(void)
{
	Sequence<OUString> aRet(1);
	aRet.getArray()[0] = OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.io.MarkableInputStream" ));
	return aRet;
}

}
