/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: linguistic_lngsvcmgr.cxx,v $
 *
 *  $Revision: 1.8 $
 *
 *  last change: $Author: hr $ $Date: 2007/01/02 16:50:20 $
 *
 *  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 <cppuhelper/factory.hxx>	// helper for factories
#include <com/sun/star/registry/XRegistryKey.hpp>
#ifndef _COM_SUN_STAR_CONTAINER_XCONTENTENUMERATIONACCESS_HPP_
#include <com/sun/star/container/XContentEnumerationAccess.hpp>
#endif
#ifndef _COM_SUN_STAR_CONTAINER_XENUMERATION_HPP_
#include <com/sun/star/container/XEnumeration.hpp>
#endif
#ifndef _COM_SUN_STAR_LANG_XMULTISERVICEFACTORY_HPP_
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#endif
#ifndef _COM_SUN_STAR_LINGUISTIC2_XSUPPORTEDLOCALES_HPP_
#include <com/sun/star/linguistic2/XSupportedLocales.hpp>
#endif

#include <com/sun/star/linguistic2/DictionaryListEventFlags.hpp>
#include <com/sun/star/linguistic2/LinguServiceEventFlags.hpp>

#ifndef _SOLAR_H
#include <tools/solar.h>
#endif
#ifndef INCLUDED_I18NPOOL_MSLANGID_HXX
#include <i18npool/mslangid.hxx>
#endif
#ifndef _SVARRAY_HXX
#include <svtools/svarray.hxx>
#endif
#ifndef _SVTOOLS_LINGUCFG_HXX_
#include <svtools/lingucfg.hxx>
#endif
#include <cppuhelper/extract.hxx>
#ifndef _UNOTOOLS_PROCESSFACTORY_HXX_
#include <unotools/processfactory.hxx>
#endif
#ifndef _LEGACYBINFILTERMGR_HXX
#include <legacysmgr/legacy_binfilters_smgr.hxx>	//STRIP002
#endif
#include "lngsvcmgr.hxx"
#include "lngopt.hxx"
#include "misc.hxx"
#include "spelldsp.hxx"
#include "hyphdsp.hxx"
#include "thesdsp.hxx"
namespace binfilter {


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


SV_DECL_VARARR_SORT( SortedINT16Array, INT16, 32, 32)//STRIP008 ;
SV_IMPL_VARARR_SORT( SortedINT16Array, INT16 );

// forward declarations
Sequence< OUString > static GetLangSvcList( const Any &rVal );
Sequence< OUString > static GetLangSvc( const Any &rVal );

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

static Sequence< Locale > GetAvailLocales(
		const Sequence< OUString > &rSvcImplNames )
{
	Sequence< Locale > aRes;

	Reference< XMultiServiceFactory >  xFac( ::legacy_binfilters::getLegacyProcessServiceFactory() );
	INT32 nNames = rSvcImplNames.getLength();
	if (nNames  &&  xFac.is())
	{
		SortedINT16Array aLanguages;

		//! since we're going to create one-instance services we have to
		//! supply their arguments even if we would not need them here...
		Sequence< Any > aArgs(2);
		aArgs.getArray()[0] <<= GetLinguProperties();

		// check all services for the supported languages and new
		// languages to the result
		const OUString *pImplNames = rSvcImplNames.getConstArray();
		INT32 i; for (i = 0;  i < nNames;  ++i)
		{
			Reference< XSupportedLocales > xSuppLoc(
					xFac->createInstanceWithArguments( pImplNames[i], aArgs ),
					UNO_QUERY );
			if (xSuppLoc.is())
			{
				Sequence< Locale > aLoc( xSuppLoc->getLocales() );
				INT32 nLoc = aLoc.getLength();
				for (INT32 k = 0;  k < nLoc;  ++k)
				{
					const Locale *pLoc = aLoc.getConstArray();
					INT16 nLang = LocaleToLanguage( pLoc[k] );

					// language not already added?
					if (!aLanguages.Seek_Entry( nLang ))
						aLanguages.Insert( nLang );
				}
			}
			else
				DBG_ERROR( "interface not supported by service" );
		}

        // build return sequence
		INT16 nLanguages = aLanguages.Count();
		aRes.realloc( nLanguages );
		Locale *pRes = aRes.getArray();
		for (i = 0;  i < nLanguages;  ++i)
		{
            INT16 nLang = aLanguages[(USHORT) i];
			pRes[i] = CreateLocale( nLang );
		}
	}

	return aRes;
}

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

struct SvcInfo
{
	const ::rtl::OUString							aSvcImplName;
	const ::com::sun::star::uno::Sequence< INT16 >	aSuppLanguages;

	SvcInfo( const ::rtl::OUString &rSvcImplName,
			 const ::com::sun::star::uno::Sequence< INT16 >	&rSuppLanguages ) :
		aSvcImplName	(rSvcImplName),
		aSuppLanguages	(rSuppLanguages)
	{
	}

