Coverage Report

Created: 2026-04-09 11:41

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