xref: /trunk/main/lingucomponent/source/spellcheck/spell/sspellimp.cxx (revision 1ecadb572e7010ff3b3382ad9bf179dbc6efadbb)
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