	BOOL	HasLanguage( INT16 nLanguage ) const;
};


BOOL SvcInfo::HasLanguage( INT16 nLanguage ) const
{
	INT32 nCnt = aSuppLanguages.getLength();
	const INT16 *pLang = aSuppLanguages.getConstArray();
	INT32 i; for (i = 0;  i < nCnt;  ++i)
	{
		if (nLanguage == pLang[i])
			break;
	}
	return i < nCnt;
}


typedef SvcInfo * PTR_SVCINFO;
SV_DECL_PTRARR_DEL( SvcInfoArray, PTR_SVCINFO, 16, 16 )//STRIP008 ;
SV_IMPL_PTRARR( SvcInfoArray, PTR_SVCINFO * );


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


#ifdef NEVER

// not used anymore (see SetAvailableCfgServiceLists)

static void SetAvailableServiceLists( LinguDispatcher &rDispatcher,
		const SvcInfoArray &rInfoArray )
{
	USHORT nSvcs = rInfoArray.Count();

	// build list of all available languages
	SortedINT16Array aLanguages;
	USHORT i;
	for (i = 0;  i < nSvcs;  ++i)
	{
		const Sequence< INT16 > &rSuppLang = rInfoArray[i]->aSuppLanguages;
		INT32 nLang = rSuppLang.getLength();
		const INT16 *pSuppLang = rSuppLang.getConstArray();
		for (INT32 j = 0;  j < nLang;  ++j)
		{
			// language not already added?
			if (!aLanguages.Seek_Entry( pSuppLang[j] ))
				aLanguages.Insert( pSuppLang[j] );
		}
	}

	// set service list per language to all available services supporting
	// that language
	INT16 nLanguages = aLanguages.Count();
	for (i = 0;  i < nLanguages;  ++i)
	{
		INT16 nActLang = aLanguages[i];
		Sequence< OUString > aSvcImplNames( nSvcs );
		OUString *pSvcImplName = aSvcImplNames.getArray();
		INT32 nSeqCnt = 0;
		for (USHORT j = 0;  j < nSvcs;  ++j)
		{
			const SvcInfo &rSvcInfo = *rInfoArray[j];
			if (rSvcInfo.HasLanguage( nActLang ))
				pSvcImplName[ nSeqCnt++ ] = rSvcInfo.aSvcImplName;
		}
		aSvcImplNames.realloc( nSeqCnt );

		rDispatcher.SetServiceList( CreateLocale( nActLang ), aSvcImplNames );
	}
}

void SetAvailableCfgServiceLists( LinguDispatcher &rDispatcher,
		const SvcInfoArray &rAvailSvcs )
{
    SvtLinguConfig aCfg;

	// get list of nodenames to look at for their service list
	const char *pEntryName = 0;
	BOOL bHasLangSvcList = TRUE;
	switch (rDispatcher.GetDspType())
	{
        case LinguDispatcher::DSP_SPELL : pEntryName = "ServiceManager/SpellCheckerList"; break;
        case LinguDispatcher::DSP_HYPH  : pEntryName = "ServiceManager/HyphenatorList";
										  bHasLangSvcList = FALSE;
										  break;
        case LinguDispatcher::DSP_THES  : pEntryName = "ServiceManager/ThesaurusList";  break;
		default :
			DBG_ERROR( "unexpected case" );
	}
	String	aNode( String::CreateFromAscii( pEntryName ) );
	Sequence < OUString > aNodeNames( aCfg.GetNodeNames( aNode ) );


	INT32 nLen = aNodeNames.getLength();
	const OUString *pNodeNames = aNodeNames.getConstArray();
	for (INT32 i = 0;  i < nLen;  ++i)
	{
		Sequence< OUString >	aSvcImplNames;

		Sequence< OUString >	aName( 1 );
		OUString *pNames = aName.getArray();

		OUString aPropName( aNode );
		aPropName += OUString::valueOf( (sal_Unicode) '/' );
		aPropName += pNodeNames[i];
		pNames[0] = aPropName;

        Sequence< Any > aValues /*= aCfg.GetProperties( aName )*/;
		if (aValues.getLength())
		{
			// get list of configured service names for the
			// current node (language)
			const Any &rValue = aValues.getConstArray()[0];
			if (bHasLangSvcList)
				aSvcImplNames = GetLangSvcList( rValue );
			else
				aSvcImplNames = GetLangSvc( rValue );

			INT32 nSvcs = aSvcImplNames.getLength();
			if (nSvcs)
			{
				const OUString *pImplNames = aSvcImplNames.getConstArray();

				INT16 nLang = MsLangId::convertIsoStringToLanguage( pNodeNames[i] );

				// build list of available services from those
				INT32 nCnt = 0;
				Sequence< OUString > aAvailSvcs( nSvcs );
				OUString *pAvailSvcs = aAvailSvcs.getArray();
				for (INT32 k = 0;  k < nSvcs;  ++k)
				{
					// check for availability of the service
					USHORT nAvailSvcs = rAvailSvcs.Count();
					for (USHORT m = 0;  m < nAvailSvcs;  ++m)
					{
						const SvcInfo &rSvcInfo = *rAvailSvcs[m];
						if (rSvcInfo.aSvcImplName == pImplNames[k]  &&
							rSvcInfo.HasLanguage( nLang ))
						{
							pAvailSvcs[ nCnt++ ] = rSvcInfo.aSvcImplName;
							break;
						}
					}
				}

				if (nCnt)
				{
					aAvailSvcs.realloc( nCnt );
					rDispatcher.SetServiceList( CreateLocale( nLang ), aAvailSvcs );
				}
			}
		}
	}
}
#endif


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


class LngSvcMgrListenerHelper :
	public cppu::WeakImplHelper2
	<
		XLinguServiceEventListener,
		XDictionaryListEventListener
	>
{
	Timer aLaunchTimer;

	//cppu::OMultiTypeInterfaceContainerHelper	aListeners;
	::cppu::OInterfaceContainerHelper			aLngSvcMgrListeners;
	::cppu::OInterfaceContainerHelper			aLngSvcEvtBroadcasters;
	Reference< XDictionaryList >				xDicList;
	Reference< XInterface >						xMyEvtObj;

	INT16	nCombinedLngSvcEvt;

	// disallow copy-constructor and assignment-operator for now
	LngSvcMgrListenerHelper(const LngSvcMgrListenerHelper &);
	LngSvcMgrListenerHelper & operator = (const LngSvcMgrListenerHelper &);

	void	LaunchEvent( INT16 nLngSvcEvtFlags );

	DECL_LINK( TimeOut, Timer* );

public:
	LngSvcMgrListenerHelper( const Reference< XInterface > &rxSource,
			const Reference< XDictionaryList > &rxDicList );

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

	// XLinguServiceEventListener
    virtual void SAL_CALL
		processLinguServiceEvent( const LinguServiceEvent& aLngSvcEvent )
			throw(RuntimeException);

	// XDictionaryListEventListener
    virtual void SAL_CALL
		processDictionaryListEvent(
				const DictionaryListEvent& rDicListEvent )
			throw(RuntimeException);

	inline	BOOL	AddLngSvcMgrListener(
						const Reference< XEventListener >& rxListener );
	inline	BOOL    RemoveLngSvcMgrListener(
						const Reference< XEventListener >& rxListener );
	void 	DisposeAndClear( const EventObject &rEvtObj );
	BOOL	AddLngSvcEvtBroadcaster(
						const Reference< XLinguServiceEventBroadcaster > &rxBroadcaster );
	BOOL	RemoveLngSvcEvtBroadcaster(
						const Reference< XLinguServiceEventBroadcaster > &rxBroadcaster );

    void    AddLngSvcEvt( INT16 nLngSvcEvt );
};


LngSvcMgrListenerHelper::LngSvcMgrListenerHelper(
		const Reference< XInterface > &rxSource,
		const Reference< XDictionaryList > &rxDicList  ) :
	aLngSvcMgrListeners		( GetLinguMutex() ),
	aLngSvcEvtBroadcasters	( GetLinguMutex() ),
	xDicList				( rxDicList ),
	xMyEvtObj				( rxSource )
{
	if (xDicList.is())
	{
		xDicList->addDictionaryListEventListener(
			(XDictionaryListEventListener *) this, FALSE );
	}

	//! The timer is used to 'sum up' different events in order to reduce the
	//! number of events forwarded.
	//! (This may happen already if a property was changed that has several
	//! listeners, and each of them is launching an event of it's own!)
	//! Thus this behaviour is necessary to avoid unecessary actions of
	//! this objects listeners!
	aLaunchTimer.SetTimeout( 2000 );
	aLaunchTimer.SetTimeoutHdl( LINK( this, LngSvcMgrListenerHelper, TimeOut ) );
	nCombinedLngSvcEvt = 0;
}


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

