1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_lingucomponent.hxx"
30 
31 #include <com/sun/star/uno/Reference.h>
32 #include <com/sun/star/linguistic2/XSearchableDictionaryList.hpp>
33 
34 #include <com/sun/star/linguistic2/SpellFailure.hpp>
35 #include <cppuhelper/factory.hxx>	// helper for factories
36 #include <com/sun/star/registry/XRegistryKey.hpp>
37 #include <tools/debug.hxx>
38 #include <unotools/processfactory.hxx>
39 #include <osl/mutex.hxx>
40 
41 #include <lingutil.hxx>
42 #include <hunspell.hxx>
43 #include <dictmgr.hxx>
44 #include <sspellimp.hxx>
45 
46 #include <linguistic/lngprops.hxx>
47 #include <linguistic/spelldta.hxx>
48 #include <i18npool/mslangid.hxx>
49 #include <unotools/pathoptions.hxx>
50 #include <unotools/lingucfg.hxx>
51 #include <unotools/useroptions.hxx>
52 #include <osl/file.hxx>
53 #include <rtl/ustrbuf.hxx>
54 #include <rtl/textenc.h>
55 
56 #include <list>
57 #include <set>
58 #include <string.h>
59 
60 using namespace utl;
61 using namespace osl;
62 using namespace rtl;
63 using namespace com::sun::star;
64 using namespace com::sun::star::beans;
65 using namespace com::sun::star::lang;
66 using namespace com::sun::star::uno;
67 using namespace com::sun::star::linguistic2;
68 using namespace linguistic;
69 
70 // XML-header of SPELLML queries
71 #define SPELLML_HEADER "<?xml?>"
72 
73 ///////////////////////////////////////////////////////////////////////////
74 
75 SpellChecker::SpellChecker() :
76 	aEvtListeners	( GetLinguMutex() )
77 {
78     aDicts = NULL;
79 	aDEncs = NULL;
80 	aDLocs = NULL;
81 	aDNames = NULL;
82 	bDisposing = sal_False;
83 	pPropHelper = NULL;
84     numdict = 0;
85 }
86 
87 
88 SpellChecker::~SpellChecker()
89 {
90     if (aDicts)
91     {
92        for (int i = 0; i < numdict; i++)
93        {
94             if (aDicts[i]) delete aDicts[i];
95             aDicts[i] = NULL;
96        }
97        delete[] aDicts;
98     }
99     aDicts = NULL;
100     numdict = 0;
101     if (aDEncs) delete[] aDEncs;
102     aDEncs = NULL;
103     if (aDLocs) delete[] aDLocs;
104     aDLocs = NULL;
105     if (aDNames) delete[] aDNames;
106     aDNames = NULL;
107     if (pPropHelper)
108         pPropHelper->RemoveAsPropListener();
109 }
110 
111 
112 PropertyHelper_Spell & SpellChecker::GetPropHelper_Impl()
113 {
114 	if (!pPropHelper)
115 	{
116 		Reference< XPropertySet	>	xPropSet( GetLinguProperties(), UNO_QUERY );
117 
118 		pPropHelper	= new PropertyHelper_Spell( (XSpellChecker *) this, xPropSet );
119 		xPropHelper = pPropHelper;
120 		pPropHelper->AddAsPropListener();	//! after a reference is established
121 	}
122 	return *pPropHelper;
123 }
124 
125 
126 Sequence< Locale > SAL_CALL SpellChecker::getLocales()
127 		throw(RuntimeException)
128 {
129     MutexGuard  aGuard( GetLinguMutex() );
130 
131     // this routine should return the locales supported by the installed
132     // dictionaries.
133 
134     if (!numdict)
135     {
136         SvtLinguConfig aLinguCfg;
137 
138         // get list of extension dictionaries-to-use
139 		// (or better speaking: the list of dictionaries using the
140 		// new configuration entries).
141         std::list< SvtLinguConfigDictionaryEntry > aDics;
142         uno::Sequence< rtl::OUString > aFormatList;
143         aLinguCfg.GetSupportedDictionaryFormatsFor( A2OU("SpellCheckers"),
144                 A2OU("org.openoffice.lingu.MySpellSpellChecker"), aFormatList );
145         sal_Int32 nLen = aFormatList.getLength();
146         for (sal_Int32 i = 0;  i < nLen;  ++i)
147         {
148             std::vector< SvtLinguConfigDictionaryEntry > aTmpDic(
149                     aLinguCfg.GetActiveDictionariesByFormat( aFormatList[i] ) );
150             aDics.insert( aDics.end(), aTmpDic.begin(), aTmpDic.end() );
151         }
152 
153         //!! for compatibility with old dictionaries (the ones not using extensions
154         //!! or new configuration entries, but still using the dictionary.lst file)
155 		//!! Get the list of old style spell checking dictionaries to use...
156         std::vector< SvtLinguConfigDictionaryEntry > aOldStyleDics(
157 				GetOldStyleDics( "DICT" ) );
158 
159 		// to prefer dictionaries with configuration entries we will only
160 		// use those old style dictionaries that add a language that
161 		// is not yet supported by the list od new style dictionaries
162 		MergeNewStyleDicsAndOldStyleDics( aDics, aOldStyleDics );
163 
164         numdict = aDics.size();
165         if (numdict)
166         {
167             // get supported locales from the dictionaries-to-use...
168             sal_Int32 k = 0;
169             std::set< rtl::OUString, lt_rtl_OUString > aLocaleNamesSet;
170             std::list< SvtLinguConfigDictionaryEntry >::const_iterator aDictIt;
171             for (aDictIt = aDics.begin();  aDictIt != aDics.end();  ++aDictIt)
172             {
173                 uno::Sequence< rtl::OUString > aLocaleNames( aDictIt->aLocaleNames );
174                 sal_Int32 nLen2 = aLocaleNames.getLength();
175                 for (k = 0;  k < nLen2;  ++k)
176                 {
177                     aLocaleNamesSet.insert( aLocaleNames[k] );
178                 }
179             }
180             // ... and add them to the resulting sequence
181             aSuppLocales.realloc( aLocaleNamesSet.size() );
182             std::set< rtl::OUString, lt_rtl_OUString >::const_iterator aItB;
183             k = 0;
184             for (aItB = aLocaleNamesSet.begin();  aItB != aLocaleNamesSet.end();  ++aItB)
185             {
186 				Locale aTmp( MsLangId::convertLanguageToLocale(
187 						MsLangId::convertIsoStringToLanguage( *aItB )));
188                 aSuppLocales[k++] = aTmp;
189             }
190 
191             //! For each dictionary and each locale we need a seperate entry.
192             //! If this results in more than one dictionary per locale than (for now)
193 			//! it is undefined which dictionary gets used.
194 			//! In the future the implementation should support using several dictionaries
195 			//! for one locale.
196 			numdict = 0;
197             for (aDictIt = aDics.begin();  aDictIt != aDics.end();  ++aDictIt)
198 				numdict = numdict + aDictIt->aLocaleNames.getLength();
199 
200             // add dictionary information
201             aDicts  = new Hunspell* [numdict];
202             aDEncs  = new rtl_TextEncoding [numdict];
203             aDLocs  = new Locale [numdict];
204             aDNames = new OUString [numdict];
205             k = 0;
206             for (aDictIt = aDics.begin();  aDictIt != aDics.end();  ++aDictIt)
207             {
208                 if (aDictIt->aLocaleNames.getLength() > 0 &&
209                     aDictIt->aLocations.getLength() > 0)
210                 {
211                     uno::Sequence< rtl::OUString > aLocaleNames( aDictIt->aLocaleNames );
212                     sal_Int32 nLocales = aLocaleNames.getLength();
213 
214                     // currently only one language per dictionary is supported in the actual implementation...
215                     // Thus here we work-around this by adding the same dictionary several times.
216                     // Once for each of it's supported locales.
217                     for (sal_Int32 i = 0;  i < nLocales;  ++i)
218                     {
219                         aDicts[k]  = NULL;
220                         aDEncs[k]  = RTL_TEXTENCODING_DONTKNOW;
221                         aDLocs[k]  = MsLangId::convertLanguageToLocale(
222                                         MsLangId::convertIsoStringToLanguage( aLocaleNames[i] ));
223                         // also both files have to be in the same directory and the
224                         // file names must only differ in the extension (.aff/.dic).
225                         // Thus we use the first location only and strip the extension part.
226                         rtl::OUString aLocation = aDictIt->aLocations[0];
227                         sal_Int32 nPos = aLocation.lastIndexOf( '.' );
228                         aLocation = aLocation.copy( 0, nPos );
229                         aDNames[k] = aLocation;
230 
231                         ++k;
232                     }
233                 }
234             }
235             DBG_ASSERT( k == numdict, "index mismatch?" );
236         }
237         else
238         {
239             /* no dictionary found so register no dictionaries */
240             numdict = 0;
241             aDicts  = NULL;
242             aDEncs  = NULL;
243             aDLocs  = NULL;
244             aDNames = NULL;
245             aSuppLocales.realloc(0);
246         }
247     }
248 
249 	return aSuppLocales;
250 }
251 
252 
253 sal_Bool SAL_CALL SpellChecker::hasLocale(const Locale& rLocale)
254 		throw(RuntimeException)
255 {
256 	MutexGuard	aGuard( GetLinguMutex() );
257 
258 	sal_Bool bRes = sal_False;
259 	if (!aSuppLocales.getLength())
260 		getLocales();
261 
262 	sal_Int32 nLen = aSuppLocales.getLength();
263 	for (sal_Int32 i = 0;  i < nLen;  ++i)
264 	{
265 		const Locale *pLocale = aSuppLocales.getConstArray();
266 		if (rLocale == pLocale[i])
267 		{
268 			bRes = sal_True;
269 			break;
270 		}
271 	}
272 	return bRes;
273 }
274 
275 
276 sal_Int16 SpellChecker::GetSpellFailure( const OUString &rWord, const Locale &rLocale )
277 {
278     Hunspell * pMS = NULL;
279     rtl_TextEncoding eEnc = RTL_TEXTENCODING_DONTKNOW;
280 
281 	// initialize a myspell object for each dictionary once
282     // (note: mutex is held higher up in isValid)
283 
284 	sal_Int16 nRes = -1;
285 
286     // first handle smart quotes both single and double
287 	OUStringBuffer rBuf(rWord);
288     sal_Int32 n = rBuf.getLength();
289     sal_Unicode c;
290     for (sal_Int32 ix=0; ix < n; ix++)
291     {
292 	    c = rBuf.charAt(ix);
293         if ((c == 0x201C) || (c == 0x201D))
294             rBuf.setCharAt(ix,(sal_Unicode)0x0022);
295         if ((c == 0x2018) || (c == 0x2019))
296             rBuf.setCharAt(ix,(sal_Unicode)0x0027);
297     }
298     OUString nWord(rBuf.makeStringAndClear());
299 
300 	if (n)
301 	{
302         for (sal_Int32 i = 0; i < numdict; ++i)
303         {
304             pMS = NULL;
305             eEnc = RTL_TEXTENCODING_DONTKNOW;
306 
307             if (rLocale == aDLocs[i])
308             {
309                 if (!aDicts[i])
310                 {
311                     OUString dicpath = aDNames[i] + A2OU(".dic");
312                     OUString affpath = aDNames[i] + A2OU(".aff");
313                     OUString dict;
314                     OUString aff;
315                     osl::FileBase::getSystemPathFromFileURL(dicpath,dict);
316                     osl::FileBase::getSystemPathFromFileURL(affpath,aff);
317                     OString aTmpaff(OU2ENC(aff,osl_getThreadTextEncoding()));
318                     OString aTmpdict(OU2ENC(dict,osl_getThreadTextEncoding()));
319 
320 #if defined(WNT)
321                     // workaround for Windows specifc problem that the
322                     // path length in calls to 'fopen' is limted to somewhat
323                     // about 120+ characters which will usually be exceed when
324                     // using dictionaries as extensions.
325                     aTmpaff = Win_GetShortPathName( aff );
326                     aTmpdict = Win_GetShortPathName( dict );
327 #endif
328 
329                     aDicts[i] = new Hunspell(aTmpaff.getStr(),aTmpdict.getStr());
330                     aDEncs[i] = RTL_TEXTENCODING_DONTKNOW;
331                     if (aDicts[i])
332                         aDEncs[i] = getTextEncodingFromCharset(aDicts[i]->get_dic_encoding());
333                 }
334                 pMS = aDicts[i];
335                 eEnc = aDEncs[i];
336             }
337 
338             if (pMS)
339             {
340                 // we don't want to work with a default text encoding since following incorrect
341                 // results may occur only for specific text and thus may be hard to notice.
342                 // Thus better always make a clean exit here if the text encoding is in question.
343                 // Hopefully something not working at all will raise proper attention quickly. ;-)
344                 DBG_ASSERT( eEnc != RTL_TEXTENCODING_DONTKNOW, "failed to get text encoding! (maybe incorrect encoding string in file)" );
345                 if (eEnc == RTL_TEXTENCODING_DONTKNOW)
346                     return -1;
347 
348                 OString aWrd(OU2ENC(nWord,eEnc));
349                 int rVal = pMS->spell((char*)aWrd.getStr());
350                 if (rVal != 1)
351                     nRes = SpellFailure::SPELLING_ERROR;
352                 else
353                     return -1;
354                 pMS = NULL;
355             }
356         }
357 	}
358 
359 	return nRes;
360 }
361 
362 
363 sal_Bool SAL_CALL SpellChecker::isValid( const OUString& rWord, const Locale& rLocale,
364 			const PropertyValues& rProperties )
365 		throw(IllegalArgumentException, RuntimeException)
366 {
367 	MutexGuard	aGuard( GetLinguMutex() );
368 
369  	if (rLocale == Locale()  ||  !rWord.getLength())
370 		return sal_True;
371 
372 	if (!hasLocale( rLocale ))
373 #ifdef LINGU_EXCEPTIONS
374 		throw( IllegalArgumentException() );
375 #else
376 		return sal_True;
377 #endif
378 
379 	// return sal_False to process SPELLML requests (they are longer than the header)
380 	if (rWord.match(A2OU(SPELLML_HEADER), 0) && (rWord.getLength() > 10)) return sal_False;
381 
382 	// Get property values to be used.
383 	// These are be the default values set in the SN_LINGU_PROPERTIES
384 	// PropertySet which are overridden by the supplied ones from the
385 	// last argument.
386 	// You'll probably like to use a simplier solution than the provided
387 	// one using the PropertyHelper_Spell.
388 
389 	PropertyHelper_Spell &rHelper = GetPropHelper();
390 	rHelper.SetTmpPropVals( rProperties );
391 
392 	sal_Int16 nFailure = GetSpellFailure( rWord, rLocale );
393 	if (nFailure != -1 && !rWord.match(A2OU(SPELLML_HEADER), 0))
394 	{
395 		sal_Int16 nLang = LocaleToLanguage( rLocale );
396 		// postprocess result for errors that should be ignored
397         const bool bIgnoreError =
398                 (!rHelper.IsSpellUpperCase()  && IsUpper( rWord, nLang )) ||
399                 (!rHelper.IsSpellWithDigits() && HasDigits( rWord )) ||
400                 (!rHelper.IsSpellCapitalization()  &&  nFailure == SpellFailure::CAPTION_ERROR);
401         if (bIgnoreError)
402             nFailure = -1;
403 	}
404 
405 	return (nFailure == -1);
406 }
407 
408 
409 Reference< XSpellAlternatives >
410 	SpellChecker::GetProposals( const OUString &rWord, const Locale &rLocale )
411 {
412 	// Retrieves the return values for the 'spell' function call in case
413 	// of a misspelled word.
414 	// Especially it may give a list of suggested (correct) words:
415 
416 	Reference< XSpellAlternatives > xRes;
417     // note: mutex is held by higher up by spell which covers both
418 
419     Hunspell* pMS = NULL;
420     rtl_TextEncoding eEnc = RTL_TEXTENCODING_DONTKNOW;
421     int count = 0;
422     int numsug = 0;
423 
424     // first handle smart quotes (single and double)
425 	OUStringBuffer rBuf(rWord);
426     sal_Int32 n = rBuf.getLength();
427     sal_Unicode c;
428     for (sal_Int32 ix=0; ix < n; ix++)
429     {
430         c = rBuf.charAt(ix);
431         if ((c == 0x201C) || (c == 0x201D))
432             rBuf.setCharAt(ix,(sal_Unicode)0x0022);
433         if ((c == 0x2018) || (c == 0x2019))
434             rBuf.setCharAt(ix,(sal_Unicode)0x0027);
435     }
436     OUString nWord(rBuf.makeStringAndClear());
437 
438 	if (n)
439 	{
440         sal_Int16 nLang = LocaleToLanguage( rLocale );
441 
442 	    Sequence< OUString > aStr( 0 );
443 
444         for (int i =0; i < numdict; i++)
445         {
446             pMS = NULL;
447             eEnc = RTL_TEXTENCODING_DONTKNOW;
448             count = 0;
449 
450             if (rLocale == aDLocs[i])
451             {
452                 pMS = aDicts[i];
453                 eEnc = aDEncs[i];
454             }
455 
456 	        if (pMS)
457 	        {
458                 char ** suglst = NULL;
459                 OString aWrd(OU2ENC(nWord,eEnc));
460                 count = pMS->suggest(&suglst, (const char *) aWrd.getStr());
461 
462                 if (count)
463                 {
464                     aStr.realloc( numsug + count );
465                     OUString *pStr = aStr.getArray();
466                     for (int ii=0; ii < count; ++ii)
467                     {
468                         OUString cvtwrd(suglst[ii],strlen(suglst[ii]),eEnc);
469                         pStr[numsug + ii] = cvtwrd;
470                     }
471                     pMS->free_list(&suglst, count);
472                     numsug += count;
473                 }
474             }
475 	    }
476 
477         // now return an empty alternative for no suggestions or the list of alternatives if some found
478 	    SpellAlternatives *pAlt = new SpellAlternatives;
479         String aTmp(rWord);
480 	    pAlt->SetWordLanguage( aTmp, nLang );
481 	    pAlt->SetFailureType( SpellFailure::SPELLING_ERROR );
482 	    pAlt->SetAlternatives( aStr );
483 	    xRes = pAlt;
484         return xRes;
485 	}
486     return xRes;
487 }
488 
489 
490 Reference< XSpellAlternatives > SAL_CALL SpellChecker::spell(
491         const OUString& rWord, const Locale& rLocale,
492         const PropertyValues& rProperties )
493 		throw(IllegalArgumentException, RuntimeException)
494 {
495 	MutexGuard	aGuard( GetLinguMutex() );
496 
497  	if (rLocale == Locale()  ||  !rWord.getLength())
498 		return NULL;
499 
500 	if (!hasLocale( rLocale ))
501 #ifdef LINGU_EXCEPTIONS
502 		throw( IllegalArgumentException() );
503 #else
504 		return NULL;
505 #endif
506 
507 	Reference< XSpellAlternatives > xAlt;
508 	if (!isValid( rWord, rLocale, rProperties ))
509 	{
510 		xAlt =  GetProposals( rWord, rLocale );
511 	}
512 	return xAlt;
513 }
514 
515 
516 Reference< XInterface > SAL_CALL SpellChecker_CreateInstance(
517         const Reference< XMultiServiceFactory > & /*rSMgr*/ )
518         throw(Exception)
519 {
520 
521 	Reference< XInterface > xService = (cppu::OWeakObject*) new SpellChecker;
522 	return xService;
523 }
524 
525 
526 sal_Bool SAL_CALL SpellChecker::addLinguServiceEventListener(
527         const Reference< XLinguServiceEventListener >& rxLstnr )
528         throw(RuntimeException)
529 {
530 	MutexGuard	aGuard( GetLinguMutex() );
531 
532 	sal_Bool bRes = sal_False;
533 	if (!bDisposing && rxLstnr.is())
534 	{
535 		bRes = GetPropHelper().addLinguServiceEventListener( rxLstnr );
536 	}
537 	return bRes;
538 }
539 
540 
541 sal_Bool SAL_CALL SpellChecker::removeLinguServiceEventListener(
542         const Reference< XLinguServiceEventListener >& rxLstnr )
543         throw(RuntimeException)
544 {
545 	MutexGuard	aGuard( GetLinguMutex() );
546 
547 	sal_Bool bRes = sal_False;
548 	if (!bDisposing && rxLstnr.is())
549 	{
550 		DBG_ASSERT( xPropHelper.is(), "xPropHelper non existent" );
551 		bRes = GetPropHelper().removeLinguServiceEventListener( rxLstnr );
552 	}
553 	return bRes;
554 }
555 
556 
557 OUString SAL_CALL SpellChecker::getServiceDisplayName( const Locale& /*rLocale*/ )
558 		throw(RuntimeException)
559 {
560 	MutexGuard	aGuard( GetLinguMutex() );
561 	return A2OU( "Hunspell SpellChecker" );
562 }
563 
564 
565 void SAL_CALL SpellChecker::initialize( const Sequence< Any >& rArguments )
566 		throw(Exception, RuntimeException)
567 {
568 	MutexGuard	aGuard( GetLinguMutex() );
569 
570 	if (!pPropHelper)
571 	{
572 		sal_Int32 nLen = rArguments.getLength();
573 		if (2 == nLen)
574 		{
575 			Reference< XPropertySet	>	xPropSet;
576 			rArguments.getConstArray()[0] >>= xPropSet;
577 			//rArguments.getConstArray()[1] >>= xDicList;
578 
579 			//! Pointer allows for access of the non-UNO functions.
580 			//! And the reference to the UNO-functions while increasing
581 			//! the ref-count and will implicitly free the memory
582 			//! when the object is not longer used.
583 			pPropHelper = new PropertyHelper_Spell( (XSpellChecker *) this, xPropSet );
584 			xPropHelper = pPropHelper;
585 			pPropHelper->AddAsPropListener();	//! after a reference is established
586 		}
587         else
588         {
589 			DBG_ERROR( "wrong number of arguments in sequence" );
590         }
591 	}
592 }
593 
594 
595 void SAL_CALL SpellChecker::dispose()
596 		throw(RuntimeException)
597 {
598 	MutexGuard	aGuard( GetLinguMutex() );
599 
600 	if (!bDisposing)
601 	{
602 		bDisposing = sal_True;
603 		EventObject	aEvtObj( (XSpellChecker *) this );
604 		aEvtListeners.disposeAndClear( aEvtObj );
605 	}
606 }
607 
608 
609 void SAL_CALL SpellChecker::addEventListener( const Reference< XEventListener >& rxListener )
610 		throw(RuntimeException)
611 {
612 	MutexGuard	aGuard( GetLinguMutex() );
613 
614 	if (!bDisposing && rxListener.is())
615 		aEvtListeners.addInterface( rxListener );
616 }
617 
618 
619 void SAL_CALL SpellChecker::removeEventListener( const Reference< XEventListener >& rxListener )
620 		throw(RuntimeException)
621 {
622 	MutexGuard	aGuard( GetLinguMutex() );
623 
624 	if (!bDisposing && rxListener.is())
625 		aEvtListeners.removeInterface( rxListener );
626 }
627 
628 
629 ///////////////////////////////////////////////////////////////////////////
630 // Service specific part
631 //
632 
633 OUString SAL_CALL SpellChecker::getImplementationName()
634 		throw(RuntimeException)
635 {
636 	MutexGuard	aGuard( GetLinguMutex() );
637 
638 	return getImplementationName_Static();
639 }
640 
641 
642 sal_Bool SAL_CALL SpellChecker::supportsService( const OUString& ServiceName )
643 		throw(RuntimeException)
644 {
645 	MutexGuard	aGuard( GetLinguMutex() );
646 
647 	Sequence< OUString > aSNL = getSupportedServiceNames();
648 	const OUString * pArray = aSNL.getConstArray();
649 	for( sal_Int32 i = 0; i < aSNL.getLength(); i++ )
650 		if( pArray[i] == ServiceName )
651 			return sal_True;
652 	return sal_False;
653 }
654 
655 
656 Sequence< OUString > SAL_CALL SpellChecker::getSupportedServiceNames()
657 		throw(RuntimeException)
658 {
659 	MutexGuard	aGuard( GetLinguMutex() );
660 
661 	return getSupportedServiceNames_Static();
662 }
663 
664 
665 Sequence< OUString > SpellChecker::getSupportedServiceNames_Static()
666 		throw()
667 {
668 	MutexGuard	aGuard( GetLinguMutex() );
669 
670 	Sequence< OUString > aSNS( 1 );	// auch mehr als 1 Service moeglich
671 	aSNS.getArray()[0] = A2OU( SN_SPELLCHECKER );
672 	return aSNS;
673 }
674 
675 void * SAL_CALL SpellChecker_getFactory( const sal_Char * pImplName,
676 			XMultiServiceFactory * pServiceManager, void *  )
677 {
678 	void * pRet = 0;
679 	if ( !SpellChecker::getImplementationName_Static().compareToAscii( pImplName ) )
680 	{
681 		Reference< XSingleServiceFactory > xFactory =
682 			cppu::createOneInstanceFactory(
683 				pServiceManager,
684 				SpellChecker::getImplementationName_Static(),
685 				SpellChecker_CreateInstance,
686 				SpellChecker::getSupportedServiceNames_Static());
687 		// acquire, because we return an interface pointer instead of a reference
688 		xFactory->acquire();
689 		pRet = xFactory.get();
690 	}
691 	return pRet;
692 }
693 
694 
695 ///////////////////////////////////////////////////////////////////////////
696