/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: linguistic_dlistimp.cxx,v $
 *
 *  $Revision: 1.4 $
 *
 *  last change: $Author: vg $ $Date: 2006/04/07 13:16:16 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#include "dlistimp.hxx"
#include "dicimp.hxx"
#include "lngopt.hxx"

#ifndef _FSYS_HXX
#include <tools/fsys.hxx>
#endif
#ifndef _STREAM_HXX
#include <tools/stream.hxx>
#endif
#ifndef _URLOBJ_HXX
#include <tools/urlobj.hxx>
#endif
#ifndef INCLUDED_I18NPOOL_MSLANGID_HXX
#include <i18npool/mslangid.hxx>
#endif
#ifndef INCLUDED_SVTOOLS_PATHOPTIONS_HXX
#include <svtools/pathoptions.hxx>
#endif
#ifndef INCLUDED_SVTOOLS_USEROPTIONS_HXX
#include <svtools/useroptions.hxx>
#endif
#ifndef _SFXDOCFILE_HXX
#include <bf_sfx2/docfile.hxx>
#endif
#ifndef _SV_SVAPP_HXX
#include <vcl/svapp.hxx>
#endif
#include <cppuhelper/factory.hxx>	// helper for factories

#ifndef _UNOTOOLS_LOCALFILEHELPER_HXX
#include <unotools/localfilehelper.hxx>
#endif

