1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <com/sun/star/uno/Reference.h>
21 
22 #include <com/sun/star/linguistic2/SpellFailure.hpp>
23 #include <com/sun/star/linguistic2/XLinguProperties.hpp>
24 #include <comphelper/lok.hxx>
25 #include <comphelper/processfactory.hxx>
26 #include <cppuhelper/factory.hxx>
27 #include <cppuhelper/supportsservice.hxx>
28 #include <cppuhelper/weak.hxx>
29 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
30 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
31 #include <com/sun/star/registry/XRegistryKey.hpp>
32 #include <tools/debug.hxx>
33 #include <osl/mutex.hxx>
34 #include <osl/thread.h>
35 #include <com/sun/star/ucb/XSimpleFileAccess.hpp>
36 
37 #include <lingutil.hxx>
38 #include <hunspell.hxx>
39 #include "sspellimp.hxx"
40 
41 #include <linguistic/lngprops.hxx>
42 #include <linguistic/spelldta.hxx>
43 #include <i18nlangtag/languagetag.hxx>
44 #include <svtools/strings.hrc>
45 #include <unotools/pathoptions.hxx>
46 #include <unotools/lingucfg.hxx>
47 #include <unotools/resmgr.hxx>
48 #include <unotools/useroptions.hxx>
49 #include <osl/file.hxx>
50 #include <rtl/ustrbuf.hxx>
51 #include <rtl/textenc.h>
52 #include <sal/log.hxx>
53 
54 #include <numeric>
55 #include <utility>
56 #include <vector>
57 #include <set>
58 #include <string.h>
59 
60 using namespace utl;
61 using namespace osl;
62 using namespace com::sun::star;
63 using namespace com::sun::star::beans;
64 using namespace com::sun::star::lang;
65 using namespace com::sun::star::uno;
66 using namespace com::sun::star::linguistic2;
67 using namespace linguistic;
68 
69 // XML-header of SPELLML queries
70 #if !defined SPELL_XML
71 #define SPELL_XML "<?xml?>"
72 #endif
73 
74 // only available in hunspell >= 1.5
75 #if !defined MAXWORDLEN
76 #define MAXWORDLEN 176
77 #endif
78 
SpellChecker()79 SpellChecker::SpellChecker() :
80     m_aEvtListeners(GetLinguMutex()),
81     m_bDisposing(false)
82 {
83 }
84 
DictItem(OUString i_DName,Locale i_DLoc,rtl_TextEncoding i_DEnc)85 SpellChecker::DictItem::DictItem(OUString i_DName, Locale i_DLoc, rtl_TextEncoding i_DEnc)
86     : m_aDName(std::move(i_DName))
87     , m_aDLoc(std::move(i_DLoc))
88     , m_aDEnc(i_DEnc)
89 {
90 }
91 
~SpellChecker()92 SpellChecker::~SpellChecker()
93 {
94     if (m_pPropHelper)
95     {
96         m_pPropHelper->RemoveAsPropListener();
97     }
98 }
99 
GetPropHelper_Impl()100 PropertyHelper_Spelling & SpellChecker::GetPropHelper_Impl()
101 {
102     if (!m_pPropHelper)
103     {
104         Reference< XLinguProperties >   xPropSet = GetLinguProperties();
105 
106         m_pPropHelper.reset( new PropertyHelper_Spelling( static_cast<XSpellChecker *>(this), xPropSet ) );
107         m_pPropHelper->AddAsPropListener();   //! after a reference is established
108     }
109     return *m_pPropHelper;
110 }
111 
getLocales()112 Sequence< Locale > SAL_CALL SpellChecker::getLocales()
113 {
114     MutexGuard  aGuard( GetLinguMutex() );
115 
116     // this routine should return the locales supported by the installed
117     // dictionaries.
118     if (m_DictItems.empty())
119     {
120         SvtLinguConfig aLinguCfg;
121 
122         // get list of extension dictionaries-to-use
123         // (or better speaking: the list of dictionaries using the
124         // new configuration entries).
125         std::vector< SvtLinguConfigDictionaryEntry > aDics;
126         uno::Sequence< OUString > aFormatList;
127         aLinguCfg.GetSupportedDictionaryFormatsFor( "SpellCheckers",
128                 "org.openoffice.lingu.MySpellSpellChecker", aFormatList );
129         for (auto const& format : std::as_const(aFormatList))
130         {
131             std::vector< SvtLinguConfigDictionaryEntry > aTmpDic(
132                     aLinguCfg.GetActiveDictionariesByFormat(format) );
133             aDics.insert( aDics.end(), aTmpDic.begin(), aTmpDic.end() );
134         }
135 
136         //!! for compatibility with old dictionaries (the ones not using extensions
137         //!! or new configuration entries, but still using the dictionary.lst file)
138         //!! Get the list of old style spell checking dictionaries to use...
139         std::vector< SvtLinguConfigDictionaryEntry > aOldStyleDics(
140                 GetOldStyleDics( "DICT" ) );
141 
142         // to prefer dictionaries with configuration entries we will only
143         // use those old style dictionaries that add a language that
144         // is not yet supported by the list of new style dictionaries
145         MergeNewStyleDicsAndOldStyleDics( aDics, aOldStyleDics );
146 
147         if (!aDics.empty())
148         {
149             uno::Reference< lang::XMultiServiceFactory > xServiceFactory(comphelper::getProcessServiceFactory());
150             uno::Reference< ucb::XSimpleFileAccess > xAccess(xServiceFactory->createInstance("com.sun.star.ucb.SimpleFileAccess"), uno::UNO_QUERY);
151             // get supported locales from the dictionaries-to-use...
152             std::set<OUString> aLocaleNamesSet;
153             for (auto const& dict : aDics)
154             {
155                 const uno::Sequence< OUString > aLocaleNames( dict.aLocaleNames );
156                 uno::Sequence< OUString > aLocations( dict.aLocations );
157                 SAL_WARN_IF(
158                     aLocaleNames.hasElements() && !aLocations.hasElements(),
159                     "lingucomponent", "no locations");
160                 if (aLocations.hasElements())
161                 {
162                     if (xAccess.is() && xAccess->exists(aLocations[0]))
163                     {
164                         for (auto const& locale : aLocaleNames)
165                         {
166                             if (!comphelper::LibreOfficeKit::isAllowlistedLanguage(locale))
167                                 continue;
168 
169                             aLocaleNamesSet.insert(locale);
170                         }
171                     }
172                     else
173                     {
174                         SAL_WARN(
175                             "lingucomponent",
176                             "missing <" << aLocations[0] << ">");
177                     }
178                 }
179             }
180             // ... and add them to the resulting sequence
181             m_aSuppLocales.realloc( aLocaleNamesSet.size() );
182             sal_Int32 k = 0;
183             for (auto const& localeName : aLocaleNamesSet)
184             {
185                 Locale aTmp( LanguageTag::convertToLocale(localeName));
186                 m_aSuppLocales[k++] = aTmp;
187             }
188 
189             //! For each dictionary and each locale we need a separate entry.
190             //! If this results in more than one dictionary per locale than (for now)
191             //! it is undefined which dictionary gets used.
192             //! In the future the implementation should support using several dictionaries
193             //! for one locale.
194             sal_uInt32 nDictSize = std::accumulate(aDics.begin(), aDics.end(), sal_uInt32(0),
195                 [](const sal_uInt32 nSum, const SvtLinguConfigDictionaryEntry& dict) {
196                     return nSum + dict.aLocaleNames.getLength(); });
197 
198             // add dictionary information
199             m_DictItems.reserve(nDictSize);
200             for (auto const& dict : aDics)
201             {
202                 if (dict.aLocaleNames.hasElements() &&
203                     dict.aLocations.hasElements())
204                 {
205                     const uno::Sequence< OUString > aLocaleNames( dict.aLocaleNames );
206 
207                     // currently only one language per dictionary is supported in the actual implementation...
208                     // Thus here we work-around this by adding the same dictionary several times.
209                     // Once for each of its supported locales.
210                     for (auto const& localeName : aLocaleNames)
211                     {
212                         // also both files have to be in the same directory and the
213                         // file names must only differ in the extension (.aff/.dic).
214                         // Thus we use the first location only and strip the extension part.
215                         OUString aLocation = dict.aLocations[0];
216                         sal_Int32 nPos = aLocation.lastIndexOf( '.' );
217                         aLocation = aLocation.copy( 0, nPos );
218 
219                         m_DictItems.emplace_back(aLocation, LanguageTag::convertToLocale(localeName), RTL_TEXTENCODING_DONTKNOW);
220                     }
221                 }
222             }
223             DBG_ASSERT( nDictSize == m_DictItems.size(), "index mismatch?" );
224         }
225         else
226         {
227             // no dictionary found so register no dictionaries
228             m_aSuppLocales.realloc(0);
229         }
230     }
231 
232     return m_aSuppLocales;
233 }
234 
hasLocale(const Locale & rLocale)235 sal_Bool SAL_CALL SpellChecker::hasLocale(const Locale& rLocale)
236 {
237     MutexGuard  aGuard( GetLinguMutex() );
238 
239     bool bRes = false;
240     if (!m_aSuppLocales.hasElements())
241         getLocales();
242 
243     for (auto const& suppLocale : std::as_const(m_aSuppLocales))
244     {
245         if (rLocale == suppLocale)
246         {
247             bRes = true;
248             break;
249         }
250     }
251     return bRes;
252 }
253 
GetSpellFailure(const OUString & rWord,const Locale & rLocale)254 sal_Int16 SpellChecker::GetSpellFailure(const OUString &rWord, const Locale &rLocale)
255 {
256     if (rWord.getLength() > MAXWORDLEN)
257         return -1;
258 
259     Hunspell * pMS = nullptr;
260     rtl_TextEncoding eEnc = RTL_TEXTENCODING_DONTKNOW;
261 
262     // initialize a myspell object for each dictionary once
263     // (note: mutex is held higher up in isValid)
264 
265     sal_Int16 nRes = -1;
266 
267     // first handle smart quotes both single and double
268     OUStringBuffer rBuf(rWord);
269     sal_Int32 n = rBuf.getLength();
270     sal_Unicode c;
271     sal_Int32 extrachar = 0;
272 
273     for (sal_Int32 ix=0; ix < n; ix++)
274     {
275         c = rBuf[ix];
276         if ((c == 0x201C) || (c == 0x201D))
277             rBuf[ix] = u'"';
278         else if ((c == 0x2018) || (c == 0x2019))
279             rBuf[ix] = u'\'';
280 
281         // recognize words with Unicode ligatures and ZWNJ/ZWJ characters (only
282         // with 8-bit encoded dictionaries. For UTF-8 encoded dictionaries
283         // set ICONV and IGNORE aff file options, if needed.)
284         else if ((c == 0x200C) || (c == 0x200D) ||
285             ((c >= 0xFB00) && (c <= 0xFB04)))
286                 extrachar = 1;
287     }
288     OUString nWord(rBuf.makeStringAndClear());
289 
290     if (n)
291     {
292         for (auto& currDict : m_DictItems)
293         {
294             pMS = nullptr;
295             eEnc = RTL_TEXTENCODING_DONTKNOW;
296 
297             if (rLocale == currDict.m_aDLoc)
298             {
299                 if (!currDict.m_pDict)
300                 {
301                     OUString dicpath = currDict.m_aDName + ".dic";
302                     OUString affpath = currDict.m_aDName + ".aff";
303                     OUString dict;
304                     OUString aff;
305                     osl::FileBase::getSystemPathFromFileURL(dicpath,dict);
306                     osl::FileBase::getSystemPathFromFileURL(affpath,aff);
307 #if defined(_WIN32)
308                     // workaround for Windows specific problem that the
309                     // path length in calls to 'fopen' is limited to somewhat
310                     // about 120+ characters which will usually be exceed when
311                     // using dictionaries as extensions. (Hunspell waits UTF-8 encoded
312                     // path with \\?\ long path prefix.)
313                     OString aTmpaff = Win_AddLongPathPrefix(OUStringToOString(aff, RTL_TEXTENCODING_UTF8));
314                     OString aTmpdict = Win_AddLongPathPrefix(OUStringToOString(dict, RTL_TEXTENCODING_UTF8));
315 #else
316                     OString aTmpaff(OU2ENC(aff,osl_getThreadTextEncoding()));
317                     OString aTmpdict(OU2ENC(dict,osl_getThreadTextEncoding()));
318 #endif
319 
320                     currDict.m_pDict = std::make_unique<Hunspell>(aTmpaff.getStr(),aTmpdict.getStr());
321 #if defined(H_DEPRECATED)
322                     currDict.m_aDEnc = getTextEncodingFromCharset(currDict.m_pDict->get_dict_encoding().c_str());
323 #else
324                     currDict.m_aDEnc = getTextEncodingFromCharset(currDict.m_pDict->get_dic_encoding());
325 #endif
326                 }
327                 pMS  = currDict.m_pDict.get();
328                 eEnc = currDict.m_aDEnc;
329             }
330 
331             if (pMS)
332             {
333                 // we don't want to work with a default text encoding since following incorrect
334                 // results may occur only for specific text and thus may be hard to notice.
335                 // Thus better always make a clean exit here if the text encoding is in question.
336                 // Hopefully something not working at all will raise proper attention quickly. ;-)
337                 DBG_ASSERT( eEnc != RTL_TEXTENCODING_DONTKNOW, "failed to get text encoding! (maybe incorrect encoding string in file)" );
338                 if (eEnc == RTL_TEXTENCODING_DONTKNOW)
339                     return -1;
340 
341                 OString aWrd(OU2ENC(nWord,eEnc));
342 #if defined(H_DEPRECATED)
343                 bool bVal = pMS->spell(std::string(aWrd.getStr()));
344 #else
345                 bool bVal = pMS->spell(aWrd.getStr()) != 0;
346 #endif
347                 if (!bVal) {
348                     if (extrachar && (eEnc != RTL_TEXTENCODING_UTF8)) {
349                         OUStringBuffer aBuf(nWord);
350                         n = aBuf.getLength();
351                         for (sal_Int32 ix=n-1; ix >= 0; ix--)
352                         {
353                           switch (aBuf[ix]) {
354                             case 0xFB00: aBuf.remove(ix, 1); aBuf.insert(ix, "ff"); break;
355                             case 0xFB01: aBuf.remove(ix, 1); aBuf.insert(ix, "fi"); break;
356                             case 0xFB02: aBuf.remove(ix, 1); aBuf.insert(ix, "fl"); break;
357                             case 0xFB03: aBuf.remove(ix, 1); aBuf.insert(ix, "ffi"); break;
358                             case 0xFB04: aBuf.remove(ix, 1); aBuf.insert(ix, "ffl"); break;
359                             case 0x200C:
360                             case 0x200D: aBuf.remove(ix, 1); break;
361                           }
362                         }
363                         OUString aWord(aBuf.makeStringAndClear());
364                         OString bWrd(OU2ENC(aWord, eEnc));
365 #if defined(H_DEPRECATED)
366                         bVal = pMS->spell(std::string(bWrd.getStr()));
367 #else
368                         bVal = pMS->spell(bWrd.getStr()) != 0;
369 #endif
370                         if (bVal) return -1;
371                     }
372                     nRes = SpellFailure::SPELLING_ERROR;
373                 } else {
374                     return -1;
375                 }
376                 pMS = nullptr;
377             }
378         }
379     }
380 
381     return nRes;
382 }
383 
isValid(const OUString & rWord,const Locale & rLocale,const css::uno::Sequence<css::beans::PropertyValue> & rProperties)384 sal_Bool SAL_CALL SpellChecker::isValid( const OUString& rWord, const Locale& rLocale,
385             const css::uno::Sequence< css::beans::PropertyValue >& rProperties )
386 {
387     MutexGuard  aGuard( GetLinguMutex() );
388 
389     if (rLocale == Locale()  ||  rWord.isEmpty())
390         return true;
391 
392     if (!hasLocale( rLocale ))
393         return true;
394 
395     // return sal_False to process SPELLML requests (they are longer than the header)
396     if (rWord.match(SPELL_XML, 0) && (rWord.getLength() > 10)) return false;
397 
398     // Get property values to be used.
399     // These are be the default values set in the SN_LINGU_PROPERTIES
400     // PropertySet which are overridden by the supplied ones from the
401     // last argument.
402     // You'll probably like to use a simpler solution than the provided
403     // one using the PropertyHelper_Spell.
404     PropertyHelper_Spelling& rHelper = GetPropHelper();
405     rHelper.SetTmpPropVals( rProperties );
406 
407     sal_Int16 nFailure = GetSpellFailure( rWord, rLocale );
408     if (nFailure != -1 && !rWord.match(SPELL_XML, 0))
409     {
410         LanguageType nLang = LinguLocaleToLanguage( rLocale );
411         // postprocess result for errors that should be ignored
412         const bool bIgnoreError =
413                 (!rHelper.IsSpellUpperCase()  && IsUpper( rWord, nLang )) ||
414                 (!rHelper.IsSpellWithDigits() && HasDigits( rWord )) ||
415                 (!rHelper.IsSpellCapitalization()  &&  nFailure == SpellFailure::CAPTION_ERROR);
416         if (bIgnoreError)
417             nFailure = -1;
418     }
419 
420     return (nFailure == -1);
421 }
422 
423 Reference< XSpellAlternatives >
GetProposals(const OUString & rWord,const Locale & rLocale)424     SpellChecker::GetProposals( const OUString &rWord, const Locale &rLocale )
425 {
426     // Retrieves the return values for the 'spell' function call in case
427     // of a misspelled word.
428     // Especially it may give a list of suggested (correct) words:
429     Reference< XSpellAlternatives > xRes;
430     // note: mutex is held by higher up by spell which covers both
431 
432     Hunspell* pMS = nullptr;
433     rtl_TextEncoding eEnc = RTL_TEXTENCODING_DONTKNOW;
434 
435     // first handle smart quotes (single and double)
436     OUStringBuffer rBuf(rWord);
437     sal_Int32 n = rBuf.getLength();
438     sal_Unicode c;
439     for (sal_Int32 ix=0; ix < n; ix++)
440     {
441         c = rBuf[ix];
442         if ((c == 0x201C) || (c == 0x201D))
443             rBuf[ix] = u'"';
444         if ((c == 0x2018) || (c == 0x2019))
445             rBuf[ix] = u'\'';
446     }
447     OUString nWord(rBuf.makeStringAndClear());
448 
449     if (n)
450     {
451         LanguageType nLang = LinguLocaleToLanguage( rLocale );
452         int numsug = 0;
453 
454         Sequence< OUString > aStr( 0 );
455         for (const auto& currDict : m_DictItems)
456         {
457             pMS = nullptr;
458             eEnc = RTL_TEXTENCODING_DONTKNOW;
459 
460             if (rLocale == currDict.m_aDLoc)
461             {
462                 pMS  = currDict.m_pDict.get();
463                 eEnc = currDict.m_aDEnc;
464             }
465 
466             if (pMS)
467             {
468                 OString aWrd(OU2ENC(nWord,eEnc));
469 #if defined(H_DEPRECATED)
470                 std::vector<std::string> suglst = pMS->suggest(std::string(aWrd.getStr()));
471                 if (!suglst.empty())
472                 {
473                     aStr.realloc(numsug + suglst.size());
474                     OUString *pStr = aStr.getArray();
475                     for (size_t ii = 0; ii < suglst.size(); ++ii)
476                     {
477                         OUString cvtwrd(suglst[ii].c_str(), suglst[ii].size(), eEnc);
478                         pStr[numsug + ii] = cvtwrd;
479                     }
480                     numsug += suglst.size();
481                 }
482 #else
483                 char ** suglst = nullptr;
484                 int count = pMS->suggest(&suglst, aWrd.getStr());
485                 if (count)
486                 {
487                     aStr.realloc( numsug + count );
488                     OUString *pStr = aStr.getArray();
489                     for (int ii=0; ii < count; ++ii)
490                     {
491                         OUString cvtwrd(suglst[ii],strlen(suglst[ii]),eEnc);
492                         pStr[numsug + ii] = cvtwrd;
493                     }
494                     numsug += count;
495                 }
496                 pMS->free_list(&suglst, count);
497 #endif
498             }
499         }
500 
501         // now return an empty alternative for no suggestions or the list of alternatives if some found
502         xRes = SpellAlternatives::CreateSpellAlternatives( rWord, nLang, SpellFailure::SPELLING_ERROR, aStr );
503         return xRes;
504     }
505     return xRes;
506 }
507 
spell(const OUString & rWord,const Locale & rLocale,const css::uno::Sequence<css::beans::PropertyValue> & rProperties)508 Reference< XSpellAlternatives > SAL_CALL SpellChecker::spell(
509         const OUString& rWord, const Locale& rLocale,
510         const css::uno::Sequence< css::beans::PropertyValue >& rProperties )
511 {
512     MutexGuard  aGuard( GetLinguMutex() );
513 
514     if (rLocale == Locale()  ||  rWord.isEmpty())
515         return nullptr;
516 
517     if (!hasLocale( rLocale ))
518         return nullptr;
519 
520     Reference< XSpellAlternatives > xAlt;
521     if (!isValid( rWord, rLocale, rProperties ))
522     {
523         xAlt =  GetProposals( rWord, rLocale );
524     }
525     return xAlt;
526 }
527 
addLinguServiceEventListener(const Reference<XLinguServiceEventListener> & rxLstnr)528 sal_Bool SAL_CALL SpellChecker::addLinguServiceEventListener(
529         const Reference< XLinguServiceEventListener >& rxLstnr )
530 {
531     MutexGuard  aGuard( GetLinguMutex() );
532 
533     bool bRes = false;
534     if (!m_bDisposing && rxLstnr.is())
535     {
536         bRes = GetPropHelper().addLinguServiceEventListener( rxLstnr );
537     }
538     return bRes;
539 }
540 
removeLinguServiceEventListener(const Reference<XLinguServiceEventListener> & rxLstnr)541 sal_Bool SAL_CALL SpellChecker::removeLinguServiceEventListener(
542         const Reference< XLinguServiceEventListener >& rxLstnr )
543 {
544     MutexGuard  aGuard( GetLinguMutex() );
545 
546     bool bRes = false;
547     if (!m_bDisposing && rxLstnr.is())
548     {
549         bRes = GetPropHelper().removeLinguServiceEventListener( rxLstnr );
550     }
551     return bRes;
552 }
553 
getServiceDisplayName(const Locale & rLocale)554 OUString SAL_CALL SpellChecker::getServiceDisplayName(const Locale& rLocale)
555 {
556     std::locale loc(Translate::Create("svt", LanguageTag(rLocale)));
557     return Translate::get(STR_DESCRIPTION_HUNSPELL, loc);
558 }
559 
initialize(const Sequence<Any> & rArguments)560 void SAL_CALL SpellChecker::initialize( const Sequence< Any >& rArguments )
561 {
562     MutexGuard  aGuard( GetLinguMutex() );
563 
564     if (m_pPropHelper)
565         return;
566 
567     sal_Int32 nLen = rArguments.getLength();
568     if (2 == nLen)
569     {
570         Reference< XLinguProperties >   xPropSet;
571         rArguments.getConstArray()[0] >>= xPropSet;
572         // rArguments.getConstArray()[1] >>= xDicList;
573 
574         //! Pointer allows for access of the non-UNO functions.
575         //! And the reference to the UNO-functions while increasing
576         //! the ref-count and will implicitly free the memory
577         //! when the object is no longer used.
578         m_pPropHelper.reset( new PropertyHelper_Spelling( static_cast<XSpellChecker *>(this), xPropSet ) );
579         m_pPropHelper->AddAsPropListener();   //! after a reference is established
580     }
581     else {
582         OSL_FAIL( "wrong number of arguments in sequence" );
583     }
584 }
585 
dispose()586 void SAL_CALL SpellChecker::dispose()
587 {
588     MutexGuard  aGuard( GetLinguMutex() );
589 
590     if (!m_bDisposing)
591     {
592         m_bDisposing = true;
593         EventObject aEvtObj( static_cast<XSpellChecker *>(this) );
594         m_aEvtListeners.disposeAndClear( aEvtObj );
595         if (m_pPropHelper)
596         {
597             m_pPropHelper->RemoveAsPropListener();
598             m_pPropHelper.reset();
599         }
600     }
601 }
602 
addEventListener(const Reference<XEventListener> & rxListener)603 void SAL_CALL SpellChecker::addEventListener( const Reference< XEventListener >& rxListener )
604 {
605     MutexGuard  aGuard( GetLinguMutex() );
606 
607     if (!m_bDisposing && rxListener.is())
608         m_aEvtListeners.addInterface( rxListener );
609 }
610 
removeEventListener(const Reference<XEventListener> & rxListener)611 void SAL_CALL SpellChecker::removeEventListener( const Reference< XEventListener >& rxListener )
612 {
613     MutexGuard  aGuard( GetLinguMutex() );
614 
615     if (!m_bDisposing && rxListener.is())
616         m_aEvtListeners.removeInterface( rxListener );
617 }
618 
619 // Service specific part
getImplementationName()620 OUString SAL_CALL SpellChecker::getImplementationName()
621 {
622     return "org.openoffice.lingu.MySpellSpellChecker";
623 }
624 
supportsService(const OUString & ServiceName)625 sal_Bool SAL_CALL SpellChecker::supportsService( const OUString& ServiceName )
626 {
627     return cppu::supportsService(this, ServiceName);
628 }
629 
getSupportedServiceNames()630 Sequence< OUString > SAL_CALL SpellChecker::getSupportedServiceNames()
631 {
632     return { SN_SPELLCHECKER };
633 }
634 
635 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
lingucomponent_SpellChecker_get_implementation(css::uno::XComponentContext *,css::uno::Sequence<css::uno::Any> const &)636 lingucomponent_SpellChecker_get_implementation(
637     css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&)
638 {
639     return cppu::acquire(new SpellChecker());
640 }
641 
642 
643 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
644