LibreOffice Module linguistic (master) 1
spelldsp.cxx
Go to the documentation of this file.
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 <sal/config.h>
21
22#include <com/sun/star/uno/Reference.h>
23#include <com/sun/star/linguistic2/XLinguServiceEventBroadcaster.hpp>
24#include <com/sun/star/linguistic2/SpellFailure.hpp>
25#include <com/sun/star/uno/XComponentContext.hpp>
26
30#include <tools/debug.hxx>
31#include <svl/lngmisc.hxx>
32#include <osl/mutex.hxx>
33#include <sal/log.hxx>
34
35#include <vector>
36
37#include "spelldsp.hxx"
39#include "lngsvcmgr.hxx"
40
41using namespace osl;
42using namespace com::sun::star;
43using namespace com::sun::star::beans;
44using namespace com::sun::star::lang;
45using namespace com::sun::star::uno;
46using namespace com::sun::star::linguistic2;
47using namespace linguistic;
48
49namespace {
50
51// ProposalList: list of proposals for misspelled words
52// The order of strings in the array should be left unchanged because the
53// spellchecker should have put the more likely suggestions at the top.
54// New entries will be added to the end but duplicates are to be avoided.
55// Removing entries is done by assigning the empty string.
56// The sequence is constructed from all non empty strings in the original
57// while maintaining the order.
58class ProposalList
59{
60 std::vector< OUString > aVec;
61
62 bool HasEntry( std::u16string_view rText ) const;
63
64public:
65 ProposalList() {}
66 ProposalList(const ProposalList&) = delete;
67 ProposalList& operator=(const ProposalList&) = delete;
68
69 size_t Count() const;
70 void Prepend( const OUString &rText );
71 void Append( const OUString &rNew );
72 void Append( const std::vector< OUString > &rNew );
73 void Append( const Sequence< OUString > &rNew );
74 std::vector< OUString > GetVector() const;
75};
76
77}
78
79bool ProposalList::HasEntry( std::u16string_view rText ) const
80{
81 bool bFound = false;
82 size_t nCnt = aVec.size();
83 for (size_t i = 0; !bFound && i < nCnt; ++i)
84 {
85 if (aVec[i] == rText)
86 bFound = true;
87 }
88 return bFound;
89}
90
91void ProposalList::Prepend( const OUString &rText )
92{
93 if (!HasEntry( rText ))
94 aVec.insert( aVec.begin(), rText );
95}
96
97void ProposalList::Append( const OUString &rText )
98{
99 if (!HasEntry( rText ))
100 aVec.push_back( rText );
101}
102
103void ProposalList::Append( const std::vector< OUString > &rNew )
104{
105 size_t nLen = rNew.size();
106 for ( size_t i = 0; i < nLen; ++i)
107 {
108 const OUString &rText = rNew[i];
109 if (!HasEntry( rText ))
110 Append( rText );
111 }
112}
113
114void ProposalList::Append( const Sequence< OUString > &rNew )
115{
116 for (const OUString& rText : rNew)
117 {
118 if (!HasEntry( rText ))
119 Append( rText );
120 }
121}
122
123size_t ProposalList::Count() const
124{
125 // returns the number of non-empty strings in the vector
126
127 size_t nRes = 0;
128 size_t nLen = aVec.size();
129 for (size_t i = 0; i < nLen; ++i)
130 {
131 if (!aVec[i].isEmpty())
132 ++nRes;
133 }
134 return nRes;
135}
136
137std::vector< OUString > ProposalList::GetVector() const
138{
139 sal_Int32 nCount = Count();
140 sal_Int32 nIdx = 0;
141 std::vector< OUString > aRes( nCount );
142 sal_Int32 nLen = aVec.size();
143 for (sal_Int32 i = 0; i < nLen; ++i)
144 {
145 const OUString &rText = aVec[i];
146 DBG_ASSERT( nIdx < nCount, "index out of range" );
147 if (nIdx < nCount && !rText.isEmpty())
148 aRes[ nIdx++ ] = rText;
149 }
150 return aRes;
151}
152
154 const LangSvcEntries_Spell &rEntry,
155 LanguageType nLanguage )
156{
157 Locale aTmpLocale = LanguageTag::convertToLocale( nLanguage );
158
159 return std::any_of(rEntry.aSvcRefs.begin(), rEntry.aSvcRefs.end(),
160 [&aTmpLocale](const Reference<XSpellChecker>& rRef) {
161 return rRef.is() && rRef->hasLocale( aTmpLocale ); });
162}
163
165 m_rMgr (rLngSvcMgr)
166{
167}
168
169
171{
172}
173
174
176{
177 MutexGuard aGuard( GetLinguMutex() );
178
179 std::vector<Locale> aLocales;
180 aLocales.reserve(m_aSvcMap.size());
181
182 std::transform(m_aSvcMap.begin(), m_aSvcMap.end(), std::back_inserter(aLocales),
183 [](SpellSvcByLangMap_t::const_reference elem) { return LanguageTag::convertToLocale(elem.first); });
184
185 return comphelper::containerToSequence(aLocales);
186}
187
188
189sal_Bool SAL_CALL SpellCheckerDispatcher::hasLocale( const Locale& rLocale )
190{
191 MutexGuard aGuard( GetLinguMutex() );
192 SpellSvcByLangMap_t::const_iterator aIt( m_aSvcMap.find( LinguLocaleToLanguage( rLocale ) ) );
193 return aIt != m_aSvcMap.end();
194}
195
196
197sal_Bool SAL_CALL
198 SpellCheckerDispatcher::isValid( const OUString& rWord, const Locale& rLocale,
199 const css::uno::Sequence< ::css::beans::PropertyValue >& rProperties )
200{
201 MutexGuard aGuard( GetLinguMutex() );
202 return isValid_Impl( rWord, LinguLocaleToLanguage( rLocale ), rProperties );
203}
204
205
207 SpellCheckerDispatcher::spell( const OUString& rWord, const Locale& rLocale,
208 const css::uno::Sequence< ::css::beans::PropertyValue >& rProperties )
209{
210 MutexGuard aGuard( GetLinguMutex() );
211 return spell_Impl( rWord, LinguLocaleToLanguage( rLocale ), rProperties );
212}
213
214
215// returns the overall result of cross-checking with all user-dictionaries
216// including the IgnoreAll list
218 const OUString &rWord,
219 LanguageType nLanguage )
220{
222
223 // the order of winning from top to bottom is:
224 // 1) IgnoreAll list will always win
225 // 2) Negative dictionaries will win over positive dictionaries
227 if (xIgnoreAll.is())
228 xRes = xIgnoreAll->getEntry( rWord );
229 if (!xRes.is())
230 {
233 rWord, nLanguage, false, true ) );
234 if (xNegEntry.is())
235 xRes = xNegEntry;
236 else
237 {
239 rWord, nLanguage, true, true ) );
240 if (xPosEntry.is())
241 xRes = xPosEntry;
242 }
243 }
244
245 return xRes;
246}
247
248
250 const OUString& rWord,
251 LanguageType nLanguage,
252 const PropertyValues& rProperties)
253{
254 MutexGuard aGuard( GetLinguMutex() );
255
256 bool bRes = true;
257
258 if (LinguIsUnspecified( nLanguage) || rWord.isEmpty())
259 return bRes;
260
261 // search for entry with that language
262 SpellSvcByLangMap_t::iterator aIt( m_aSvcMap.find( nLanguage ) );
263 LangSvcEntries_Spell *pEntry = aIt != m_aSvcMap.end() ? aIt->second.get() : nullptr;
264
265 if (pEntry)
266 {
267 OUString aChkWord( rWord );
268 Locale aLocale( LanguageTag::convertToLocale( nLanguage ) );
269
270 // replace typographical apostroph by ascii apostroph
271 OUString aSingleQuote( GetLocaleDataWrapper( nLanguage ).getQuotationMarkEnd() );
272 DBG_ASSERT( 1 == aSingleQuote.getLength(), "unexpected length of quotation mark" );
273 if (!aSingleQuote.isEmpty())
274 aChkWord = aChkWord.replace( aSingleQuote[0], '\'' );
275
276 RemoveHyphens( aChkWord );
277 if (IsIgnoreControlChars( rProperties, GetPropSet() ))
278 RemoveControlChars( aChkWord );
279
280 sal_Int32 nLen = pEntry->aSvcRefs.getLength();
281 DBG_ASSERT( nLen == pEntry->aSvcImplNames.getLength(),
282 "lng : sequence length mismatch");
283 DBG_ASSERT( pEntry->nLastTriedSvcIndex < nLen,
284 "lng : index out of range");
285
286 sal_Int32 i = 0;
287 bool bTmpRes = true;
288 bool bTmpResValid = false;
289
290 // try already instantiated services first
291 {
292 const Reference< XSpellChecker > *pRef =
293 pEntry->aSvcRefs.getConstArray();
294 while (i <= pEntry->nLastTriedSvcIndex
295 && (!bTmpResValid || !bTmpRes))
296 {
297 bTmpResValid = true;
298 if (pRef[i].is() && pRef[i]->hasLocale( aLocale ))
299 {
300 bTmpRes = GetCache().CheckWord( aChkWord, nLanguage );
301 if (!bTmpRes)
302 {
303 bTmpRes = pRef[i]->isValid( aChkWord, aLocale, rProperties );
304
305 // Add correct words to the cache.
306 // But not those that are correct only because of
307 // the temporary supplied settings.
308 if (bTmpRes && !rProperties.hasElements())
309 GetCache().AddWord( aChkWord, nLanguage );
310 }
311 }
312 else
313 bTmpResValid = false;
314
315 if (bTmpResValid)
316 bRes = bTmpRes;
317
318 ++i;
319 }
320 }
321
322 // if still no result instantiate new services and try those
323 if ((!bTmpResValid || !bTmpRes)
324 && pEntry->nLastTriedSvcIndex < nLen - 1)
325 {
326 const OUString *pImplNames = pEntry->aSvcImplNames.getConstArray();
327 Reference< XSpellChecker > *pRef = pEntry->aSvcRefs .getArray();
328
331
332 // build service initialization argument
333 Sequence< Any > aArgs(2);
334 aArgs.getArray()[0] <<= GetPropSet();
335
336 while (i < nLen && (!bTmpResValid || !bTmpRes))
337 {
338 // create specific service via it's implementation name
340 try
341 {
342 xSpell.set( xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
343 pImplNames[i], aArgs, xContext ),
344 UNO_QUERY );
345 }
346 catch (uno::Exception &)
347 {
348 SAL_WARN( "linguistic", "createInstanceWithArguments failed" );
349 }
350 pRef [i] = xSpell;
351
353 xBroadcaster( xSpell, UNO_QUERY );
354 if (xBroadcaster.is())
355 m_rMgr.AddLngSvcEvtBroadcaster( xBroadcaster );
356
357 bTmpResValid = true;
358 if (xSpell.is() && xSpell->hasLocale( aLocale ))
359 {
360 bTmpRes = GetCache().CheckWord( aChkWord, nLanguage );
361 if (!bTmpRes)
362 {
363 bTmpRes = xSpell->isValid( aChkWord, aLocale, rProperties );
364 // Add correct words to the cache.
365 // But not those that are correct only because of
366 // the temporary supplied settings.
367 if (bTmpRes && !rProperties.hasElements())
368 GetCache().AddWord( aChkWord, nLanguage );
369 }
370 }
371 else
372 bTmpResValid = false;
373 if (bTmpResValid)
374 bRes = bTmpRes;
375
376 pEntry->nLastTriedSvcIndex = static_cast<sal_Int16>(i);
377 ++i;
378 }
379
380 // if language is not supported by any of the services
381 // remove it from the list.
382 if (i == nLen)
383 {
384 if (!SvcListHasLanguage( *pEntry, nLanguage ))
385 m_aSvcMap.erase( nLanguage );
386 }
387 }
388
389 // cross-check against results from dictionaries which have precedence!
390 if (GetDicList().is() && IsUseDicList( rProperties, GetPropSet() ))
391 {
392 Reference< XDictionaryEntry > xTmp( lcl_GetRulingDictionaryEntry( aChkWord, nLanguage ) );
393 if (xTmp.is()) {
394 bRes = !xTmp->isNegative();
395 } else {
396 setCharClass(LanguageTag(nLanguage));
397 CapType ct = capitalType(aChkWord, m_oCharClass ? &*m_oCharClass : nullptr);
398 if (ct == CapType::INITCAP || ct == CapType::ALLCAP) {
400 if (xTmp2.is()) {
401 bRes = !xTmp2->isNegative();
402 }
403 }
404 }
405 }
406 }
407
408 return bRes;
409}
410
411
413 const OUString& rWord,
414 LanguageType nLanguage,
415 const PropertyValues& rProperties )
416{
417 MutexGuard aGuard( GetLinguMutex() );
418
420
421 if (LinguIsUnspecified( nLanguage) || rWord.isEmpty())
422 return xRes;
423
424 // search for entry with that language
425 SpellSvcByLangMap_t::iterator aIt( m_aSvcMap.find( nLanguage ) );
426 LangSvcEntries_Spell *pEntry = aIt != m_aSvcMap.end() ? aIt->second.get() : nullptr;
427
428 if (pEntry)
429 {
430 OUString aChkWord( rWord );
431 Locale aLocale( LanguageTag::convertToLocale( nLanguage ) );
432
433 // replace typographical apostroph by ascii apostroph
434 OUString aSingleQuote( GetLocaleDataWrapper( nLanguage ).getQuotationMarkEnd() );
435 DBG_ASSERT( 1 == aSingleQuote.getLength(), "unexpected length of quotation mark" );
436 if (!aSingleQuote.isEmpty())
437 aChkWord = aChkWord.replace( aSingleQuote[0], '\'' );
438
439 RemoveHyphens( aChkWord );
440 if (IsIgnoreControlChars( rProperties, GetPropSet() ))
441 RemoveControlChars( aChkWord );
442
443 sal_Int32 nLen = pEntry->aSvcRefs.getLength();
444 DBG_ASSERT( nLen == pEntry->aSvcImplNames.getLength(),
445 "lng : sequence length mismatch");
446 DBG_ASSERT( pEntry->nLastTriedSvcIndex < nLen,
447 "lng : index out of range");
448
449 sal_Int32 i = 0;
451 bool bTmpResValid = false;
452
453 // try already instantiated services first
454 {
455 const Reference< XSpellChecker > *pRef = pEntry->aSvcRefs.getConstArray();
456 sal_Int32 nNumSuggestions = -1;
457 while (i <= pEntry->nLastTriedSvcIndex
458 && (!bTmpResValid || xTmpRes.is()) )
459 {
460 bTmpResValid = true;
461 if (pRef[i].is() && pRef[i]->hasLocale( aLocale ))
462 {
463 bool bOK = GetCache().CheckWord( aChkWord, nLanguage );
464 if (bOK)
465 xTmpRes = nullptr;
466 else
467 {
468 xTmpRes = pRef[i]->spell( aChkWord, aLocale, rProperties );
469
470 // Add correct words to the cache.
471 // But not those that are correct only because of
472 // the temporary supplied settings.
473 if (!xTmpRes.is() && !rProperties.hasElements())
474 GetCache().AddWord( aChkWord, nLanguage );
475 }
476 }
477 else
478 bTmpResValid = false;
479
480 // return first found result if the word is not known by any checker.
481 // But if that result has no suggestions use the first one that does
482 // provide suggestions for the misspelled word.
483 if (!xRes.is() && bTmpResValid)
484 {
485 xRes = xTmpRes;
486 nNumSuggestions = 0;
487 if (xRes.is())
488 nNumSuggestions = xRes->getAlternatives().getLength();
489 }
490 sal_Int32 nTmpNumSuggestions = 0;
491 if (xTmpRes.is() && bTmpResValid)
492 nTmpNumSuggestions = xTmpRes->getAlternatives().getLength();
493 if (xRes.is() && nNumSuggestions == 0 && nTmpNumSuggestions > 0)
494 {
495 xRes = xTmpRes;
496 nNumSuggestions = nTmpNumSuggestions;
497 }
498
499 ++i;
500 }
501 }
502
503 // if still no result instantiate new services and try those
504 if ((!bTmpResValid || xTmpRes.is())
505 && pEntry->nLastTriedSvcIndex < nLen - 1)
506 {
507 const OUString *pImplNames = pEntry->aSvcImplNames.getConstArray();
508 Reference< XSpellChecker > *pRef = pEntry->aSvcRefs .getArray();
509
512
513 // build service initialization argument
514 Sequence< Any > aArgs(2);
515 aArgs.getArray()[0] <<= GetPropSet();
516
517 sal_Int32 nNumSuggestions = -1;
518 while (i < nLen && (!bTmpResValid || xTmpRes.is()))
519 {
520 // create specific service via it's implementation name
522 try
523 {
524 xSpell.set( xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
525 pImplNames[i], aArgs, xContext ),
526 UNO_QUERY );
527 }
528 catch (uno::Exception &)
529 {
530 SAL_WARN( "linguistic", "createInstanceWithArguments failed" );
531 }
532 pRef [i] = xSpell;
533
535 xBroadcaster( xSpell, UNO_QUERY );
536 if (xBroadcaster.is())
537 m_rMgr.AddLngSvcEvtBroadcaster( xBroadcaster );
538
539 bTmpResValid = true;
540 if (xSpell.is() && xSpell->hasLocale( aLocale ))
541 {
542 bool bOK = GetCache().CheckWord( aChkWord, nLanguage );
543 if (bOK)
544 xTmpRes = nullptr;
545 else
546 {
547 xTmpRes = xSpell->spell( aChkWord, aLocale, rProperties );
548
549 // Add correct words to the cache.
550 // But not those that are correct only because of
551 // the temporary supplied settings.
552 if (!xTmpRes.is() && !rProperties.hasElements())
553 GetCache().AddWord( aChkWord, nLanguage );
554 }
555 }
556 else
557 bTmpResValid = false;
558
559 // return first found result if the word is not known by any checker.
560 // But if that result has no suggestions use the first one that does
561 // provide suggestions for the misspelled word.
562 if (!xRes.is() && bTmpResValid)
563 {
564 xRes = xTmpRes;
565 nNumSuggestions = 0;
566 if (xRes.is())
567 nNumSuggestions = xRes->getAlternatives().getLength();
568 }
569 sal_Int32 nTmpNumSuggestions = 0;
570 if (xTmpRes.is() && bTmpResValid)
571 nTmpNumSuggestions = xTmpRes->getAlternatives().getLength();
572 if (xRes.is() && nNumSuggestions == 0 && nTmpNumSuggestions > 0)
573 {
574 xRes = xTmpRes;
575 nNumSuggestions = nTmpNumSuggestions;
576 }
577
578 pEntry->nLastTriedSvcIndex = static_cast<sal_Int16>(i);
579 ++i;
580 }
581
582 // if language is not supported by any of the services
583 // remove it from the list.
584 if (i == nLen)
585 {
586 if (!SvcListHasLanguage( *pEntry, nLanguage ))
587 m_aSvcMap.erase( nLanguage );
588 }
589 }
590
591 // if word is finally found to be correct
592 // clear previously remembered alternatives
593 if (bTmpResValid && !xTmpRes.is())
594 xRes = nullptr;
595
596 // list of proposals found (to be checked against entries of
597 // negative dictionaries)
598 ProposalList aProposalList;
599 sal_Int16 eFailureType = -1; // no failure
600 if (xRes.is())
601 {
602 aProposalList.Append( xRes->getAlternatives() );
603 eFailureType = xRes->getFailureType();
604 }
606 if (GetDicList().is() && IsUseDicList( rProperties, GetPropSet() ))
607 xDList = GetDicList();
608
609 // cross-check against results from user-dictionaries which have precedence!
610 if (xDList.is())
611 {
612 Reference< XDictionaryEntry > xTmp( lcl_GetRulingDictionaryEntry( aChkWord, nLanguage ) );
613 if (xTmp.is())
614 {
615 if (xTmp->isNegative()) // negative entry found
616 {
617 eFailureType = SpellFailure::IS_NEGATIVE_WORD;
618
619 // replacement text to be added to suggestions, if not empty
620 OUString aAddRplcTxt( xTmp->getReplacementText() );
621
622 // replacement text must not be in negative dictionary itself
623 if (!aAddRplcTxt.isEmpty() &&
624 !SearchDicList( xDList, aAddRplcTxt, nLanguage, false, true ).is())
625 {
626 aProposalList.Prepend( aAddRplcTxt );
627 }
628 }
629 else // positive entry found
630 {
631 xRes = nullptr;
632 eFailureType = -1; // no failure
633 }
634 }
635 else
636 {
637 setCharClass(LanguageTag(nLanguage));
638 CapType ct = capitalType(aChkWord, m_oCharClass ? &*m_oCharClass : nullptr);
639 if (ct == CapType::INITCAP || ct == CapType::ALLCAP)
640 {
642 if (xTmp2.is())
643 {
644 if (xTmp2->isNegative()) // negative entry found
645 {
646 eFailureType = SpellFailure::IS_NEGATIVE_WORD;
647
648 // replacement text to be added to suggestions, if not empty
649 OUString aAddRplcTxt( xTmp2->getReplacementText() );
650
651 // replacement text must not be in negative dictionary itself
652 if (!aAddRplcTxt.isEmpty() &&
653 !SearchDicList( xDList, aAddRplcTxt, nLanguage, false, true ).is())
654 {
655 switch ( ct )
656 {
657 case CapType::INITCAP:
658 aProposalList.Prepend( m_oCharClass->titlecase(aAddRplcTxt) );
659 break;
660 case CapType::ALLCAP:
661 aProposalList.Prepend( m_oCharClass->uppercase(aAddRplcTxt) );
662 break;
663 default:
664 /* can't happen because of if ct == above */
665 break;
666 }
667 }
668 }
669 else // positive entry found
670 {
671 xRes = nullptr;
672 eFailureType = -1; // no failure
673 }
674 }
675 }
676 }
677 }
678
679 if (eFailureType != -1) // word misspelled or found in negative user-dictionary
680 {
681 // search suitable user-dictionaries for suggestions that are
682 // similar to the misspelled word
683 std::vector< OUString > aDicListProps; // list of proposals from user-dictionaries
684 SearchSimilarText( aChkWord, nLanguage, xDList, aDicListProps );
685 aProposalList.Append( aDicListProps );
686 std::vector< OUString > aProposals = aProposalList.GetVector();
687
688 // remove entries listed in negative dictionaries
689 // (we don't want to display suggestions that will be regarded as misspelled later on)
690 if (xDList.is())
691 SeqRemoveNegEntries( aProposals, xDList, nLanguage );
692
693 uno::Reference< linguistic2::XSetSpellAlternatives > xSetAlt( xRes, uno::UNO_QUERY );
694 if (xSetAlt.is())
695 {
696 xSetAlt->setAlternatives( comphelper::containerToSequence(aProposals) );
697 xSetAlt->setFailureType( eFailureType );
698 }
699 else
700 {
701 if (xRes.is())
702 {
703 SAL_WARN( "linguistic", "XSetSpellAlternatives not implemented!" );
704 }
705 else if (!aProposals.empty())
706 {
707 // no xRes but Proposals found from the user-dictionaries.
708 // Thus we need to create an xRes...
709 xRes = new linguistic::SpellAlternatives( rWord, nLanguage,
711 }
712 }
713 }
714 }
715
716 return xRes;
717}
718
719uno::Sequence< sal_Int16 > SAL_CALL SpellCheckerDispatcher::getLanguages( )
720{
721 MutexGuard aGuard( GetLinguMutex() );
722 uno::Sequence< Locale > aTmp( getLocales() );
723 uno::Sequence< sal_Int16 > aRes( LocaleSeqToLangSeq( aTmp ) );
724 return aRes;
725}
726
727
729 sal_Int16 nLanguage )
730{
731 MutexGuard aGuard( GetLinguMutex() );
732 return hasLocale( LanguageTag::convertToLocale(LanguageType(static_cast<sal_uInt16>(nLanguage))));
733}
734
735
737 const OUString& rWord,
738 sal_Int16 nLanguage,
739 const uno::Sequence< beans::PropertyValue >& rProperties )
740{
741 MutexGuard aGuard( GetLinguMutex() );
742 return isValid( rWord, LanguageTag::convertToLocale(LanguageType(static_cast<sal_uInt16>(nLanguage))), rProperties);
743}
744
745
746uno::Reference< linguistic2::XSpellAlternatives > SAL_CALL SpellCheckerDispatcher::spell(
747 const OUString& rWord,
748 sal_Int16 nLanguage,
749 const uno::Sequence< beans::PropertyValue >& rProperties )
750{
751 MutexGuard aGuard( GetLinguMutex() );
752 return spell(rWord, LanguageTag::convertToLocale(LanguageType(static_cast<sal_uInt16>(nLanguage))), rProperties);
753}
754
755
756void SpellCheckerDispatcher::SetServiceList( const Locale &rLocale,
757 const Sequence< OUString > &rSvcImplNames )
758{
759 MutexGuard aGuard( GetLinguMutex() );
760
761 if (m_pCache)
762 m_pCache->Flush(); // new services may spell differently...
763
764 LanguageType nLanguage = LinguLocaleToLanguage( rLocale );
765
766 sal_Int32 nLen = rSvcImplNames.getLength();
767 if (0 == nLen)
768 // remove entry
769 m_aSvcMap.erase( nLanguage );
770 else
771 {
772 // modify/add entry
773 LangSvcEntries_Spell *pEntry = m_aSvcMap[ nLanguage ].get();
774 if (pEntry)
775 {
776 pEntry->Clear();
777 pEntry->aSvcImplNames = rSvcImplNames;
779 }
780 else
781 {
782 auto pTmpEntry = std::make_shared<LangSvcEntries_Spell>( rSvcImplNames );
783 pTmpEntry->aSvcRefs = Sequence< Reference < XSpellChecker > >( nLen );
784 m_aSvcMap[ nLanguage ] = pTmpEntry;
785 }
786 }
787}
788
789
791 SpellCheckerDispatcher::GetServiceList( const Locale &rLocale ) const
792{
793 MutexGuard aGuard( GetLinguMutex() );
794
796
797 // search for entry with that language and use data from that
798 LanguageType nLanguage = LinguLocaleToLanguage( rLocale );
799 const SpellSvcByLangMap_t::const_iterator aIt( m_aSvcMap.find( nLanguage ) );
800 const LangSvcEntries_Spell *pEntry = aIt != m_aSvcMap.end() ? aIt->second.get() : nullptr;
801 if (pEntry)
802 aRes = pEntry->aSvcImplNames;
803
804 return aRes;
805}
806
807
809{
810 if (m_pCache)
811 m_pCache->Flush();
812}
813
815{
816 if (m_oCharClass && m_oCharClass->getLanguageTag() == rLanguageTag)
817 return;
818 m_oCharClass.emplace( rLanguageTag );
819}
820
821
822OUString SpellCheckerDispatcher::makeLowerCase(const OUString& aTerm, const std::optional<CharClass> & pCC)
823{
824 if (pCC)
825 return pCC->lowercase(aTerm);
826 return aTerm;
827}
828
829/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
static css::lang::Locale convertToLocale(LanguageType nLangID, bool bResolveSystem=true)
bool AddLngSvcEvtBroadcaster(const css::uno::Reference< css::linguistic2::XLinguServiceEventBroadcaster > &rxBroadcaster)
Definition: lngsvcmgr.cxx:1787
virtual css::uno::Reference< css::linguistic2::XSpellAlternatives > SAL_CALL spell(const OUString &aWord, const css::lang::Locale &aLocale, const css::uno::Sequence< ::css::beans::PropertyValue > &aProperties) override
virtual void SetServiceList(const css::lang::Locale &rLocale, const css::uno::Sequence< OUString > &rSvcImplNames) override
Definition: spelldsp.cxx:756
virtual sal_Bool SAL_CALL hasLocale(const css::lang::Locale &aLocale) override
Definition: spelldsp.cxx:189
linguistic::SpellCache & GetCache() const
Definition: spelldsp.hxx:113
void setCharClass(const LanguageTag &rLanguageTag)
Definition: spelldsp.cxx:814
const css::uno::Reference< css::linguistic2::XLinguProperties > & GetPropSet()
Definition: spelldsp.hxx:122
const css::uno::Reference< css::linguistic2::XSearchableDictionaryList > & GetDicList()
Definition: spelldsp.hxx:131
SpellSvcByLangMap_t m_aSvcMap
Definition: spelldsp.hxx:50
virtual css::uno::Sequence< css::lang::Locale > SAL_CALL getLocales() override
Definition: spelldsp.cxx:175
std::unique_ptr< linguistic::SpellCache > m_pCache
Definition: spelldsp.hxx:56
LngSvcMgr & m_rMgr
Definition: spelldsp.hxx:55
virtual css::uno::Sequence< ::sal_Int16 > SAL_CALL getLanguages() override
Definition: spelldsp.cxx:719
std::optional< CharClass > m_oCharClass
Definition: spelldsp.hxx:57
virtual sal_Bool SAL_CALL hasLanguage(::sal_Int16 nLanguage) override
Definition: spelldsp.cxx:728
virtual sal_Bool SAL_CALL isValid(const OUString &aWord, const css::lang::Locale &aLocale, const css::uno::Sequence< ::css::beans::PropertyValue > &aProperties) override
css::uno::Reference< css::linguistic2::XSpellAlternatives > spell_Impl(const OUString &aWord, LanguageType nLanguage, const css::beans::PropertyValues &aProperties)
Definition: spelldsp.cxx:412
virtual ~SpellCheckerDispatcher() override
Definition: spelldsp.cxx:170
static OUString makeLowerCase(const OUString &, const std::optional< CharClass > &)
Definition: spelldsp.cxx:822
bool isValid_Impl(const OUString &aWord, LanguageType nLanguage, const css::beans::PropertyValues &aProperties)
Definition: spelldsp.cxx:249
SpellCheckerDispatcher(const SpellCheckerDispatcher &)=delete
virtual css::uno::Sequence< OUString > GetServiceList(const css::lang::Locale &rLocale) const override
Definition: spelldsp.cxx:791
void AddWord(const OUString &rWord, LanguageType nLang)
Definition: iprcache.cxx:217
bool CheckWord(const OUString &rWord, LanguageType nLang)
Definition: iprcache.cxx:209
int nCount
#define DBG_ASSERT(sCon, aError)
#define SAL_WARN(area, stream)
css::uno::Sequence< DstElementType > containerToSequence(const SrcType &i_Container)
Reference< XComponentContext > getProcessComponentContext()
std::map< sal_Int32, std::shared_ptr< SetItemPropertyStorage > > PropertyValues
int i
void SearchSimilarText(const OUString &rText, LanguageType nLanguage, Reference< XSearchableDictionaryList > const &xDicList, std::vector< OUString > &rDicListProps)
Definition: spelldta.cxx:63
uno::Sequence< sal_Int16 > LocaleSeqToLangSeq(uno::Sequence< Locale > const &rLocaleSeq)
Definition: misc.cxx:375
bool LinguIsUnspecified(LanguageType nLanguage)
Checks if a LanguageType is one of the values that denote absence of language or undetermined languag...
Definition: misc.cxx:88
uno::Reference< XSearchableDictionaryList > GetDictionaryList()
Definition: misc.cxx:671
bool RemoveHyphens(OUString &rTxt)
bool RemoveControlChars(OUString &rTxt)
CapType capitalType(const OUString &aTerm, CharClass const *pCC)
Definition: misc.cxx:564
uno::Reference< XDictionaryEntry > SearchDicList(const uno::Reference< XSearchableDictionaryList > &xDicList, const OUString &rWord, LanguageType nLanguage, bool bSearchPosDics, bool bSearchSpellEntry)
Definition: misc.cxx:248
osl::Mutex & GetLinguMutex()
! multi-thread safe mutex for all platforms !!
Definition: misc.cxx:60
void SeqRemoveNegEntries(std::vector< OUString > &rSeq, Reference< XSearchableDictionaryList > const &rxDicList, LanguageType nLanguage)
Definition: spelldta.cxx:107
bool IsIgnoreControlChars(const PropertyValues &rProperties, const uno::Reference< XPropertySet > &rxProp)
Definition: misc.cxx:211
const LocaleDataWrapper & GetLocaleDataWrapper(LanguageType nLang)
Definition: misc.cxx:66
uno::Reference< XDictionary > GetIgnoreAllList()
Definition: misc.cxx:687
LanguageType LinguLocaleToLanguage(const css::lang::Locale &rLocale)
Convert Locale to LanguageType for legacy handling.
Definition: misc.cxx:74
bool IsUseDicList(const PropertyValues &rProperties, const uno::Reference< XPropertySet > &rxProp)
Definition: misc.cxx:189
CapType
static Reference< XDictionaryEntry > lcl_GetRulingDictionaryEntry(const OUString &rWord, LanguageType nLanguage)
Definition: spelldsp.cxx:217
static bool SvcListHasLanguage(const LangSvcEntries_Spell &rEntry, LanguageType nLanguage)
Definition: spelldsp.cxx:153
css::uno::Sequence< css::uno::Reference< css::linguistic2::XSpellChecker > > aSvcRefs
Definition: defs.hxx:59
css::uno::Sequence< OUString > aSvcImplNames
Definition: defs.hxx:34
void Clear()
Definition: defs.hxx:50
sal_Int16 nLastTriedSvcIndex
Definition: defs.hxx:36
unsigned char sal_Bool
Count