#ifndef _COM_SUN_STAR_FRAME_XSTORABLE_HPP_
#include <com/sun/star/frame/XStorable.hpp>
#endif
#include <com/sun/star/lang/Locale.hpp>
#include <com/sun/star/uno/Reference.h>
#include <com/sun/star/linguistic2/DictionaryEventFlags.hpp>
#include <com/sun/star/linguistic2/DictionaryListEventFlags.hpp>
#include <com/sun/star/registry/XRegistryKey.hpp>
namespace binfilter {

//using namespace utl;
using namespace ::osl;
using namespace ::rtl;
using namespace ::com::sun::star;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::linguistic2;
using namespace linguistic;

///////////////////////////////////////////////////////////////////////////

SV_IMPL_OBJARR(ActDicArray, ActDic);

///////////////////////////////////////////////////////////////////////////

#define BUFSIZE				 256
#define VERS2_NOLANGUAGE 	1024

static sal_Char			aBuf[ BUFSIZE ];


// forward dedclarations

static BOOL IsVers2OrNewer( const String& rFileURL, USHORT& nLng, BOOL& bNeg,
					 		sal_Char* pWordBuf);

static void AddInternal( Reference< XDictionary > &rDic,
						 const OUString& rNew );
static void AddUserData( const Reference< XDictionary > &rDic );

///////////////////////////////////////////////////////////////////////////

class DicEvtListenerHelper :
	public cppu::WeakImplHelper1
	<
		XDictionaryEventListener
	>
{
	cppu::OInterfaceContainerHelper		aDicListEvtListeners;
	Sequence< DictionaryEvent >			aCollectDicEvt;
	Reference< XDictionaryList >		xMyDicList;

	INT16								nCondensedEvt;
	INT16								nNumCollectEvtListeners,
		 								nNumVerboseListeners;

public:
	DicEvtListenerHelper( const Reference< XDictionaryList > &rxDicList );
	virtual ~DicEvtListenerHelper();

	// XEventListener
	virtual void SAL_CALL
		disposing( const EventObject& rSource )
			throw(RuntimeException);

	// XDictionaryEventListener
    virtual void SAL_CALL
		processDictionaryEvent( const DictionaryEvent& rDicEvent )
			throw(RuntimeException);

	// non-UNO functions
	void 	DisposeAndClear( const EventObject &rEvtObj );

    BOOL	AddDicListEvtListener(
				const Reference< XDictionaryListEventListener >& rxListener,
				BOOL bReceiveVerbose );
    BOOL	RemoveDicListEvtListener(
				const Reference< XDictionaryListEventListener >& rxListener );
    INT16	BeginCollectEvents();
    INT16	EndCollectEvents();
    INT16	FlushEvents();
};


DicEvtListenerHelper::DicEvtListenerHelper(
		const Reference< XDictionaryList > &rxDicList ) :
	aDicListEvtListeners	( GetLinguMutex() ),
	xMyDicList				( rxDicList )
{
	nCondensedEvt	= 0;
	nNumCollectEvtListeners = nNumVerboseListeners	= 0;
}


DicEvtListenerHelper::~DicEvtListenerHelper()
{
	DBG_ASSERT(aDicListEvtListeners.getLength() == 0,
		"lng : event listeners are still existing");
}


void DicEvtListenerHelper::DisposeAndClear( const EventObject &rEvtObj )
{
	aDicListEvtListeners.disposeAndClear( rEvtObj );
}


void SAL_CALL DicEvtListenerHelper::disposing( const EventObject& rSource )
		throw(RuntimeException)
{
	MutexGuard	aGuard( GetLinguMutex() );

	Reference< XInterface > xSrc( rSource.Source );

	// remove event object from EventListener list
	if (xSrc.is())
		aDicListEvtListeners.removeInterface( xSrc );

	// if object is a dictionary then remove it from the dictionary list
	// Note: this will probably happen only if someone makes a XDictionary
	// implementation of his own that is also a XComponent.
	Reference< XDictionary > xDic( xSrc, UNO_QUERY );
	if (xDic.is())
	{
		xMyDicList->removeDictionary( xDic );
	}
}


void SAL_CALL DicEvtListenerHelper::processDictionaryEvent(
			const DictionaryEvent& rDicEvent )
		throw(RuntimeException)
{
	MutexGuard	aGuard( GetLinguMutex() );

	Reference< XDictionary > xDic( rDicEvent.Source, UNO_QUERY );
	DBG_ASSERT(xDic.is(), "lng : missing event source");

	// assert that there is a corresponding dictionary entry if one was
	// added or deleted
	Reference< XDictionaryEntry > xDicEntry( rDicEvent.xDictionaryEntry, UNO_QUERY );
	DBG_ASSERT( !(rDicEvent.nEvent &
					(DictionaryEventFlags::ADD_ENTRY | DictionaryEventFlags::DEL_ENTRY))
				|| xDicEntry.is(),
				"lng : missing dictionary entry" );

	BOOL bActiveDicsModified = FALSE;
	//
	// evaluate DictionaryEvents and update data for next DictionaryListEvent
	//
	DictionaryType eDicType = xDic->getDictionaryType();
	DBG_ASSERT(eDicType != DictionaryType_MIXED,
		"lng : unexpected dictionary type");
	if ((rDicEvent.nEvent & DictionaryEventFlags::ADD_ENTRY) && xDic->isActive())
		nCondensedEvt |= xDicEntry->isNegative() ?
			DictionaryListEventFlags::ADD_NEG_ENTRY :
			DictionaryListEventFlags::ADD_POS_ENTRY;
	if ((rDicEvent.nEvent & DictionaryEventFlags::DEL_ENTRY) && xDic->isActive())
		nCondensedEvt |= xDicEntry->isNegative() ?
			DictionaryListEventFlags::DEL_NEG_ENTRY :
			DictionaryListEventFlags::DEL_POS_ENTRY;
	if ((rDicEvent.nEvent & DictionaryEventFlags::ENTRIES_CLEARED) && xDic->isActive())
		nCondensedEvt |= eDicType == DictionaryType_NEGATIVE ?
			DictionaryListEventFlags::DEL_NEG_ENTRY :
			DictionaryListEventFlags::DEL_POS_ENTRY;
	if ((rDicEvent.nEvent & DictionaryEventFlags::CHG_LANGUAGE) && xDic->isActive())
		nCondensedEvt |= eDicType == DictionaryType_NEGATIVE ?
			DictionaryListEventFlags::DEACTIVATE_NEG_DIC
				| DictionaryListEventFlags::ACTIVATE_NEG_DIC :
			DictionaryListEventFlags::DEACTIVATE_POS_DIC
				| DictionaryListEventFlags::ACTIVATE_POS_DIC;
	if ((rDicEvent.nEvent & DictionaryEventFlags::ACTIVATE_DIC))
		nCondensedEvt |= eDicType == DictionaryType_NEGATIVE ?
			DictionaryListEventFlags::ACTIVATE_NEG_DIC :
			DictionaryListEventFlags::ACTIVATE_POS_DIC;
	if ((rDicEvent.nEvent & DictionaryEventFlags::DEACTIVATE_DIC))
		nCondensedEvt |= eDicType == DictionaryType_NEGATIVE ?
			DictionaryListEventFlags::DEACTIVATE_NEG_DIC :
			DictionaryListEventFlags::DEACTIVATE_POS_DIC;

	// update list of collected events if needs to be
	if (nNumVerboseListeners > 0)
	{
		INT32 nColEvts = aCollectDicEvt.getLength();
		aCollectDicEvt.realloc( nColEvts + 1 );
		aCollectDicEvt.getArray()[ nColEvts ] = rDicEvent;
	}

	if (nNumCollectEvtListeners == 0 && nCondensedEvt != 0)
		FlushEvents();
}


BOOL DicEvtListenerHelper::AddDicListEvtListener(
			const Reference< XDictionaryListEventListener >& xListener,
			BOOL bReceiveVerbose )
{
	DBG_ASSERT( xListener.is(), "empty reference" );
	INT32	nCount = aDicListEvtListeners.getLength();
	return aDicListEvtListeners.addInterface( xListener ) != nCount;
}


BOOL DicEvtListenerHelper::RemoveDicListEvtListener(
			const Reference< XDictionaryListEventListener >& xListener )
{
	DBG_ASSERT( xListener.is(), "empty reference" );
	INT32	nCount = aDicListEvtListeners.getLength();
	return aDicListEvtListeners.removeInterface( xListener ) != nCount;
}


INT16 DicEvtListenerHelper::BeginCollectEvents()
{
	return ++nNumCollectEvtListeners;
}


INT16 DicEvtListenerHelper::EndCollectEvents()
{
	DBG_ASSERT(nNumCollectEvtListeners > 0, "lng: mismatched function call");
	if (nNumCollectEvtListeners > 0)
	{
		FlushEvents();
		nNumCollectEvtListeners--;
	}

	return nNumCollectEvtListeners;
}


INT16 DicEvtListenerHelper::FlushEvents()
{
	if (0 != nCondensedEvt)
	{
		// build DictionaryListEvent to pass on to listeners
		uno::Sequence< DictionaryEvent > aDicEvents;
		if (nNumVerboseListeners > 0)
			aDicEvents = aCollectDicEvt;
		DictionaryListEvent aEvent( xMyDicList, nCondensedEvt, aDicEvents );

		// pass on event
		cppu::OInterfaceIteratorHelper aIt( aDicListEvtListeners );
		while (aIt.hasMoreElements())
		{
			Reference< XDictionaryListEventListener > xRef( aIt.next(), UNO_QUERY );
			if (xRef.is())
				xRef->processDictionaryListEvent( aEvent );
		}

		// clear "list" of events
		nCondensedEvt = 0;
		aCollectDicEvt.realloc( 0 );
	}

	return nNumCollectEvtListeners;
}


///////////////////////////////////////////////////////////////////////////


void DicList::MyAppExitListener::AtExit()
{
    rMyDicList.SaveDics();
}


DicList::DicList() :
	aEvtListeners	( GetLinguMutex() ),
	pDicList( 0 )
{
	pDicEvtLstnrHelper	= new DicEvtListenerHelper( this );
	xDicEvtLstnrHelper	= pDicEvtLstnrHelper;
	bDisposing = FALSE;

	pExitListener = new MyAppExitListener( *this );
	xExitListener = pExitListener;
	pExitListener->Activate();
}

DicList::~DicList()
{
	pExitListener->Deactivate();
	delete pDicList;
}


void DicList::searchForDictionaries( ActDicArray &rDicList,
									 const String &rDicDir )
{
	MutexGuard	aGuard( GetLinguMutex() );

    const Sequence< OUString > aDirCnt( ::utl::LocalFileHelper::
										GetFolderContents( rDicDir, FALSE ) );
	const OUString *pDirCnt = aDirCnt.getConstArray();
	INT32 nEntries = aDirCnt.getLength();

	String aDCN( String::CreateFromAscii( "dcn" ) );
	String aDCP( String::CreateFromAscii( "dcp" ) );
	for (INT32 i = 0;  i < nEntries;  ++i)
	{
        String  aURL( pDirCnt[i] );
		USHORT	nLang = LANGUAGE_NONE;
		BOOL	bNeg  = FALSE;

		if(!::binfilter::IsVers2OrNewer( aURL, nLang, bNeg, aBuf ))//STRIP008         if(!::IsVers2OrNewer( aURL, nLang, bNeg, aBuf ))
		{
			// Wenn kein
            xub_StrLen nPos  = aURL.Search('.');
            String aExt(aURL.Copy(nPos + 1));
			aExt.ToLowerAscii();

			if(aExt == aDCN)       // negativ
				bNeg = TRUE;
			else if(aExt == aDCP)  // positiv
				bNeg = FALSE;
			else
				continue;          // andere Files
		}

		// Aufnehmen in die Liste der Dictionaries
		// Wenn existent nicht aufnehmen
		//
		INT16 nSystemLanguage = MsLangId::getSystemLanguage();
        String aTmp1 = ToLower( aURL, nSystemLanguage );
		xub_StrLen nPos = aTmp1.SearchBackward( '/' );
		if (STRING_NOTFOUND != nPos)
			aTmp1 = aTmp1.Copy( nPos + 1 );
		String aTmp2;
        USHORT j;
        USHORT nCount = rDicList.Count();
		for(j = 0;  j < nCount;  j++)
		{
			aTmp2 = rDicList.GetObject( j ).xDic->getName().getStr();
			aTmp2 = ToLower( aTmp2, nSystemLanguage );
			if(aTmp1 == aTmp2)
				break;
		}
		if(j >= nCount)		// dictionary not yet in DicList
		{
            // get decoded dictionary file name
            INetURLObject aURLObj( aURL );
            String aDicName = aURLObj.getName( INetURLObject::LAST_SEGMENT, 
                        true, INetURLObject::DECODE_WITH_CHARSET, 
                        RTL_TEXTENCODING_UTF8 );
            
            DictionaryType eType = bNeg ? DictionaryType_NEGATIVE : DictionaryType_POSITIVE;
			Reference< XDictionary > xDic =
                        new DictionaryNeo( aDicName, nLang, eType, aURL );

			addDictionary( xDic );
			nCount++;
		}
	}
}


INT32 DicList::getDicPos(const Reference< XDictionary > &xDic)
{
	MutexGuard	aGuard( GetLinguMutex() );

	INT32 nPos = -1;
	ActDicArray& rDicList = GetDicList();
    USHORT n = rDicList.Count();
    for (USHORT i = 0;  i < n;  i++)
	{
		if ( rDicList.GetObject(i).xDic == xDic )
			return i;
	}
	return nPos;
}


Reference< XInterface > SAL_CALL
	DicList_CreateInstance(	const Reference< XMultiServiceFactory > & rSMgr )
			throw(Exception)
{
	Reference< XInterface > xService = (cppu::OWeakObject *) new DicList;
	return xService;
}

sal_Int16 SAL_CALL DicList::getCount() throw(RuntimeException)
{
	MutexGuard	aGuard( GetLinguMutex() );
	return GetDicList().Count();
}

uno::Sequence< Reference< XDictionary > > SAL_CALL
		DicList::getDictionaries()
			throw(RuntimeException)
{
	MutexGuard	aGuard( GetLinguMutex() );

	ActDicArray& rDicList = GetDicList();

	uno::Sequence< Reference< XDictionary > > aDics( rDicList.Count() );
	Reference< XDictionary > *pDic = aDics.getArray();

    USHORT n = (USHORT) aDics.getLength();
    for (USHORT i = 0;  i < n;  i++)
		pDic[i] = rDicList.GetObject(i).xDic;

	return aDics;
}

Reference< XDictionary > SAL_CALL
		DicList::getDictionaryByName( const OUString& aDictionaryName )
			throw(RuntimeException)
{
	MutexGuard	aGuard( GetLinguMutex() );

	Reference< XDictionary > xDic;
	ActDicArray& rDicList = GetDicList();
    USHORT nCount = rDicList.Count();
    for (USHORT i = 0;  i < nCount;  i++)
	{
		const Reference< XDictionary > &rDic = rDicList.GetObject(i).xDic;
		if (rDic.is()  &&  rDic->getName() == aDictionaryName)
		{
			xDic = rDic;
			break;
		}
	}

	return xDic;
}

sal_Bool SAL_CALL DicList::addDictionary(
			const Reference< XDictionary >& xDictionary )
		throw(RuntimeException)
{
	MutexGuard	aGuard( GetLinguMutex() );

	if (bDisposing)
		return FALSE;

	BOOL bRes = FALSE;
	if (xDictionary.is())
	{
		ActDicArray& rDicList = GetDicList();
		rDicList.Insert( ActDic(xDictionary), rDicList.Count() );
		bRes = TRUE;

		// add listener helper to the dictionaries listener lists
		xDictionary->addDictionaryEventListener( xDicEvtLstnrHelper );
	}
	return bRes;
}

sal_Bool SAL_CALL
	DicList::removeDictionary( const Reference< XDictionary >& xDictionary )
		throw(RuntimeException)
{
	MutexGuard	aGuard( GetLinguMutex() );

	if (bDisposing)
		return FALSE;

	BOOL  bRes = FALSE;
	INT32 nPos = getDicPos( xDictionary );
	if (nPos >= 0)
	{
		// remove dictionary list from the dictionaries listener lists
		ActDicArray& rDicList = GetDicList();
        Reference< XDictionary > xDic( rDicList.GetObject( (USHORT) nPos ).xDic );
		DBG_ASSERT(xDic.is(), "lng : empty reference");
		if (xDic.is())
		{
			// deactivate dictionary if not already done
			xDic->setActive( FALSE );

			xDic->removeDictionaryEventListener( xDicEvtLstnrHelper );
		}

        rDicList.Remove( (USHORT) nPos );
		bRes = TRUE;
	}
	return bRes;
}

sal_Bool SAL_CALL DicList::addDictionaryListEventListener(
			const Reference< XDictionaryListEventListener >& xListener,
			sal_Bool bReceiveVerbose )
		throw(RuntimeException)
{
	MutexGuard	aGuard( GetLinguMutex() );

	if (bDisposing)
		return FALSE;

	DBG_ASSERT(!bReceiveVerbose, "lng : not yet supported");

	BOOL bRes = FALSE;
	if (xListener.is())	//! don't add empty references
	{
		bRes = pDicEvtLstnrHelper->
						AddDicListEvtListener( xListener, bReceiveVerbose );
	}
	return bRes;
}

sal_Bool SAL_CALL DicList::removeDictionaryListEventListener(
			const Reference< XDictionaryListEventListener >& xListener )
		throw(RuntimeException)
{
	MutexGuard	aGuard( GetLinguMutex() );

	if (bDisposing)
		return FALSE;

	BOOL bRes = FALSE;
	if(xListener.is())
	{
		bRes = pDicEvtLstnrHelper->RemoveDicListEvtListener( xListener );
	}
	return bRes;
}

sal_Int16 SAL_CALL DicList::beginCollectEvents() throw(RuntimeException)
{
	MutexGuard	aGuard( GetLinguMutex() );
	return pDicEvtLstnrHelper->BeginCollectEvents();
}

sal_Int16 SAL_CALL DicList::endCollectEvents() throw(RuntimeException)
{
	MutexGuard	aGuard( GetLinguMutex() );
	return pDicEvtLstnrHelper->EndCollectEvents();
}

sal_Int16 SAL_CALL DicList::flushEvents() throw(RuntimeException)
{
	MutexGuard	aGuard( GetLinguMutex() );
	return pDicEvtLstnrHelper->FlushEvents();
}

Reference< XDictionary > SAL_CALL
	DicList::createDictionary( const OUString& rName, const Locale& rLocale,
			DictionaryType eDicType, const OUString& rURL )
		throw(RuntimeException)
{
	MutexGuard	aGuard( GetLinguMutex() );

	INT16 nLanguage = LocaleToLanguage( rLocale );
	return new DictionaryNeo( rName, nLanguage, eDicType, rURL );
}


Reference< XDictionaryEntry > SAL_CALL
	DicList::queryDictionaryEntry( const OUString& rWord, const Locale& rLocale,
			sal_Bool bSearchPosDics, sal_Bool bSearchSpellEntry )
		throw(RuntimeException)
{
	MutexGuard	aGuard( GetLinguMutex() );
	return SearchDicList( this, rWord, LocaleToLanguage( rLocale ),
							bSearchPosDics, bSearchSpellEntry );
}


void SAL_CALL
	DicList::dispose()
		throw(RuntimeException)
{
	MutexGuard	aGuard( GetLinguMutex() );

	if (!bDisposing)
	{
		bDisposing = TRUE;
		EventObject	aEvtObj( (XDictionaryList *) this );

		aEvtListeners.disposeAndClear( aEvtObj );
		if (pDicEvtLstnrHelper)
			pDicEvtLstnrHelper->DisposeAndClear( aEvtObj );

        //! avoid creation of dictionaries if not already done
        if (pDicList)
        {
            ActDicArray& rDicList = GetDicList();
            INT16 nCount = rDicList.Count();
            for (INT16 i = 0;  i < nCount;  i++)
            {
                Reference< XDictionary > xDic( rDicList.GetObject(i).xDic , UNO_QUERY );

                // save (modified) dictionaries
                Reference< frame::XStorable >  xStor( xDic , UNO_QUERY );
                if (xStor.is())
                {
                    try
                    {
                        if (!xStor->isReadonly() && xStor->hasLocation())
                            xStor->store();
                    }
                    catch(Exception &)
                    {
                    }
                }

                // release references to (members of) this object hold by
                // dictionaries
                if (xDic.is())
                    xDic->removeDictionaryEventListener( xDicEvtLstnrHelper );
            }
        }
	}
}

void SAL_CALL
	DicList::addEventListener( const Reference< XEventListener >& rxListener )
		throw(RuntimeException)
{
	MutexGuard	aGuard( GetLinguMutex() );

	if (!bDisposing && rxListener.is())
		aEvtListeners.addInterface( rxListener );
}

void SAL_CALL
	DicList::removeEventListener( const Reference< XEventListener >& rxListener )
		throw(RuntimeException)
{
	MutexGuard	aGuard( GetLinguMutex() );

	if (!bDisposing && rxListener.is())
		aEvtListeners.removeInterface( rxListener );
}

void DicList::_CreateDicList()
{
	pDicList = new ActDicArray;

	// look for dictionaries
	SvtPathOptions aPathOpt;
	searchForDictionaries( *pDicList, aPathOpt.GetUserDictionaryPath() );
	searchForDictionaries( *pDicList, aPathOpt.GetDictionaryPath() );

	// create IgnoreAllList dictionary with empty URL (non persistent)
	// and add it to list
	OUString aDicName( A2OU( "IgnoreAllList" ) );
	Reference< XDictionary > xIgnAll(
			createDictionary( aDicName, CreateLocale( LANGUAGE_NONE ),
							  DictionaryType_POSITIVE, OUString() ) );
	if (xIgnAll.is())
	{
		AddUserData( xIgnAll );
		xIgnAll->setActive( TRUE );
		addDictionary( xIgnAll );
	}
    
    
    // evaluate list of dictionaries to be activated from configuration
	//
	//! to suppress overwriting the list of active dictionaries in the
	//! configuration with incorrect arguments during the following
	//! activation of the dictionaries
	pDicEvtLstnrHelper->BeginCollectEvents();
	//
	const Sequence< OUString > aActiveDics( aOpt.GetActiveDics() );
	const OUString *pActiveDic = aActiveDics.getConstArray();
	INT32 nLen = aActiveDics.getLength();
	for (INT32 i = 0;  i < nLen;  ++i)
	{
		if (pActiveDic[i].getLength())
		{
			Reference< XDictionary > xDic( getDictionaryByName( pActiveDic[i] ) );
			if (xDic.is())
				xDic->setActive( TRUE );
		}
	}
	pDicEvtLstnrHelper->EndCollectEvents();
}


void DicList::SaveDics()
{
    // save dics only if they have already been used/created.
    //! don't create them just for the purpose of saving them !
    if (pDicList)
    {
        // save (modified) dictionaries
        ActDicArray& rDicList = GetDicList();
        USHORT nCount = rDicList.Count();;
        for (USHORT i = 0;  i < nCount;  i++)
        {
            // save (modified) dictionaries
            Reference< frame::XStorable >  xStor( rDicList.GetObject(i).xDic, 
                                                  UNO_QUERY );
            if (xStor.is())
            {
                try
                {
                    if (!xStor->isReadonly() && xStor->hasLocation())
                        xStor->store();
                }
                catch(Exception &)
                {
                }
            }
        }
    }
}


///////////////////////////////////////////////////////////////////////////
// Service specific part
//

OUString SAL_CALL DicList::getImplementationName(  ) throw(RuntimeException)
{
	MutexGuard	aGuard( GetLinguMutex() );
	return getImplementationName_Static();
}


sal_Bool SAL_CALL DicList::supportsService( const OUString& ServiceName )
		throw(RuntimeException)
{
	MutexGuard	aGuard( GetLinguMutex() );

	uno::Sequence< OUString > aSNL = getSupportedServiceNames();
	const OUString * pArray = aSNL.getConstArray();
	for( INT32 i = 0; i < aSNL.getLength(); i++ )
		if( pArray[i] == ServiceName )
			return TRUE;
	return FALSE;
}


uno::Sequence< OUString > SAL_CALL DicList::getSupportedServiceNames(  )
		throw(RuntimeException)
{
	MutexGuard	aGuard( GetLinguMutex() );
	return getSupportedServiceNames_Static();
}


uno::Sequence< OUString > DicList::getSupportedServiceNames_Static() throw()
{
	MutexGuard	aGuard( GetLinguMutex() );

	uno::Sequence< OUString > aSNS( 1 );	// auch mehr als 1 Service moeglich
	aSNS.getArray()[0] = A2OU( SN_DICTIONARY_LIST );
	return aSNS;
}


sal_Bool SAL_CALL DicList_writeInfo(
	void * /*pServiceManager*/, registry::XRegistryKey * pRegistryKey )
{
	try
	{
		String aImpl( '/' );
		aImpl += DicList::getImplementationName_Static().getStr();
		aImpl.AppendAscii( "/UNO/SERVICES" );
		Reference< registry::XRegistryKey > xNewKey =
				pRegistryKey->createKey(aImpl );
		uno::Sequence< OUString > aServices =
				DicList::getSupportedServiceNames_Static();
		for( INT32 i = 0; i < aServices.getLength(); i++ )
			xNewKey->createKey( aServices.getConstArray()[i]);

		return sal_True;
	}
	catch(Exception &)
	{
		return sal_False;
	}
}


void * SAL_CALL DicList_getFactory(	const sal_Char * pImplName,
		XMultiServiceFactory * pServiceManager, void *  )
{
	void * pRet = 0;
	if ( !DicList::getImplementationName_Static().compareToAscii( pImplName ) )
	{
		Reference< XSingleServiceFactory > xFactory =
			cppu::createOneInstanceFactory(
				pServiceManager,
				DicList::getImplementationName_Static(),
				DicList_CreateInstance,
				DicList::getSupportedServiceNames_Static());
		// acquire, because we return an interface pointer instead of a reference
		xFactory->acquire();
		pRet = xFactory.get();
	}
	return pRet;
}

///////////////////////////////////////////////////////////////////////////

xub_StrLen lcl_GetToken( String &rToken,
			const String &rText, xub_StrLen nPos, const String &rDelim )
{
	xub_StrLen nRes = STRING_LEN;

	if (rText.Len() == 0  ||  nPos >= rText.Len())
		rToken = String();
	else if (rDelim.Len() == 0)
	{
		rToken = rText;
		if (rToken.Len())
			nRes = rText.Len();
	}
	else
	{
		xub_StrLen	i;
		for (i = nPos;  i < rText.Len();  ++i)
		{
			if (STRING_NOTFOUND != rDelim.Search( rText.GetChar(i) ))
				break;
		}

		if (i >= rText.Len())	// delimeter not found
			rToken	= rText.Copy( nPos );
		else
			rToken	= rText.Copy( nPos, (INT32) i - nPos );
		nRes	= i + 1;	// continue after found delimeter
	}

	return nRes;
}


static void AddInternal(
		const Reference<XDictionary> &rDic,
		const OUString& rNew )
{
	if (rDic.is())
	{
		//! TL TODO: word iterator should be used to break up the text
		static const char *pDefWordDelim =
				"!\"#$%&'()*+,-./:;<=>?[]\\_^`{|}~\t \n";
		ByteString aDummy( pDefWordDelim );
        String aDelim( aDummy, osl_getThreadTextEncoding() );
		aDelim.EraseAllChars( '.' );

		String 		aToken;
		xub_StrLen  nPos = 0;
		while (STRING_LEN !=
					(nPos = lcl_GetToken( aToken, rNew, nPos, aDelim )))
		{
        	if( aToken.Len()  &&  !IsNumeric( aToken ) )
			{
				rDic->add( aToken, FALSE, OUString() );
			}
		}
	}
}

static void AddUserData( const Reference< XDictionary > &rDic )
{
	if (rDic.is())
	{
		SvtUserOptions aUserOpt;
		AddInternal( rDic, aUserOpt.GetFullName() );
		AddInternal( rDic, aUserOpt.GetCompany() );
		AddInternal( rDic, aUserOpt.GetStreet() );
		AddInternal( rDic, aUserOpt.GetCity() );
		AddInternal( rDic, aUserOpt.GetTitle() );
		AddInternal( rDic, aUserOpt.GetPosition() );
		AddInternal( rDic, aUserOpt.GetEmail() );
	}
}

///////////////////////////////////////////////////////////////////////////

#pragma optimize("ge",off)

static BOOL IsVers2OrNewer( const String& rFileURL, USHORT& nLng, BOOL& bNeg,
					 		sal_Char* pWordBuf)
{
	if (rFileURL.Len() == 0)
		return FALSE;
	String aDIC( GetDicExtension() );
	String aExt;
	xub_StrLen nPos = rFileURL.SearchBackward( '.' );
	if (STRING_NOTFOUND != nPos)
		aExt = rFileURL.Copy( nPos + 1 );
	aExt.ToLowerAscii();

	if(aExt != aDIC)
		return FALSE;

	// get stream to be used
	SfxMedium aMedium( rFileURL, STREAM_READ | STREAM_SHARE_DENYWRITE, FALSE );
    aMedium.SetTransferPriority( SFX_TFPRIO_SYNCHRON );
	SvStream *pStream = aMedium.GetInStream();
	if (!pStream || pStream->GetError())
		return FALSE;

	// Header einlesen
	USHORT 		nLen;
	sal_Char	nTmp;

	*pStream >> nLen;
	if( nLen >= BUFSIZE )
		return FALSE;
	pStream->Read( pWordBuf, nLen);
	*( pWordBuf + nLen ) = 0;
	// Version 2.0 oder Version 5.0 ?
	int nDicVersion = GetDicVersion( pWordBuf );
	if (2 == nDicVersion ||
		5 == nDicVersion ||
		6 == nDicVersion)
	{
		// Sprache des Dictionaries
		*pStream >> nLng;

		if ( VERS2_NOLANGUAGE == nLng )
			nLng = LANGUAGE_NONE;

		// Negativ-Flag
		*pStream >> nTmp;
		bNeg = (BOOL)nTmp;

		return TRUE;
	}
	return FALSE;
}

///////////////////////////////////////////////////////////////////////////

}
