Coverage Report

Created: 2026-06-30 11:14

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