	Reference< XInterface > xRef( rSource.Source );
	if ( xRef.is() )
	{
		aLngSvcMgrListeners   .removeInterface( xRef );
		aLngSvcEvtBroadcasters.removeInterface( xRef );
		if (xDicList == xRef)
			xDicList = 0;
	}
}


IMPL_LINK( LngSvcMgrListenerHelper, TimeOut, Timer*, pTimer )
{
	MutexGuard	aGuard( GetLinguMutex() );

	if (&aLaunchTimer == pTimer)
	{
		// change event source to LinguServiceManager since the listeners
		// probably do not know (and need not to know) about the specific
		// SpellChecker's or Hyphenator's.
		LinguServiceEvent aEvtObj( xMyEvtObj, nCombinedLngSvcEvt );
		nCombinedLngSvcEvt = 0;

		// pass event on to XLinguServiceEventListener's
		cppu::OInterfaceIteratorHelper aIt( aLngSvcMgrListeners );
		while (aIt.hasMoreElements())
		{
			Reference< XLinguServiceEventListener > xRef( aIt.next(), UNO_QUERY );
			if (xRef.is())
				xRef->processLinguServiceEvent( aEvtObj );
		}
	}
	return 0;
}


void LngSvcMgrListenerHelper::AddLngSvcEvt( INT16 nLngSvcEvt )
{
    nCombinedLngSvcEvt |= nLngSvcEvt;
	aLaunchTimer.Start();
}


void SAL_CALL
	LngSvcMgrListenerHelper::processLinguServiceEvent(
			const LinguServiceEvent& rLngSvcEvent )
		throw(RuntimeException)
{
	MutexGuard	aGuard( GetLinguMutex() );
    AddLngSvcEvt( rLngSvcEvent.nEvent );
}


void SAL_CALL
	LngSvcMgrListenerHelper::processDictionaryListEvent(
			const DictionaryListEvent& rDicListEvent )
		throw(RuntimeException)
{
	MutexGuard	aGuard( GetLinguMutex() );

	INT16 nDlEvt = rDicListEvent.nCondensedEvent;
	if (0 == nDlEvt)
		return;

	// we do keep the original event source here though...

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

	//
	// "translate" DictionaryList event into LinguServiceEvent
	//
	INT16 nLngSvcEvt = 0;
	//
	INT16 nSpellCorrectFlags =
			DictionaryListEventFlags::ADD_NEG_ENTRY		|
			DictionaryListEventFlags::DEL_POS_ENTRY		|
			DictionaryListEventFlags::ACTIVATE_NEG_DIC	|
			DictionaryListEventFlags::DEACTIVATE_POS_DIC;
	if (0 != (nDlEvt & nSpellCorrectFlags))
		nLngSvcEvt |= LinguServiceEventFlags::SPELL_CORRECT_WORDS_AGAIN;
	//
	INT16 nSpellWrongFlags =
			DictionaryListEventFlags::ADD_POS_ENTRY		|
			DictionaryListEventFlags::DEL_NEG_ENTRY		|
			DictionaryListEventFlags::ACTIVATE_POS_DIC	|
			DictionaryListEventFlags::DEACTIVATE_NEG_DIC;
	if (0 != (nDlEvt & nSpellWrongFlags))
		nLngSvcEvt |= LinguServiceEventFlags::SPELL_WRONG_WORDS_AGAIN;
	//
	INT16 nHyphenateFlags =
			DictionaryListEventFlags::ADD_POS_ENTRY		|
			DictionaryListEventFlags::DEL_POS_ENTRY		|
			DictionaryListEventFlags::ACTIVATE_POS_DIC	|
			DictionaryListEventFlags::ACTIVATE_NEG_DIC;
	if (0 != (nDlEvt & nHyphenateFlags))
		nLngSvcEvt |= LinguServiceEventFlags::HYPHENATE_AGAIN;
	//
	if (nLngSvcEvt)
		LaunchEvent( nLngSvcEvt );
}


void LngSvcMgrListenerHelper::LaunchEvent( INT16 nLngSvcEvtFlags )
{
	LinguServiceEvent aEvt( xMyEvtObj, nLngSvcEvtFlags );

	// pass event on to XLinguServiceEventListener's
	cppu::OInterfaceIteratorHelper aIt( aLngSvcMgrListeners );
	while (aIt.hasMoreElements())
	{
		Reference< XLinguServiceEventListener > xRef( aIt.next(), UNO_QUERY );
		if (xRef.is())
			xRef->processLinguServiceEvent( aEvt );
	}
}


inline BOOL LngSvcMgrListenerHelper::AddLngSvcMgrListener(
		const Reference< XEventListener >& rxListener )
{
	aLngSvcMgrListeners.addInterface(
		/*::getCppuType((const ::com::sun::star::uno::Reference<
			::com::sun::star::lang::XEventListener >*)0), */
		rxListener );
	return TRUE;
}


inline BOOL LngSvcMgrListenerHelper::RemoveLngSvcMgrListener(
		const Reference< XEventListener >& rxListener )
{
	aLngSvcMgrListeners.removeInterface(
		/*::getCppuType((const ::com::sun::star::uno::Reference<
			::com::sun::star::lang::XEventListener >*)0), */
		rxListener );
	return TRUE;
}


