/**************************************************************
 * 
 * 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_comphelper.hxx"

// includes --------------------------------------------------------------
#include <comphelper/accessibletexthelper.hxx>
#include <com/sun/star/accessibility/AccessibleTextType.hpp>
#include <com/sun/star/i18n/CharacterIteratorMode.hpp>
#ifndef _COM_SUN_STAR_TEXT_WORDTYPE_HPP_
#include <com/sun/star/i18n/WordType.hpp>
#endif
#include <com/sun/star/i18n/KCharacterType.hpp>
#include <comphelper/processfactory.hxx>
#include <com/sun/star/accessibility/TextSegment.hpp>

#include <algorithm>

//..............................................................................
namespace comphelper
{
//..............................................................................

	using namespace ::com::sun::star;
	using namespace ::com::sun::star::uno;
	using namespace ::com::sun::star::lang;
	using namespace ::com::sun::star::beans;
	using namespace ::com::sun::star::accessibility;

	//==============================================================================
	// OCommonAccessibleText
	//==============================================================================

	OCommonAccessibleText::OCommonAccessibleText()
	{
	}

	// -----------------------------------------------------------------------------

	OCommonAccessibleText::~OCommonAccessibleText()
	{
	}

	// -----------------------------------------------------------------------------

	Reference < i18n::XBreakIterator > OCommonAccessibleText::implGetBreakIterator()
	{
		if ( !m_xBreakIter.is() )
		{
			Reference< lang::XMultiServiceFactory > xMSF = ::comphelper::getProcessServiceFactory();
			if ( xMSF.is() )
			{
				m_xBreakIter = Reference< i18n::XBreakIterator >
					( xMSF->createInstance( ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.i18n.BreakIterator" ) ) ), UNO_QUERY );
			}
		}

		return m_xBreakIter;
	}

	// -----------------------------------------------------------------------------

	Reference < i18n::XCharacterClassification > OCommonAccessibleText::implGetCharacterClassification()
	{
		if ( !m_xCharClass.is() )
		{
			Reference< lang::XMultiServiceFactory > xMSF = ::comphelper::getProcessServiceFactory();
			if ( xMSF.is() )
			{
				m_xCharClass = Reference< i18n::XCharacterClassification >
					( xMSF->createInstance( ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.i18n.CharacterClassification" ) ) ), UNO_QUERY );
			}
		}

		return m_xCharClass;
	}

	// -----------------------------------------------------------------------------

	sal_Bool OCommonAccessibleText::implIsValidBoundary( i18n::Boundary& rBoundary, sal_Int32 nLength )
	{
		return ( rBoundary.startPos >= 0 ) && ( rBoundary.startPos < nLength ) && ( rBoundary.endPos >= 0 ) && ( rBoundary.endPos <= nLength );
	}

	// -----------------------------------------------------------------------------

	sal_Bool OCommonAccessibleText::implIsValidIndex( sal_Int32 nIndex, sal_Int32 nLength )
	{
		return ( nIndex >= 0 ) && ( nIndex < nLength );
	}

	// -----------------------------------------------------------------------------

	sal_Bool OCommonAccessibleText::implIsValidRange( sal_Int32 nStartIndex, sal_Int32 nEndIndex, sal_Int32 nLength )
	{
		return ( nStartIndex >= 0 ) && ( nStartIndex <= nLength ) && ( nEndIndex >= 0 ) && ( nEndIndex <= nLength );
	}

	// -----------------------------------------------------------------------------

	void OCommonAccessibleText::implGetGlyphBoundary( i18n::Boundary& rBoundary, sal_Int32 nIndex )
	{
		::rtl::OUString sText( implGetText() );

		if ( implIsValidIndex( nIndex, sText.getLength() ) )
		{
			Reference < i18n::XBreakIterator > xBreakIter = implGetBreakIterator();
			if ( xBreakIter.is() )
			{
				sal_Int32 nCount = 1;
				sal_Int32 nDone;
				sal_Int32 nStartIndex = xBreakIter->previousCharacters( sText, nIndex, implGetLocale(), i18n::CharacterIteratorMode::SKIPCELL, nCount, nDone );
				if ( nDone != 0 )
					nStartIndex = xBreakIter->nextCharacters( sText, nStartIndex, implGetLocale(), i18n::CharacterIteratorMode::SKIPCELL, nCount, nDone );
				sal_Int32 nEndIndex = xBreakIter->nextCharacters( sText, nStartIndex, implGetLocale(), i18n::CharacterIteratorMode::SKIPCELL, nCount, nDone );
				if ( nDone != 0 )
				{
					rBoundary.startPos = nStartIndex;
					rBoundary.endPos = nEndIndex;
				}
			}
		}
		else
		{
			rBoundary.startPos = nIndex;
			rBoundary.endPos = nIndex;
		}
	}

	// -----------------------------------------------------------------------------

	sal_Bool OCommonAccessibleText::implGetWordBoundary( i18n::Boundary& rBoundary, sal_Int32 nIndex )
	{
		sal_Bool bWord = sal_False;
		::rtl::OUString sText( implGetText() );

		if ( implIsValidIndex( nIndex, sText.getLength() ) )
		{
			Reference < i18n::XBreakIterator > xBreakIter = implGetBreakIterator();
			if ( xBreakIter.is() )
			{
				rBoundary = xBreakIter->getWordBoundary( sText, nIndex, implGetLocale(), i18n::WordType::ANY_WORD, sal_True );

				// it's a word, if the first character is an alpha-numeric character
				Reference< i18n::XCharacterClassification >	xCharClass = implGetCharacterClassification();
				if ( xCharClass.is() )
				{
					sal_Int32 nType = xCharClass->getCharacterType( sText, rBoundary.startPos, implGetLocale() );
					if ( ( nType & ( i18n::KCharacterType::LETTER | i18n::KCharacterType::DIGIT ) ) != 0 )
						bWord = sal_True;
				}
			}
		}
		else
		{
			rBoundary.startPos = nIndex;
			rBoundary.endPos = nIndex;
		}

		return bWord;
	}

	// -----------------------------------------------------------------------------

	void OCommonAccessibleText::implGetSentenceBoundary( i18n::Boundary& rBoundary, sal_Int32 nIndex )
	{
		::rtl::OUString sText( implGetText() );

		if ( implIsValidIndex( nIndex, sText.getLength() ) )
		{
			Locale aLocale = implGetLocale();
			Reference < i18n::XBreakIterator > xBreakIter = implGetBreakIterator();		
			if ( xBreakIter.is() )
			{
				rBoundary.endPos = xBreakIter->endOfSentence( sText, nIndex, aLocale );
				rBoundary.startPos = xBreakIter->beginOfSentence( sText, rBoundary.endPos, aLocale );
			}
		}
		else
		{
			rBoundary.startPos = nIndex;
			rBoundary.endPos = nIndex;
		}
	}

	// -----------------------------------------------------------------------------

	void OCommonAccessibleText::implGetParagraphBoundary( i18n::Boundary& rBoundary, sal_Int32 nIndex )
	{
		::rtl::OUString sText( implGetText() );

		if ( implIsValidIndex( nIndex, sText.getLength() ) )
		{
			rBoundary.startPos = 0;
			rBoundary.endPos = sText.getLength();

			sal_Int32 nFound = sText.lastIndexOf( (sal_Unicode)'\n', nIndex );
			if ( nFound != -1 )
				rBoundary.startPos = nFound + 1;

			nFound = sText.indexOf( (sal_Unicode)'\n', nIndex );
			if ( nFound != -1 )
				rBoundary.endPos = nFound + 1;
		}
		else
		{
			rBoundary.startPos = nIndex;
			rBoundary.endPos = nIndex;
		}
	}

	// -----------------------------------------------------------------------------

	void OCommonAccessibleText::implGetLineBoundary( i18n::Boundary& rBoundary, sal_Int32 nIndex )
	{
		::rtl::OUString sText( implGetText() );
		sal_Int32 nLength = sText.getLength();

		if ( implIsValidIndex( nIndex, nLength ) || nIndex == nLength )
		{		
			rBoundary.startPos = 0;
			rBoundary.endPos = nLength;
		}
		else
		{
			rBoundary.startPos = nIndex;
			rBoundary.endPos = nIndex;
		}
	}

	// -----------------------------------------------------------------------------

	sal_Unicode OCommonAccessibleText::getCharacter( sal_Int32 nIndex ) throw (IndexOutOfBoundsException, RuntimeException)
	{
		::rtl::OUString sText( implGetText() );

		if ( !implIsValidIndex( nIndex, sText.getLength() ) )
			throw IndexOutOfBoundsException();

		return sText.getStr()[nIndex];
	}

	// -----------------------------------------------------------------------------

	sal_Int32 OCommonAccessibleText::getCharacterCount() throw (RuntimeException)
	{
		return implGetText().getLength();
	}

	// -----------------------------------------------------------------------------

	::rtl::OUString OCommonAccessibleText::getSelectedText() throw (RuntimeException)
	{
		::rtl::OUString sText;
		sal_Int32 nStartIndex;
		sal_Int32 nEndIndex;

		implGetSelection( nStartIndex, nEndIndex );

		try
		{
			sText = getTextRange( nStartIndex, nEndIndex );
		}
		catch ( IndexOutOfBoundsException& )
		{
		}

		return sText;
	}

	// -----------------------------------------------------------------------------

	sal_Int32 OCommonAccessibleText::getSelectionStart() throw (RuntimeException)
	{
		sal_Int32 nStartIndex;
		sal_Int32 nEndIndex;

		implGetSelection( nStartIndex, nEndIndex );

		return nStartIndex;
	}

	// -----------------------------------------------------------------------------

	sal_Int32 OCommonAccessibleText::getSelectionEnd() throw (RuntimeException)
	{
		sal_Int32 nStartIndex;
		sal_Int32 nEndIndex;

		implGetSelection( nStartIndex, nEndIndex );

		return nEndIndex;
	}

	// -----------------------------------------------------------------------------

	::rtl::OUString OCommonAccessibleText::getText() throw (RuntimeException)
	{
		return implGetText();
	}

	// -----------------------------------------------------------------------------

	::rtl::OUString OCommonAccessibleText::getTextRange( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) throw (IndexOutOfBoundsException, RuntimeException)
	{
		::rtl::OUString sText( implGetText() );

		if ( !implIsValidRange( nStartIndex, nEndIndex, sText.getLength() ) )
			throw IndexOutOfBoundsException();

		sal_Int32 nMinIndex = ::std::min( nStartIndex, nEndIndex );
		sal_Int32 nMaxIndex = ::std::max( nStartIndex, nEndIndex );

		return sText.copy( nMinIndex, nMaxIndex - nMinIndex );
	}

	// -----------------------------------------------------------------------------

    TextSegment OCommonAccessibleText::getTextAtIndex( sal_Int32 nIndex, sal_Int16 aTextType ) throw (IndexOutOfBoundsException, IllegalArgumentException, RuntimeException)
	{
		::rtl::OUString sText( implGetText() );
		sal_Int32 nLength = sText.getLength();

		if ( !implIsValidIndex( nIndex, nLength ) && nIndex != nLength )
			throw IndexOutOfBoundsException();

		i18n::Boundary aBoundary;
		TextSegment aResult;
		aResult.SegmentStart = -1;
		aResult.SegmentEnd = -1;

		switch ( aTextType )
		{
			case AccessibleTextType::CHARACTER:
			{
				if ( implIsValidIndex( nIndex, nLength ) )
				{
					aResult.SegmentText = sText.copy( nIndex, 1 );
					aResult.SegmentStart = nIndex;
					aResult.SegmentEnd = nIndex+1;
				}
			}
			break;
			case AccessibleTextType::GLYPH:
			{
				// get glyph at index
				implGetGlyphBoundary( aBoundary, nIndex );
				if ( implIsValidBoundary( aBoundary, nLength ) )
			    {
					aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
					aResult.SegmentStart = aBoundary.startPos;
					aResult.SegmentEnd = aBoundary.endPos;
				}
			}
			break;
			case AccessibleTextType::WORD:
			{	
				// get word at index
				sal_Bool bWord = implGetWordBoundary( aBoundary, nIndex );
				if ( bWord && implIsValidBoundary( aBoundary, nLength ) )
				{
					aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
					aResult.SegmentStart = aBoundary.startPos;
					aResult.SegmentEnd = aBoundary.endPos;
				}
			}
			break;
			case AccessibleTextType::SENTENCE:
			{
				// get sentence at index
				implGetSentenceBoundary( aBoundary, nIndex );
				if ( implIsValidBoundary( aBoundary, nLength ) )
				{
					aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
					aResult.SegmentStart = aBoundary.startPos;
					aResult.SegmentEnd = aBoundary.endPos;
				}
			}
			break;
			case AccessibleTextType::PARAGRAPH:
			{
				// get paragraph at index
				implGetParagraphBoundary( aBoundary, nIndex );
				if ( implIsValidBoundary( aBoundary, nLength ) )
				{
					aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
					aResult.SegmentStart = aBoundary.startPos;
					aResult.SegmentEnd = aBoundary.endPos;
				}
			}
			break;
			case AccessibleTextType::LINE:
			{
				// get line at index
				implGetLineBoundary( aBoundary, nIndex );
				if ( implIsValidBoundary( aBoundary, nLength ) )
				{
					aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
					aResult.SegmentStart = aBoundary.startPos;
					aResult.SegmentEnd = aBoundary.endPos;
				}
			}
			break;
            case AccessibleTextType::ATTRIBUTE_RUN:
            {
                // TODO: implGetAttributeRunBoundary() (incompatible!)

                aResult.SegmentText = sText;
                aResult.SegmentStart = 0;
                aResult.SegmentEnd = nLength;
            }
            break;
			default:
			{
				// unknown text type
			}
		}

		return aResult;
	}

	// -----------------------------------------------------------------------------

    TextSegment OCommonAccessibleText::getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 aTextType ) throw (IndexOutOfBoundsException, IllegalArgumentException, RuntimeException)
	{
		::rtl::OUString sText( implGetText() );
		sal_Int32 nLength = sText.getLength();

		if ( !implIsValidIndex( nIndex, nLength ) && nIndex != nLength )
			throw IndexOutOfBoundsException();

		i18n::Boundary aBoundary;
		TextSegment aResult;
		aResult.SegmentStart = -1;
		aResult.SegmentEnd = -1;

		switch ( aTextType )
		{
			case AccessibleTextType::CHARACTER:
			{
				if ( implIsValidIndex( nIndex - 1, nLength ) )
				{
					aResult.SegmentText = sText.copy( nIndex - 1, 1 );
					aResult.SegmentStart = nIndex-1;
					aResult.SegmentEnd = nIndex;
				}
			}
			break;
			case AccessibleTextType::GLYPH:
			{
				// get glyph at index
				implGetGlyphBoundary( aBoundary, nIndex );
				// get previous glyph
				if ( aBoundary.startPos > 0 )
				{
					implGetGlyphBoundary( aBoundary, aBoundary.startPos - 1 );
					if ( implIsValidBoundary( aBoundary, nLength ) )
					{
						aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
    					aResult.SegmentStart = aBoundary.startPos;
    					aResult.SegmentEnd = aBoundary.endPos;
					}
				}
			}
			break;
			case AccessibleTextType::WORD:
			{
				// get word at index
				implGetWordBoundary( aBoundary, nIndex );
				// get previous word
				sal_Bool bWord = sal_False;
				while ( !bWord && aBoundary.startPos > 0 )
					bWord = implGetWordBoundary( aBoundary, aBoundary.startPos - 1 );
				if ( bWord && implIsValidBoundary( aBoundary, nLength ) )
				{
					aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
					aResult.SegmentStart = aBoundary.startPos;
					aResult.SegmentEnd = aBoundary.endPos;
				}
			}
			break;
			case AccessibleTextType::SENTENCE:
			{
				// get sentence at index
				implGetSentenceBoundary( aBoundary, nIndex );
				// get previous sentence
				if ( aBoundary.startPos > 0 )
				{
					implGetSentenceBoundary( aBoundary, aBoundary.startPos - 1 );
					if ( implIsValidBoundary( aBoundary, nLength ) )
					{
						aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
    					aResult.SegmentStart = aBoundary.startPos;
    					aResult.SegmentEnd = aBoundary.endPos;
					}
				}
			}
			break;
			case AccessibleTextType::PARAGRAPH:
			{
				// get paragraph at index
				implGetParagraphBoundary( aBoundary, nIndex );
				// get previous paragraph
				if ( aBoundary.startPos > 0 )
				{
					implGetParagraphBoundary( aBoundary, aBoundary.startPos - 1 );
					if ( implIsValidBoundary( aBoundary, nLength ) )
					{
						aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
    					aResult.SegmentStart = aBoundary.startPos;
    					aResult.SegmentEnd = aBoundary.endPos;
					}
				}
			}
			break;
			case AccessibleTextType::LINE:
			{
				// get line at index
				implGetLineBoundary( aBoundary, nIndex );
				// get previous line
				if ( aBoundary.startPos > 0 )
				{
					implGetLineBoundary( aBoundary, aBoundary.startPos - 1 );
					if ( implIsValidBoundary( aBoundary, nLength ) )
					{
						aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
    					aResult.SegmentStart = aBoundary.startPos;
    					aResult.SegmentEnd = aBoundary.endPos;
					}
				}
			}
			break;
            case AccessibleTextType::ATTRIBUTE_RUN:
            {
                // TODO: implGetAttributeRunBoundary() (incompatible!)
            }
            break;
			default:
			{
				// unknown text type
			}
		}

		return aResult;
	}

	// -----------------------------------------------------------------------------

    TextSegment OCommonAccessibleText::getTextBehindIndex( sal_Int32 nIndex, sal_Int16 aTextType ) throw (IndexOutOfBoundsException, IllegalArgumentException, RuntimeException)
	{
		::rtl::OUString sText( implGetText() );
		sal_Int32 nLength = sText.getLength();

		if ( !implIsValidIndex( nIndex, nLength ) && nIndex != nLength )
			throw IndexOutOfBoundsException();

		i18n::Boundary aBoundary;
		TextSegment aResult;
		aResult.SegmentStart = -1;
		aResult.SegmentEnd = -1;

		switch ( aTextType )
		{
			case AccessibleTextType::CHARACTER:
			{
				if ( implIsValidIndex( nIndex + 1, nLength ) )
			    {
					aResult.SegmentText = sText.copy( nIndex + 1, 1 );
					aResult.SegmentStart = nIndex+1;
					aResult.SegmentEnd = nIndex+2;
				}
			}
			break;
			case AccessibleTextType::GLYPH:
			{
				// get glyph at index
				implGetGlyphBoundary( aBoundary, nIndex );
				// get next glyph
				if ( aBoundary.endPos < nLength )
				{
					implGetGlyphBoundary( aBoundary, aBoundary.endPos );
					if ( implIsValidBoundary( aBoundary, nLength ) )
					{
						aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
    					aResult.SegmentStart = aBoundary.startPos;
    					aResult.SegmentEnd = aBoundary.endPos;
					}
				}							
			}
			break;
			case AccessibleTextType::WORD:
			{
				// get word at index
				implGetWordBoundary( aBoundary, nIndex );
				// get next word
				sal_Bool bWord = sal_False;
				while ( !bWord && aBoundary.endPos < nLength )
					bWord = implGetWordBoundary( aBoundary, aBoundary.endPos );
				if ( bWord && implIsValidBoundary( aBoundary, nLength ) )
				{
					aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
					aResult.SegmentStart = aBoundary.startPos;
					aResult.SegmentEnd = aBoundary.endPos;
				}
			}
			break;
			case AccessibleTextType::SENTENCE:
			{
				// get sentence at index
				implGetSentenceBoundary( aBoundary, nIndex );
				// get next sentence
				sal_Int32 nEnd = aBoundary.endPos;
				sal_Int32 nI = aBoundary.endPos;
				sal_Bool bFound = sal_False;
				while ( !bFound && ++nI < nLength )
				{
					implGetSentenceBoundary( aBoundary, nI );
					bFound = ( aBoundary.endPos > nEnd );
				}
				if ( bFound && implIsValidBoundary( aBoundary, nLength ) )
				{
					aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
					aResult.SegmentStart = aBoundary.startPos;
					aResult.SegmentEnd = aBoundary.endPos;
				}
			}
			break;
			case AccessibleTextType::PARAGRAPH:
			{
				// get paragraph at index
				implGetParagraphBoundary( aBoundary, nIndex );
				// get next paragraph
				if ( aBoundary.endPos < nLength )
				{
					implGetParagraphBoundary( aBoundary, aBoundary.endPos );
					if ( implIsValidBoundary( aBoundary, nLength ) )
					{
						aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
    					aResult.SegmentStart = aBoundary.startPos;
    					aResult.SegmentEnd = aBoundary.endPos;
					}
				}
			}
			break;
			case AccessibleTextType::LINE:
			{
				// get line at index
				implGetLineBoundary( aBoundary, nIndex );
				// get next line
				if ( aBoundary.endPos < nLength )
				{
					implGetLineBoundary( aBoundary, aBoundary.endPos );
					if ( implIsValidBoundary( aBoundary, nLength ) )
					{
						aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
    					aResult.SegmentStart = aBoundary.startPos;
    					aResult.SegmentEnd = aBoundary.endPos;
					}
				}
			}
			break;
            case AccessibleTextType::ATTRIBUTE_RUN:
            {
                // TODO: implGetAttributeRunBoundary() (incompatible!)
            }
            break;
			default:
			{
				// unknown text type
			}
		}

		return aResult;
	}

    // -----------------------------------------------------------------------------
    bool OCommonAccessibleText::implInitTextChangedEvent(
        const rtl::OUString& rOldString, 
        const rtl::OUString& rNewString, 		
        ::com::sun::star::uno::Any& rDeleted,
        ::com::sun::star::uno::Any& rInserted) // throw()
    {
        sal_uInt32 nLenOld = rOldString.getLength();
        sal_uInt32 nLenNew = rNewString.getLength();
                
        // equal
        if ((0 == nLenOld) && (0 == nLenNew))
            return false;

        TextSegment aDeletedText;
        TextSegment aInsertedText;                                
        
        aDeletedText.SegmentStart = -1;
        aDeletedText.SegmentEnd = -1;        
        aInsertedText.SegmentStart = -1;
        aInsertedText.SegmentEnd = -1;
        
        // insert only        
        if ((0 == nLenOld) && (nLenNew > 0))
        {
            aInsertedText.SegmentStart = 0;
            aInsertedText.SegmentEnd = nLenNew;
            aInsertedText.SegmentText = rNewString.copy( aInsertedText.SegmentStart, aInsertedText.SegmentEnd - aInsertedText.SegmentStart );

            rInserted <<= aInsertedText;            
            return true;
        }
            
        // delete only
        if ((nLenOld > 0) && (0 == nLenNew))
        {
            aDeletedText.SegmentStart = 0;
            aDeletedText.SegmentEnd = nLenOld;
            aDeletedText.SegmentText = rOldString.copy( aDeletedText.SegmentStart, aDeletedText.SegmentEnd - aDeletedText.SegmentStart );

            rDeleted <<= aDeletedText;            
            return true;
        }
        
        const sal_Unicode* pFirstDiffOld = rOldString.getStr();
        const sal_Unicode* pLastDiffOld  = rOldString.getStr() + nLenOld;    
        const sal_Unicode* pFirstDiffNew = rNewString.getStr();
        const sal_Unicode* pLastDiffNew  = rNewString.getStr() + nLenNew;
        
        // find first difference
        while ((*pFirstDiffOld == *pFirstDiffNew) && 
               (pFirstDiffOld  <  pLastDiffOld) && 
               (pFirstDiffNew  <  pLastDiffNew))
        {
            pFirstDiffOld++;
            pFirstDiffNew++;        
        }    
        
        // equality test
        if ((0 == *pFirstDiffOld) && (0 == *pFirstDiffNew))
            return false;
        
        // find last difference
        while ( ( pLastDiffOld > pFirstDiffOld) &&
                ( pLastDiffNew > pFirstDiffNew) &&
                (pLastDiffOld[-1]  == pLastDiffNew[-1]))
        {
            pLastDiffOld--;
            pLastDiffNew--;
        }
                
        if (pFirstDiffOld < pLastDiffOld)
        {        
            aDeletedText.SegmentStart = pFirstDiffOld - rOldString.getStr(); 
            aDeletedText.SegmentEnd = pLastDiffOld  - rOldString.getStr();
            aDeletedText.SegmentText = rOldString.copy( aDeletedText.SegmentStart, aDeletedText.SegmentEnd - aDeletedText.SegmentStart );            
            
            rDeleted <<= aDeletedText;
        }
        
        if (pFirstDiffNew < pLastDiffNew)
        {
            aInsertedText.SegmentStart = pFirstDiffNew - rNewString.getStr();
            aInsertedText.SegmentEnd = pLastDiffNew  - rNewString.getStr();
            aInsertedText.SegmentText = rNewString.copy( aInsertedText.SegmentStart, aInsertedText.SegmentEnd - aInsertedText.SegmentStart );
            
            rInserted <<= aInsertedText;
        }
        return true;
    }
                    
	//==============================================================================
	// OAccessibleTextHelper
	//==============================================================================

	OAccessibleTextHelper::OAccessibleTextHelper()
	{
	}

	// -----------------------------------------------------------------------------

	OAccessibleTextHelper::OAccessibleTextHelper( IMutex* _pExternalLock )
		:OAccessibleExtendedComponentHelper( _pExternalLock )
	{
	}

	// -----------------------------------------------------------------------------
	// XInterface
	// -----------------------------------------------------------------------------

	IMPLEMENT_FORWARD_XINTERFACE2( OAccessibleTextHelper, OAccessibleExtendedComponentHelper, OAccessibleTextHelper_Base )

	// -----------------------------------------------------------------------------
	// XTypeProvider
	// -----------------------------------------------------------------------------

	IMPLEMENT_FORWARD_XTYPEPROVIDER2( OAccessibleTextHelper, OAccessibleExtendedComponentHelper, OAccessibleTextHelper_Base )

	// -----------------------------------------------------------------------------
	// XAccessibleText
	// -----------------------------------------------------------------------------

	sal_Unicode OAccessibleTextHelper::getCharacter( sal_Int32 nIndex ) throw (IndexOutOfBoundsException, RuntimeException)
	{
		OExternalLockGuard aGuard( this );
		
		return OCommonAccessibleText::getCharacter( nIndex );		
	}

	// -----------------------------------------------------------------------------

	sal_Int32 OAccessibleTextHelper::getCharacterCount() throw (RuntimeException)
	{
		OExternalLockGuard aGuard( this );
		
		return OCommonAccessibleText::getCharacterCount();		
	}

	// -----------------------------------------------------------------------------

	::rtl::OUString OAccessibleTextHelper::getSelectedText() throw (RuntimeException)
	{
		OExternalLockGuard aGuard( this );
		
		return OCommonAccessibleText::getSelectedText();				
	}

	// -----------------------------------------------------------------------------

	sal_Int32 OAccessibleTextHelper::getSelectionStart() throw (RuntimeException)
	{
		OExternalLockGuard aGuard( this );
		
		return OCommonAccessibleText::getSelectionStart();
	}

	// -----------------------------------------------------------------------------

	sal_Int32 OAccessibleTextHelper::getSelectionEnd() throw (RuntimeException)
	{
		OExternalLockGuard aGuard( this );
		
		return OCommonAccessibleText::getSelectionEnd();	
	}

	// -----------------------------------------------------------------------------

	::rtl::OUString OAccessibleTextHelper::getText() throw (RuntimeException)
	{
		OExternalLockGuard aGuard( this );
		
		return OCommonAccessibleText::getText();
	}

	// -----------------------------------------------------------------------------

	::rtl::OUString OAccessibleTextHelper::getTextRange( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) throw (IndexOutOfBoundsException, RuntimeException)
	{
		OExternalLockGuard aGuard( this );
		
		return OCommonAccessibleText::getTextRange( nStartIndex, nEndIndex );		
	}

	// -----------------------------------------------------------------------------

    TextSegment OAccessibleTextHelper::getTextAtIndex( sal_Int32 nIndex, sal_Int16 aTextType ) throw (IndexOutOfBoundsException, IllegalArgumentException, RuntimeException)
	{
		OExternalLockGuard aGuard( this );
		
		return OCommonAccessibleText::getTextAtIndex( nIndex, aTextType );
	}

	// -----------------------------------------------------------------------------

    TextSegment OAccessibleTextHelper::getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 aTextType ) throw (IndexOutOfBoundsException, IllegalArgumentException, RuntimeException)
	{
		OExternalLockGuard aGuard( this );
		
		return OCommonAccessibleText::getTextBeforeIndex( nIndex, aTextType );
	}

	// -----------------------------------------------------------------------------

    TextSegment OAccessibleTextHelper::getTextBehindIndex( sal_Int32 nIndex, sal_Int16 aTextType ) throw (IndexOutOfBoundsException, IllegalArgumentException, RuntimeException)
	{
		OExternalLockGuard aGuard( this );
		
		return OCommonAccessibleText::getTextBehindIndex( nIndex, aTextType );
	}

	// -----------------------------------------------------------------------------

//..............................................................................
}	// namespace comphelper
//..............................................................................