void LngSvcMgrListenerHelper::DisposeAndClear( const EventObject &rEvtObj )
{
	// call "disposing" for all listeners and clear list
	aLngSvcMgrListeners   .disposeAndClear( rEvtObj );

	// remove references to this object hold by the broadcasters
	cppu::OInterfaceIteratorHelper aIt( aLngSvcEvtBroadcasters );
	while (aIt.hasMoreElements())
	{
		Reference< XLinguServiceEventBroadcaster > xRef( aIt.next(), UNO_QUERY );
		if (xRef.is())
			RemoveLngSvcEvtBroadcaster( xRef );
	}

	// remove refernce to this object hold by the dictionary-list
	if (xDicList.is())
	{
		xDicList->removeDictionaryListEventListener(
			(XDictionaryListEventListener *) this );
		xDicList = 0;
	}
}


BOOL LngSvcMgrListenerHelper::AddLngSvcEvtBroadcaster(
		const Reference< XLinguServiceEventBroadcaster > &rxBroadcaster )
{
	BOOL bRes = FALSE;
	if (rxBroadcaster.is())
	{
		aLngSvcEvtBroadcasters.addInterface( rxBroadcaster );
		rxBroadcaster->addLinguServiceEventListener(
				(XLinguServiceEventListener *) this );
	}
	return bRes;
}


BOOL LngSvcMgrListenerHelper::RemoveLngSvcEvtBroadcaster(
		const Reference< XLinguServiceEventBroadcaster > &rxBroadcaster )
{
	BOOL bRes = FALSE;
	if (rxBroadcaster.is())
	{
		aLngSvcEvtBroadcasters.removeInterface( rxBroadcaster );
		rxBroadcaster->removeLinguServiceEventListener(
				(XLinguServiceEventListener *) this );
	}
	return bRes;
}


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


LngSvcMgr::LngSvcMgr() :
	aEvtListeners	( GetLinguMutex() )
{
	bHasAvailSpellLocales	=
	bHasAvailHyphLocales	=
	bHasAvailThesLocales	=
    bDisposing = FALSE;

	pSpellDsp	= 0;
	pHyphDsp	= 0;
	pThesDsp	= 0;

	pAvailSpellSvcs	= 0;
	pAvailHyphSvcs	= 0;
	pAvailThesSvcs	= 0;
	pListenerHelper	= 0;
}


LngSvcMgr::~LngSvcMgr()
{
	// memory for pSpellDsp, pHyphDsp, pThesDsp, pListenerHelper
	// will be freed in the destructor of the respective Reference's
	// xSpellDsp, xHyphDsp, xThesDsp

	delete pAvailSpellSvcs;
	delete pAvailHyphSvcs;
	delete pAvailThesSvcs;
}


void LngSvcMgr::GetListenerHelper_Impl()
{
	if (!pListenerHelper)
	{
		pListenerHelper	= new LngSvcMgrListenerHelper(
				(XLinguServiceManager *) this, linguistic::GetDictionaryList() );
		xListenerHelper	= (XLinguServiceEventListener *) pListenerHelper;
	}
}


void LngSvcMgr::GetSpellCheckerDsp_Impl()
{
	if (!pSpellDsp)
	{
		pSpellDsp	= new SpellCheckerDispatcher( *this );
		xSpellDsp	= pSpellDsp;
		SetCfgServiceLists( *pSpellDsp );
	}
}


void LngSvcMgr::GetHyphenatorDsp_Impl()
{
	if (!pHyphDsp)
	{
		pHyphDsp	= new HyphenatorDispatcher( *this );
		xHyphDsp	= pHyphDsp;
		SetCfgServiceLists( *pHyphDsp );
	}
}


void LngSvcMgr::GetThesaurusDsp_Impl()
{
	if (!pThesDsp)
	{
		pThesDsp	= new ThesaurusDispatcher;
		xThesDsp	= pThesDsp;
		SetCfgServiceLists( *pThesDsp );
	}
}


void LngSvcMgr::GetAvailableSpellSvcs_Impl()
{
	if (!pAvailSpellSvcs)
	{
		pAvailSpellSvcs = new SvcInfoArray;

		Reference< XMultiServiceFactory >  xFac( ::legacy_binfilters::getLegacyProcessServiceFactory() );
		if (xFac.is())
		{
			Reference< XContentEnumerationAccess > xEnumAccess( xFac, UNO_QUERY );
			Reference< XEnumeration > xEnum;
			if (xEnumAccess.is())
				xEnum = xEnumAccess->createContentEnumeration(
						A2OU( SN_SPELLCHECKER ) );

			if (xEnum.is())
			{
				while (xEnum->hasMoreElements())
				{
					Any aCurrent = xEnum->nextElement();
					Reference< XSingleServiceFactory > xFactory;

					if (!::cppu::extractInterface( xFactory, aCurrent ))
						continue;

					Reference< XSpellChecker > xSvc( xFactory->createInstance(),
													 UNO_QUERY );
					if (xSvc.is())
					{
						OUString 			aImplName;
						Sequence< INT16 > 	aLanguages;
						Reference< XServiceInfo > xInfo( xSvc, UNO_QUERY );
						if (xInfo.is())
							aImplName = xInfo->getImplementationName();
						DBG_ASSERT( aImplName.getLength(),
								"empty implementation name" );
						Reference< XSupportedLocales > xSuppLoc( xSvc, UNO_QUERY );
						DBG_ASSERT( xSuppLoc.is(), "interfaces not supported" );
						if (xSuppLoc.is()) {
                            Sequence<Locale> aLocaleSequence(xSuppLoc->getLocales());
							aLanguages = LocaleSeqToLangSeq( aLocaleSequence );
                        }

						pAvailSpellSvcs->Insert( new SvcInfo( aImplName, aLanguages ),
											pAvailSpellSvcs->Count() );

						// TL_TODO:
						// when HRO has the functionality provided to unload
						// the DLLs it should be done here!
					}
				}
			}
		}
	}
}


void LngSvcMgr::GetAvailableHyphSvcs_Impl()
{
	if (!pAvailHyphSvcs)
	{
		pAvailHyphSvcs = new SvcInfoArray;

		Reference< XMultiServiceFactory >  xFac( ::legacy_binfilters::getLegacyProcessServiceFactory() );
		if (xFac.is())
		{
			Reference< XContentEnumerationAccess > xEnumAccess( xFac, UNO_QUERY );
			Reference< XEnumeration > xEnum;
			if (xEnumAccess.is())
				xEnum = xEnumAccess->createContentEnumeration(
						A2OU( SN_HYPHENATOR ) );

			if (xEnum.is())
			{
				while (xEnum->hasMoreElements())
				{
					Any aCurrent = xEnum->nextElement();
					Reference< XSingleServiceFactory > xFactory;

					if (!::cppu::extractInterface( xFactory, aCurrent ))
						continue;

					Reference< XHyphenator > xSvc( xFactory->createInstance(),
												   UNO_QUERY );
					if (xSvc.is())
					{
						OUString 			aImplName;
						Sequence< INT16 > 	aLanguages;
						Reference< XServiceInfo > xInfo( xSvc, UNO_QUERY );
						if (xInfo.is())
							aImplName = xInfo->getImplementationName();
						DBG_ASSERT( aImplName.getLength(),
								"empty implementation name" );
						Reference< XSupportedLocales > xSuppLoc( xSvc, UNO_QUERY );
						DBG_ASSERT( xSuppLoc.is(), "interfaces not supported" );
						if (xSuppLoc.is()) {
                            Sequence<Locale> aLocaleSequence(xSuppLoc->getLocales());
							aLanguages = LocaleSeqToLangSeq( aLocaleSequence );
                        }

						pAvailHyphSvcs->Insert( new SvcInfo( aImplName, aLanguages ),
											pAvailHyphSvcs->Count() );

						// TL_TODO:
						// when HRO has the functionality provided to unload
						// the DLLs it should be done here!
					}
				}
			}
		}
	}
}


void LngSvcMgr::GetAvailableThesSvcs_Impl()
{
	if (!pAvailThesSvcs)
	{
		pAvailThesSvcs = new SvcInfoArray;

		Reference< XMultiServiceFactory >  xFac( ::legacy_binfilters::getLegacyProcessServiceFactory() );
		if (xFac.is())
		{
			Reference< XContentEnumerationAccess > xEnumAccess( xFac, UNO_QUERY );
			Reference< XEnumeration > xEnum;
			if (xEnumAccess.is())
				xEnum = xEnumAccess->createContentEnumeration(
						A2OU( SN_THESAURUS ) );

			if (xEnum.is())
			{
				while (xEnum->hasMoreElements())
				{
					Any aCurrent = xEnum->nextElement();
					Reference< XSingleServiceFactory > xFactory;

					if (!::cppu::extractInterface( xFactory, aCurrent ))
						continue;

					Reference< XThesaurus > xSvc( xFactory->createInstance(),
												  UNO_QUERY );
					if (xSvc.is())
					{
						OUString 			aImplName;
						Sequence< INT16 > 	aLanguages;
						Reference< XServiceInfo > xInfo( xSvc, UNO_QUERY );
						if (xInfo.is())
							aImplName = xInfo->getImplementationName();
						DBG_ASSERT( aImplName.getLength(),
								"empty implementation name" );
						Reference< XSupportedLocales > xSuppLoc( xSvc, UNO_QUERY );
						DBG_ASSERT( xSuppLoc.is(), "interfaces not supported" );
						if (xSuppLoc.is()) {
                            Sequence<Locale> aLocaleSequence(xSuppLoc->getLocales());
							aLanguages = LocaleSeqToLangSeq( aLocaleSequence );
                        }

						pAvailThesSvcs->Insert( new SvcInfo( aImplName, aLanguages ),
											pAvailThesSvcs->Count() );

						// TL_TODO:
						// when HRO has the functionality provided to unload
						// the DLLs it should be done here!
					}
				}
			}
		}
	}
}


void LngSvcMgr::SetCfgServiceLists( SpellCheckerDispatcher &rSpellDsp )
{
    SvtLinguConfig aCfg;

    String  aNode( String::CreateFromAscii( "ServiceManager/SpellCheckerList" ) );
    Sequence< OUString > aNames( aCfg.GetNodeNames( aNode ) );
    const OUString *pNames = aNames.getConstArray();
    INT32 nLen = aNames.getLength();

    Sequence< Any > aValues/*( aCfg.GetProperties( aNames ) )*/;
    if (nLen  &&  nLen == aValues.getLength())
    {
        const Any *pValues = aValues.getConstArray();
        for (INT32 i = 0;  i < nLen;  ++i)
        {
            Sequence< OUString > aSvcImplNames;
            if (pValues[i] >>= aSvcImplNames)
            {
#if OSL_DEBUG_LEVEL > 1
                INT32 nSvcs = aSvcImplNames.getLength();
                const OUString *pSvcImplNames = aSvcImplNames.getConstArray();
#endif
                Locale aLocale( CreateLocale( MsLangId::convertIsoStringToLanguage(pNames[i]) ) );
                rSpellDsp.SetServiceList( aLocale, aSvcImplNames );
            }
        }
    }
}


void LngSvcMgr::SetCfgServiceLists( HyphenatorDispatcher &rHyphDsp )
{
    SvtLinguConfig aCfg;

    String  aNode( String::CreateFromAscii( "ServiceManager/HyphenatorList" ) );
    Sequence< OUString > aNames( aCfg.GetNodeNames( aNode ) );
    const OUString *pNames = aNames.getConstArray();
    INT32 nLen = aNames.getLength();

    Sequence< Any > aValues/*( aCfg.GetProperties( aNames ) )*/;
    if (nLen  &&  nLen == aValues.getLength())
    {
        Sequence< OUString > aSvcImplNames( 1 );
        OUString *pSvcImplNames = aSvcImplNames.getArray();

        const Any *pValues = aValues.getConstArray();
        for (INT32 i = 0;  i < nLen;  ++i)
        {
            OUString aSvcImplName;
            if (pValues[i] >>= aSvcImplName)
            {
                pSvcImplNames[0] = aSvcImplName;
                Locale aLocale( CreateLocale( MsLangId::convertIsoStringToLanguage(pNames[i]) ) );
                rHyphDsp.SetServiceList( aLocale, aSvcImplNames );
            }
        }
    }
}


void LngSvcMgr::SetCfgServiceLists( ThesaurusDispatcher &rThesDsp )
{
    SvtLinguConfig aCfg;

    String  aNode( String::CreateFromAscii( "ServiceManager/ThesaurusList" ) );
    Sequence< OUString > aNames( aCfg.GetNodeNames( aNode ) );
    const OUString *pNames = aNames.getConstArray();
    INT32 nLen = aNames.getLength();

    Sequence< Any > aValues/*( aCfg.GetProperties( aNames ) )*/;
    if (nLen  &&  nLen == aValues.getLength())
    {
        const Any *pValues = aValues.getConstArray();
        for (INT32 i = 0;  i < nLen;  ++i)
        {
            Sequence< OUString > aSvcImplNames;
            if (pValues[i] >>= aSvcImplNames)
            {
#if OSL_DEBUG_LEVEL > 1
                INT32 nSvcs = aSvcImplNames.getLength();
                const OUString *pSvcImplNames = aSvcImplNames.getConstArray();
#endif
                Locale aLocale( CreateLocale( MsLangId::convertIsoStringToLanguage(pNames[i]) ) );
                rThesDsp.SetServiceList( aLocale, aSvcImplNames );
            }
        }
    }
}


Reference< XSpellChecker > SAL_CALL
	LngSvcMgr::getSpellChecker()
		throw(RuntimeException)
{
	MutexGuard	aGuard( GetLinguMutex() );
#if OSL_DEBUG_LEVEL > 1
	getAvailableLocales( A2OU( SN_SPELLCHECKER ));
#endif

	Reference< XSpellChecker >	xRes;
	if (!bDisposing)
	{
		if (!xSpellDsp.is())
			GetSpellCheckerDsp_Impl();
		xRes = xSpellDsp;
	}
	return xRes;
}


Reference< XHyphenator > SAL_CALL
	LngSvcMgr::getHyphenator()
		throw(RuntimeException)
{
	MutexGuard	aGuard( GetLinguMutex() );
#if OSL_DEBUG_LEVEL > 1
	getAvailableLocales( A2OU( SN_HYPHENATOR ));
#endif

	Reference< XHyphenator >	xRes;
	if (!bDisposing)
	{
		if (!xHyphDsp.is())
			GetHyphenatorDsp_Impl();
		xRes = xHyphDsp;
	}
	return xRes;
}


Reference< XThesaurus > SAL_CALL
	LngSvcMgr::getThesaurus()
		throw(RuntimeException)
{
	MutexGuard	aGuard( GetLinguMutex() );
#if OSL_DEBUG_LEVEL > 1
	getAvailableLocales( A2OU( SN_THESAURUS ));
#endif

	Reference< XThesaurus >	xRes;
	if (!bDisposing)
	{
		if (!xThesDsp.is())
			GetThesaurusDsp_Impl();
		xRes = xThesDsp;
	}
	return xRes;
}


sal_Bool SAL_CALL
	LngSvcMgr::addLinguServiceManagerListener(
			const Reference< XEventListener >& xListener )
		throw(RuntimeException)
{
	MutexGuard	aGuard( GetLinguMutex() );

	BOOL bRes = FALSE;
	if (!bDisposing  &&  xListener.is())
	{
		if (!pListenerHelper)
			GetListenerHelper_Impl();
		bRes = pListenerHelper->AddLngSvcMgrListener( xListener );
	}
	return bRes;
}


sal_Bool SAL_CALL
	LngSvcMgr::removeLinguServiceManagerListener(
			const Reference< XEventListener >& xListener )
		throw(RuntimeException)
{
	MutexGuard	aGuard( GetLinguMutex() );

	BOOL bRes = FALSE;
	if (!bDisposing  &&  xListener.is())
	{
		DBG_ASSERT( pListenerHelper, "listener removed without being added" );
		if (!pListenerHelper)
			GetListenerHelper_Impl();
		bRes = pListenerHelper->RemoveLngSvcMgrListener( xListener );
	}
	return bRes;
}


Sequence< OUString > SAL_CALL
	LngSvcMgr::getAvailableServices(
			const OUString& rServiceName,
			const Locale& rLocale )
		throw(RuntimeException)
{
	MutexGuard	aGuard( GetLinguMutex() );

	Sequence< OUString > aRes;
	const SvcInfoArray *pInfoArray = 0;

	if (0 == rServiceName.compareToAscii( SN_SPELLCHECKER ))
	{
		if (!pAvailSpellSvcs)
			GetAvailableSpellSvcs_Impl();
		pInfoArray = pAvailSpellSvcs;
	}
	else if (0 == rServiceName.compareToAscii( SN_HYPHENATOR ))
	{
		if (!pAvailHyphSvcs)
			GetAvailableHyphSvcs_Impl();
		pInfoArray = pAvailHyphSvcs;
	}
	else if (0 == rServiceName.compareToAscii( SN_THESAURUS ))
	{
		if (!pAvailThesSvcs)
			GetAvailableThesSvcs_Impl();
		pInfoArray = pAvailThesSvcs;
	}

	if (pInfoArray)
	{
		// resize to max number of entries
		USHORT nMaxCnt = pInfoArray->Count();
		aRes.realloc( nMaxCnt );
		OUString *pImplName = aRes.getArray();

		USHORT nCnt = 0;
		INT16 nLanguage = LocaleToLanguage( rLocale );
		for (USHORT i = 0;  i < nMaxCnt;  ++i)
		{
			const SvcInfo *pInfo = pInfoArray->GetObject(i);
			if (LANGUAGE_NONE == nLanguage
				|| (pInfo && pInfo->HasLanguage( nLanguage )))
			{
				pImplName[ nCnt++ ] = pInfo->aSvcImplName;
			}
		}

		// resize to actual number of entries
		if (nCnt && nCnt != nMaxCnt)
			aRes.realloc( nCnt );
	}

	return aRes;
}


Sequence< Locale > SAL_CALL
	LngSvcMgr::getAvailableLocales(
			const OUString& rServiceName )
		throw(RuntimeException)
{
	MutexGuard	aGuard( GetLinguMutex() );

	Sequence< Locale > aRes;

	Sequence< Locale >  *pAvailLocales		= NULL;
	BOOL				*pHasAvailLocales	= NULL;
	if (0 == rServiceName.compareToAscii( SN_SPELLCHECKER ))
	{
		pAvailLocales		= &aAvailSpellLocales;
		pHasAvailLocales	= &bHasAvailSpellLocales;
	}
	else if (0 == rServiceName.compareToAscii( SN_HYPHENATOR ))
	{
		pAvailLocales		= &aAvailHyphLocales;
		pHasAvailLocales	= &bHasAvailHyphLocales;
	}
	else if (0 == rServiceName.compareToAscii( SN_THESAURUS ))
	{
		pAvailLocales		= &aAvailThesLocales;
		pHasAvailLocales	= &bHasAvailThesLocales;
	}

	if (pAvailLocales  &&  pHasAvailLocales)
	{
		if (!*pHasAvailLocales)
		{
			*pAvailLocales = GetAvailLocales(
					getAvailableServices( rServiceName, Locale() ) );
			*pHasAvailLocales = TRUE;
		}
		aRes = *pAvailLocales;
	}

	return aRes;
}

static BOOL IsEqSvcList( const Sequence< OUString > &rList1,
                        const Sequence< OUString > &rList2 )
{
    // returns TRUE iff both sequences are equal

    BOOL bRes = FALSE;
    INT32 nLen = rList1.getLength();
    if (rList2.getLength() == nLen)
    {
        const OUString *pStr1 = rList1.getConstArray();
        const OUString *pStr2 = rList2.getConstArray();
        bRes = TRUE;
        for (INT32 i = 0;  i < nLen  &&  bRes;  ++i)
        {
            if (*pStr1++ != *pStr2++)
                bRes = FALSE;
        }
    }
    return bRes;
}


void SAL_CALL
	LngSvcMgr::setConfiguredServices(
			const OUString& rServiceName,
			const Locale& rLocale,
			const Sequence< OUString >& rServiceImplNames )
		throw(RuntimeException)
{
	MutexGuard	aGuard( GetLinguMutex() );

#if OSL_DEBUG_LEVEL > 1
    const OUString *pImplNames = rServiceImplNames.getConstArray();
#endif

	INT16 nLanguage = LocaleToLanguage( rLocale );
    if (LANGUAGE_NONE != nLanguage)
	{
		if (0 == rServiceName.compareToAscii( SN_SPELLCHECKER ))
		{
			if (!xSpellDsp.is())
				GetSpellCheckerDsp_Impl();
            BOOL bChanged = !IsEqSvcList( rServiceImplNames,
                                          pSpellDsp->GetServiceList( rLocale ) );
			pSpellDsp->SetServiceList( rLocale, rServiceImplNames );
            SaveCfgSvcs( A2OU( SN_SPELLCHECKER ) );

            if (pListenerHelper  &&  bChanged)
                pListenerHelper->AddLngSvcEvt(
                        LinguServiceEventFlags::SPELL_CORRECT_WORDS_AGAIN |
                        LinguServiceEventFlags::SPELL_WRONG_WORDS_AGAIN );
		}
		else if (0 == rServiceName.compareToAscii( SN_HYPHENATOR ))
		{
			if (!xHyphDsp.is())
				GetHyphenatorDsp_Impl();
            BOOL bChanged = !IsEqSvcList( rServiceImplNames,
                                          pHyphDsp->GetServiceList( rLocale ) );
			pHyphDsp->SetServiceList( rLocale, rServiceImplNames );
            SaveCfgSvcs( A2OU( SN_HYPHENATOR ) );

            if (pListenerHelper  &&  bChanged)
                pListenerHelper->AddLngSvcEvt(
                        LinguServiceEventFlags::HYPHENATE_AGAIN );
		}
		else if (0 == rServiceName.compareToAscii( SN_THESAURUS ))
		{
			if (!xThesDsp.is())
				GetThesaurusDsp_Impl();
			pThesDsp->SetServiceList( rLocale, rServiceImplNames );
            SaveCfgSvcs( A2OU( SN_THESAURUS ) );
		}
	}
}


BOOL LngSvcMgr::SaveCfgSvcs( const String &rServiceName )
{
	BOOL bRes = FALSE;

	LinguDispatcher *pDsp = 0;
	Sequence< Locale > aLocales;

	if (0 == rServiceName.CompareToAscii( SN_SPELLCHECKER ))
	{
		if (!pSpellDsp)
			GetSpellCheckerDsp_Impl();
		pDsp = pSpellDsp;
//        aLocales = xSpellDsp->getLocales();
        aLocales = getAvailableLocales( A2OU( SN_SPELLCHECKER ) );
	}
	else if (0 == rServiceName.CompareToAscii( SN_HYPHENATOR ))
	{
		if (!pHyphDsp)
			GetHyphenatorDsp_Impl();
		pDsp = pHyphDsp;
//        aLocales = xHyphDsp->getLocales();
        aLocales = getAvailableLocales( A2OU( SN_HYPHENATOR ) );
	}
	else if (0 == rServiceName.CompareToAscii( SN_THESAURUS ))
	{
		if (!pThesDsp)
			GetThesaurusDsp_Impl();
		pDsp = pThesDsp;
//        aLocales = xThesDsp->getLocales();
        aLocales = getAvailableLocales( A2OU( SN_THESAURUS ) );
	}

	if (pDsp  &&  aLocales.getLength())
	{
        SvtLinguConfig aCfg;

        INT32 nLen = aLocales.getLength();
		const Locale *pLocale = aLocales.getConstArray();

        Sequence< PropertyValue > aValues( nLen );
        PropertyValue *pValues = aValues.getArray();
        PropertyValue *pValue  = pValues;

        // get node name to be used
        const char *pNodeName = NULL;
        if (pDsp == pSpellDsp)
            pNodeName = "ServiceManager/SpellCheckerList";
        else if (pDsp == pThesDsp)
            pNodeName = "ServiceManager/ThesaurusList";
        else if (pDsp == pHyphDsp)
            pNodeName = "ServiceManager/HyphenatorList";
        else
            DBG_ERROR( "node name missing" );
        OUString aNodeName( A2OU(pNodeName) );

		for (INT32 i = 0;  i < nLen;  ++i)
		{
			Sequence< OUString > aSvcImplNames;
			aSvcImplNames = pDsp->GetServiceList( pLocale[i] );

#if OSL_DEBUG_LEVEL > 1
			INT32 nSvcs = aSvcImplNames.getLength();
			const OUString *pSvcImplName = aSvcImplNames.getConstArray();
			for (INT32 j = 0;  j < nSvcs;  ++j)
			{
				OUString aImplName( pSvcImplName[j] );
			}
#endif
			// build value to be written back to configuration
			Any aCfgAny;
			if (pDsp == pHyphDsp)
			{
                OUString aTmp;
				if (aSvcImplNames.getLength())
                    aTmp = aSvcImplNames.getConstArray()[0];
                aCfgAny <<= aTmp;
			}
			else
				aCfgAny <<= aSvcImplNames;
			DBG_ASSERT( aCfgAny.hasValue(), "missing value for 'Any' type" );

			OUString aCfgLocaleStr( MsLangId::convertLanguageToIsoString(
										LocaleToLanguage( pLocale[i] ) ) );
            pValue->Value = aCfgAny;
            pValue->Name  = aNodeName;
            pValue->Name += OUString::valueOf( (sal_Unicode) '/' );
            pValue->Name += aCfgLocaleStr;
            pValue++;
		}
        // change, add new or replace existing entries.
        bRes |= aCfg.ReplaceSetProperties( aNodeName, aValues );
	}

	return bRes;
}


static Sequence< OUString > GetLangSvcList( const Any &rVal )
{
	Sequence< OUString > aRes;

	if (rVal.hasValue())
	{
		rVal >>= aRes;
#if OSL_DEBUG_LEVEL > 1
		INT32 nSvcs = aRes.getLength();
		if (nSvcs)
		{
			const OUString *pSvcName = aRes.getConstArray();
			for (INT32 j = 0;  j < nSvcs;  ++j)
			{
				OUString aImplName( pSvcName[j] );
                DBG_ASSERT( aImplName.getLength(), "service impl-name missing" );
			}
		}
#endif
	}

	return aRes;
}


static Sequence< OUString > GetLangSvc( const Any &rVal )
{
    OUString aImplName;
    if (rVal.hasValue())
    {
        rVal >>= aImplName;
        //DBG_ASSERT( aImplName.getLength(), "service impl-name missing" );
    }

    Sequence< OUString > aRes( aImplName.getLength() ? 1 : 0 );
    if (aRes.getLength())
        aRes.getArray()[0] = aImplName;

	return aRes;
}


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

static BOOL lcl_HasProperty( SvtLinguConfig &rItem,
        const OUString &rNode, const OUString &rPropName )
{
    BOOL bRes = FALSE;
    const Sequence< OUString > aChilds( rItem.GetNodeNames( rNode ) );
    INT32 nChilds = aChilds.getLength();
    const OUString *pChilds = aChilds.getConstArray();
    for (INT32 i = 0;  i < nChilds  &&  !bRes;  ++i)
    {
        if (rPropName == pChilds[i])
            bRes = TRUE;
    }
    return bRes;
}


Sequence< OUString > SAL_CALL
	LngSvcMgr::getConfiguredServices(
			const OUString& rServiceName,
			const Locale& rLocale )
		throw(RuntimeException)
{
	MutexGuard	aGuard( GetLinguMutex() );

	Sequence< OUString > aSvcImplNames;

	INT16 nLanguage = LocaleToLanguage( rLocale );
    OUString aCfgLocale( MsLangId::convertLanguageToIsoString( nLanguage ) );

    SvtLinguConfig aCfg;

	Sequence< Any > aValues;
	Sequence< OUString > aName( 1 );
	OUString *pNames = aName.getArray();
	if ( 0 == rServiceName.compareToAscii( SN_SPELLCHECKER ) )
	{
        OUString aNode( OUString::createFromAscii( "ServiceManager/SpellCheckerList" ));
        if (lcl_HasProperty( aCfg, aNode, aCfgLocale ))
        {
            OUString aPropName( aNode );
            aPropName += OUString::valueOf( (sal_Unicode) '/' );
            aPropName += aCfgLocale;
            pNames[0] = aPropName;
            //aValues = aCfg.GetProperties( aName );
            if (aValues.getLength())
                aSvcImplNames = GetLangSvcList( aValues.getConstArray()[0] );
        }
	}
	else if ( 0 == rServiceName.compareToAscii( SN_HYPHENATOR ) )
	{
        OUString aNode( OUString::createFromAscii( "ServiceManager/HyphenatorList" ));
        if (lcl_HasProperty( aCfg, aNode, aCfgLocale ))
        {
            OUString aPropName( aNode );
            aPropName += OUString::valueOf( (sal_Unicode) '/' );
            aPropName += aCfgLocale;
            pNames[0] = aPropName;
            //aValues = aCfg.GetProperties( aName );
            if (aValues.getLength())
                aSvcImplNames = GetLangSvc( aValues.getConstArray()[0] );
        }
    }
	else if ( 0 == rServiceName.compareToAscii( SN_THESAURUS ) )
	{
        OUString aNode( OUString::createFromAscii( "ServiceManager/ThesaurusList" ));
        if (lcl_HasProperty( aCfg, aNode, aCfgLocale ))
        {
            OUString aPropName( aNode );
            aPropName += OUString::valueOf( (sal_Unicode) '/' );
            aPropName += aCfgLocale;
            pNames[0] = aPropName;
            //aValues = aCfg.GetProperties( aName );
            if (aValues.getLength())
                aSvcImplNames = GetLangSvcList( aValues.getConstArray()[0] );
        }
    }

#if OSL_DEBUG_LEVEL > 1
    const OUString *pImplNames = aSvcImplNames.getConstArray();
#endif
	return aSvcImplNames;
}


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

	if (!bDisposing)
	{
		bDisposing = TRUE;

		// require listeners to release this object
		EventObject aEvtObj( (XLinguServiceManager *) this );
		aEvtListeners.disposeAndClear( aEvtObj );

		if (pListenerHelper)
			pListenerHelper->DisposeAndClear( aEvtObj );
	}
}


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

	if (!bDisposing  &&  xListener.is())
	{
		aEvtListeners.addInterface(
			/*::getCppuType((const ::com::sun::star::uno::Reference<
				::com::sun::star::lang::XEventListener >*)0), */
			xListener );
	}
}


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

	if (xListener.is())
	{
		aEvtListeners.removeInterface(
			/*::getCppuType((const ::com::sun::star::uno::Reference<
				::com::sun::star::lang::XEventListener >*)0), */
			xListener );
	}
}


BOOL LngSvcMgr::AddLngSvcEvtBroadcaster(
			const Reference< XLinguServiceEventBroadcaster > &rxBroadcaster )
{
	BOOL bRes = FALSE;
	if (rxBroadcaster.is())
	{
		if (!pListenerHelper)
			GetListenerHelper_Impl();
		bRes = pListenerHelper->AddLngSvcEvtBroadcaster( rxBroadcaster );
	}
	return bRes;
}


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


sal_Bool SAL_CALL
	LngSvcMgr::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;
}


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


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

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


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



sal_Bool SAL_CALL LngSvcMgr_writeInfo(
			void * /*pServiceManager*/,
			registry::XRegistryKey * pRegistryKey )
{
	try
	{
		String aImpl( '/' );
		aImpl += LngSvcMgr::getImplementationName_Static().getStr();
		aImpl.AppendAscii( "/UNO/SERVICES" );
		Reference< registry::XRegistryKey > xNewKey =
			pRegistryKey->createKey( aImpl );
		uno::Sequence< OUString > aServices = LngSvcMgr::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 LngSvcMgr_getFactory(
			const sal_Char * pImplName,
			XMultiServiceFactory * pServiceManager,
			void * /*pRegistryKey*/ )
{

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


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

}
