1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
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>
27 #include <unotools/localedatawrapper.hxx>
28 #include <comphelper/processfactory.hxx>
29 #include <comphelper/sequence.hxx>
30 #include <tools/debug.hxx>
31 #include <svl/lngmisc.hxx>
32 #include <osl/mutex.hxx>
33 #include <sal/log.hxx>
37 #include "spelldsp.hxx"
38 #include <linguistic/spelldta.hxx>
39 #include "lngsvcmgr.hxx"
42 using namespace com::sun::star
;
43 using namespace com::sun::star::beans
;
44 using namespace com::sun::star::lang
;
45 using namespace com::sun::star::uno
;
46 using namespace com::sun::star::linguistic2
;
47 using namespace linguistic
;
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.
60 std::vector
< OUString
> aVec
;
62 bool HasEntry( std::u16string_view rText
) const;
66 ProposalList(const ProposalList
&) = delete;
67 ProposalList
& operator=(const ProposalList
&) = delete;
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;
79 bool ProposalList::HasEntry( std::u16string_view rText
) const
82 size_t nCnt
= aVec
.size();
83 for (size_t i
= 0; !bFound
&& i
< nCnt
; ++i
)
91 void ProposalList::Prepend( const OUString
&rText
)
93 if (!HasEntry( rText
))
94 aVec
.insert( aVec
.begin(), rText
);
97 void ProposalList::Append( const OUString
&rText
)
99 if (!HasEntry( rText
))
100 aVec
.push_back( rText
);
103 void ProposalList::Append( const std::vector
< OUString
> &rNew
)
105 size_t nLen
= rNew
.size();
106 for ( size_t i
= 0; i
< nLen
; ++i
)
108 const OUString
&rText
= rNew
[i
];
109 if (!HasEntry( rText
))
114 void ProposalList::Append( const Sequence
< OUString
> &rNew
)
116 for (const OUString
& rText
: rNew
)
118 if (!HasEntry( rText
))
123 size_t ProposalList::Count() const
125 // returns the number of non-empty strings in the vector
128 size_t nLen
= aVec
.size();
129 for (size_t i
= 0; i
< nLen
; ++i
)
131 if (!aVec
[i
].isEmpty())
137 std::vector
< OUString
> ProposalList::GetVector() const
139 sal_Int32 nCount
= Count();
141 std::vector
< OUString
> aRes( nCount
);
142 sal_Int32 nLen
= aVec
.size();
143 for (sal_Int32 i
= 0; i
< nLen
; ++i
)
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
;
153 static bool SvcListHasLanguage(
154 const LangSvcEntries_Spell
&rEntry
,
155 LanguageType nLanguage
)
157 Locale aTmpLocale
= LanguageTag::convertToLocale( nLanguage
);
159 return std::any_of(rEntry
.aSvcRefs
.begin(), rEntry
.aSvcRefs
.end(),
160 [&aTmpLocale
](const Reference
<XSpellChecker
>& rRef
) {
161 return rRef
.is() && rRef
->hasLocale( aTmpLocale
); });
164 SpellCheckerDispatcher::SpellCheckerDispatcher( LngSvcMgr
&rLngSvcMgr
) :
170 SpellCheckerDispatcher::~SpellCheckerDispatcher()
175 Sequence
< Locale
> SAL_CALL
SpellCheckerDispatcher::getLocales()
177 MutexGuard
aGuard( GetLinguMutex() );
179 std::vector
<Locale
> aLocales
;
180 aLocales
.reserve(m_aSvcMap
.size());
182 std::transform(m_aSvcMap
.begin(), m_aSvcMap
.end(), std::back_inserter(aLocales
),
183 [](SpellSvcByLangMap_t::const_reference elem
) { return LanguageTag::convertToLocale(elem
.first
); });
185 return comphelper::containerToSequence(aLocales
);
189 sal_Bool SAL_CALL
SpellCheckerDispatcher::hasLocale( const Locale
& rLocale
)
191 MutexGuard
aGuard( GetLinguMutex() );
192 SpellSvcByLangMap_t::const_iterator
aIt( m_aSvcMap
.find( LinguLocaleToLanguage( rLocale
) ) );
193 return aIt
!= m_aSvcMap
.end();
198 SpellCheckerDispatcher::isValid( const OUString
& rWord
, const Locale
& rLocale
,
199 const css::uno::Sequence
< ::css::beans::PropertyValue
>& rProperties
)
201 MutexGuard
aGuard( GetLinguMutex() );
202 return isValid_Impl( rWord
, LinguLocaleToLanguage( rLocale
), rProperties
);
206 Reference
< XSpellAlternatives
> SAL_CALL
207 SpellCheckerDispatcher::spell( const OUString
& rWord
, const Locale
& rLocale
,
208 const css::uno::Sequence
< ::css::beans::PropertyValue
>& rProperties
)
210 MutexGuard
aGuard( GetLinguMutex() );
211 return spell_Impl( rWord
, LinguLocaleToLanguage( rLocale
), rProperties
);
215 // returns the overall result of cross-checking with all user-dictionaries
216 // including the IgnoreAll list
217 static Reference
< XDictionaryEntry
> lcl_GetRulingDictionaryEntry(
218 const OUString
&rWord
,
219 LanguageType nLanguage
)
221 Reference
< XDictionaryEntry
> xRes
;
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
226 Reference
< XDictionary
> xIgnoreAll( GetIgnoreAllList() );
228 xRes
= xIgnoreAll
->getEntry( rWord
);
231 Reference
< XSearchableDictionaryList
> xDList( GetDictionaryList() );
232 Reference
< XDictionaryEntry
> xNegEntry( SearchDicList( xDList
,
233 rWord
, nLanguage
, false, true ) );
238 Reference
< XDictionaryEntry
> xPosEntry( SearchDicList( xDList
,
239 rWord
, nLanguage
, true, true ) );
249 bool SpellCheckerDispatcher::isValid_Impl(
250 const OUString
& rWord
,
251 LanguageType nLanguage
,
252 const PropertyValues
& rProperties
)
254 MutexGuard
aGuard( GetLinguMutex() );
258 if (LinguIsUnspecified( nLanguage
) || rWord
.isEmpty())
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;
267 OUString
aChkWord( rWord
);
268 Locale
aLocale( LanguageTag::convertToLocale( nLanguage
) );
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], '\'' );
276 RemoveHyphens( aChkWord
);
277 if (IsIgnoreControlChars( rProperties
, GetPropSet() ))
278 RemoveControlChars( aChkWord
);
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");
288 bool bTmpResValid
= false;
290 // try already instantiated services first
292 const Reference
< XSpellChecker
> *pRef
=
293 pEntry
->aSvcRefs
.getConstArray();
294 while (i
<= pEntry
->nLastTriedSvcIndex
295 && (!bTmpResValid
|| !bTmpRes
))
298 if (pRef
[i
].is() && pRef
[i
]->hasLocale( aLocale
))
300 bTmpRes
= GetCache().CheckWord( aChkWord
, nLanguage
);
303 bTmpRes
= pRef
[i
]->isValid( aChkWord
, aLocale
, rProperties
);
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
);
313 bTmpResValid
= false;
322 // if still no result instantiate new services and try those
323 if ((!bTmpResValid
|| !bTmpRes
)
324 && pEntry
->nLastTriedSvcIndex
< nLen
- 1)
326 const OUString
*pImplNames
= pEntry
->aSvcImplNames
.getConstArray();
327 Reference
< XSpellChecker
> *pRef
= pEntry
->aSvcRefs
.getArray();
329 Reference
< XComponentContext
> xContext(
330 comphelper::getProcessComponentContext() );
332 // build service initialization argument
333 Sequence
< Any
> aArgs(2);
334 aArgs
.getArray()[0] <<= GetPropSet();
336 while (i
< nLen
&& (!bTmpResValid
|| !bTmpRes
))
338 // create specific service via it's implementation name
339 Reference
< XSpellChecker
> xSpell
;
342 xSpell
.set( xContext
->getServiceManager()->createInstanceWithArgumentsAndContext(
343 pImplNames
[i
], aArgs
, xContext
),
346 catch (uno::Exception
&)
348 SAL_WARN( "linguistic", "createInstanceWithArguments failed" );
352 Reference
< XLinguServiceEventBroadcaster
>
353 xBroadcaster( xSpell
, UNO_QUERY
);
354 if (xBroadcaster
.is())
355 m_rMgr
.AddLngSvcEvtBroadcaster( xBroadcaster
);
358 if (xSpell
.is() && xSpell
->hasLocale( aLocale
))
360 bTmpRes
= GetCache().CheckWord( aChkWord
, nLanguage
);
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
);
372 bTmpResValid
= false;
376 pEntry
->nLastTriedSvcIndex
= static_cast<sal_Int16
>(i
);
380 // if language is not supported by any of the services
381 // remove it from the list.
384 if (!SvcListHasLanguage( *pEntry
, nLanguage
))
385 m_aSvcMap
.erase( nLanguage
);
389 // cross-check against results from dictionaries which have precedence!
390 if (GetDicList().is() && IsUseDicList( rProperties
, GetPropSet() ))
392 Reference
< XDictionaryEntry
> xTmp( lcl_GetRulingDictionaryEntry( aChkWord
, nLanguage
) );
394 bRes
= !xTmp
->isNegative();
396 setCharClass(LanguageTag(nLanguage
));
397 CapType ct
= capitalType(aChkWord
, m_oCharClass
? &*m_oCharClass
: nullptr);
398 if (ct
== CapType::INITCAP
|| ct
== CapType::ALLCAP
) {
399 Reference
< XDictionaryEntry
> xTmp2( lcl_GetRulingDictionaryEntry( makeLowerCase(aChkWord
, m_oCharClass
), nLanguage
) );
401 bRes
= !xTmp2
->isNegative();
412 Reference
< XSpellAlternatives
> SpellCheckerDispatcher::spell_Impl(
413 const OUString
& rWord
,
414 LanguageType nLanguage
,
415 const PropertyValues
& rProperties
)
417 MutexGuard
aGuard( GetLinguMutex() );
419 Reference
< XSpellAlternatives
> xRes
;
421 if (LinguIsUnspecified( nLanguage
) || rWord
.isEmpty())
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;
430 OUString
aChkWord( rWord
);
431 Locale
aLocale( LanguageTag::convertToLocale( nLanguage
) );
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], '\'' );
439 RemoveHyphens( aChkWord
);
440 if (IsIgnoreControlChars( rProperties
, GetPropSet() ))
441 RemoveControlChars( aChkWord
);
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");
450 Reference
< XSpellAlternatives
> xTmpRes
;
451 bool bTmpResValid
= false;
453 // try already instantiated services first
455 const Reference
< XSpellChecker
> *pRef
= pEntry
->aSvcRefs
.getConstArray();
456 sal_Int32 nNumSuggestions
= -1;
457 while (i
<= pEntry
->nLastTriedSvcIndex
458 && (!bTmpResValid
|| xTmpRes
.is()) )
461 if (pRef
[i
].is() && pRef
[i
]->hasLocale( aLocale
))
463 bool bOK
= GetCache().CheckWord( aChkWord
, nLanguage
);
468 xTmpRes
= pRef
[i
]->spell( aChkWord
, aLocale
, rProperties
);
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
);
478 bTmpResValid
= false;
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
)
488 nNumSuggestions
= xRes
->getAlternatives().getLength();
490 sal_Int32 nTmpNumSuggestions
= 0;
491 if (xTmpRes
.is() && bTmpResValid
)
492 nTmpNumSuggestions
= xTmpRes
->getAlternatives().getLength();
493 if (xRes
.is() && nNumSuggestions
== 0 && nTmpNumSuggestions
> 0)
496 nNumSuggestions
= nTmpNumSuggestions
;
503 // if still no result instantiate new services and try those
504 if ((!bTmpResValid
|| xTmpRes
.is())
505 && pEntry
->nLastTriedSvcIndex
< nLen
- 1)
507 const OUString
*pImplNames
= pEntry
->aSvcImplNames
.getConstArray();
508 Reference
< XSpellChecker
> *pRef
= pEntry
->aSvcRefs
.getArray();
510 Reference
< XComponentContext
> xContext(
511 comphelper::getProcessComponentContext() );
513 // build service initialization argument
514 Sequence
< Any
> aArgs(2);
515 aArgs
.getArray()[0] <<= GetPropSet();
517 sal_Int32 nNumSuggestions
= -1;
518 while (i
< nLen
&& (!bTmpResValid
|| xTmpRes
.is()))
520 // create specific service via it's implementation name
521 Reference
< XSpellChecker
> xSpell
;
524 xSpell
.set( xContext
->getServiceManager()->createInstanceWithArgumentsAndContext(
525 pImplNames
[i
], aArgs
, xContext
),
528 catch (uno::Exception
&)
530 SAL_WARN( "linguistic", "createInstanceWithArguments failed" );
534 Reference
< XLinguServiceEventBroadcaster
>
535 xBroadcaster( xSpell
, UNO_QUERY
);
536 if (xBroadcaster
.is())
537 m_rMgr
.AddLngSvcEvtBroadcaster( xBroadcaster
);
540 if (xSpell
.is() && xSpell
->hasLocale( aLocale
))
542 bool bOK
= GetCache().CheckWord( aChkWord
, nLanguage
);
547 xTmpRes
= xSpell
->spell( aChkWord
, aLocale
, rProperties
);
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
);
557 bTmpResValid
= false;
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
)
567 nNumSuggestions
= xRes
->getAlternatives().getLength();
569 sal_Int32 nTmpNumSuggestions
= 0;
570 if (xTmpRes
.is() && bTmpResValid
)
571 nTmpNumSuggestions
= xTmpRes
->getAlternatives().getLength();
572 if (xRes
.is() && nNumSuggestions
== 0 && nTmpNumSuggestions
> 0)
575 nNumSuggestions
= nTmpNumSuggestions
;
578 pEntry
->nLastTriedSvcIndex
= static_cast<sal_Int16
>(i
);
582 // if language is not supported by any of the services
583 // remove it from the list.
586 if (!SvcListHasLanguage( *pEntry
, nLanguage
))
587 m_aSvcMap
.erase( nLanguage
);
591 // if word is finally found to be correct
592 // clear previously remembered alternatives
593 if (bTmpResValid
&& !xTmpRes
.is())
596 // list of proposals found (to be checked against entries of
597 // negative dictionaries)
598 ProposalList aProposalList
;
599 sal_Int16 eFailureType
= -1; // no failure
602 aProposalList
.Append( xRes
->getAlternatives() );
603 eFailureType
= xRes
->getFailureType();
605 Reference
< XSearchableDictionaryList
> xDList
;
606 if (GetDicList().is() && IsUseDicList( rProperties
, GetPropSet() ))
607 xDList
= GetDicList();
609 // cross-check against results from user-dictionaries which have precedence!
612 Reference
< XDictionaryEntry
> xTmp( lcl_GetRulingDictionaryEntry( aChkWord
, nLanguage
) );
615 if (xTmp
->isNegative()) // negative entry found
617 eFailureType
= SpellFailure::IS_NEGATIVE_WORD
;
619 // replacement text to be added to suggestions, if not empty
620 OUString
aAddRplcTxt( xTmp
->getReplacementText() );
622 // replacement text must not be in negative dictionary itself
623 if (!aAddRplcTxt
.isEmpty() &&
624 !SearchDicList( xDList
, aAddRplcTxt
, nLanguage
, false, true ).is())
626 aProposalList
.Prepend( aAddRplcTxt
);
629 else // positive entry found
632 eFailureType
= -1; // no failure
637 setCharClass(LanguageTag(nLanguage
));
638 CapType ct
= capitalType(aChkWord
, m_oCharClass
? &*m_oCharClass
: nullptr);
639 if (ct
== CapType::INITCAP
|| ct
== CapType::ALLCAP
)
641 Reference
< XDictionaryEntry
> xTmp2( lcl_GetRulingDictionaryEntry( makeLowerCase(aChkWord
, m_oCharClass
), nLanguage
) );
644 if (xTmp2
->isNegative()) // negative entry found
646 eFailureType
= SpellFailure::IS_NEGATIVE_WORD
;
648 // replacement text to be added to suggestions, if not empty
649 OUString
aAddRplcTxt( xTmp2
->getReplacementText() );
651 // replacement text must not be in negative dictionary itself
652 if (!aAddRplcTxt
.isEmpty() &&
653 !SearchDicList( xDList
, aAddRplcTxt
, nLanguage
, false, true ).is())
657 case CapType::INITCAP
:
658 aProposalList
.Prepend( m_oCharClass
->titlecase(aAddRplcTxt
) );
660 case CapType::ALLCAP
:
661 aProposalList
.Prepend( m_oCharClass
->uppercase(aAddRplcTxt
) );
664 /* can't happen because of if ct == above */
669 else // positive entry found
672 eFailureType
= -1; // no failure
679 if (eFailureType
!= -1) // word misspelled or found in negative user-dictionary
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();
688 // remove entries listed in negative dictionaries
689 // (we don't want to display suggestions that will be regarded as misspelled later on)
691 SeqRemoveNegEntries( aProposals
, xDList
, nLanguage
);
693 uno::Reference
< linguistic2::XSetSpellAlternatives
> xSetAlt( xRes
, uno::UNO_QUERY
);
696 xSetAlt
->setAlternatives( comphelper::containerToSequence(aProposals
) );
697 xSetAlt
->setFailureType( eFailureType
);
703 SAL_WARN( "linguistic", "XSetSpellAlternatives not implemented!" );
705 else if (!aProposals
.empty())
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
,
710 comphelper::containerToSequence(aProposals
) );
719 uno::Sequence
< sal_Int16
> SAL_CALL
SpellCheckerDispatcher::getLanguages( )
721 MutexGuard
aGuard( GetLinguMutex() );
722 uno::Sequence
< Locale
> aTmp( getLocales() );
723 uno::Sequence
< sal_Int16
> aRes( LocaleSeqToLangSeq( aTmp
) );
728 sal_Bool SAL_CALL
SpellCheckerDispatcher::hasLanguage(
729 sal_Int16 nLanguage
)
731 MutexGuard
aGuard( GetLinguMutex() );
732 return hasLocale( LanguageTag::convertToLocale(LanguageType(static_cast<sal_uInt16
>(nLanguage
))));
736 sal_Bool SAL_CALL
SpellCheckerDispatcher::isValid(
737 const OUString
& rWord
,
739 const uno::Sequence
< beans::PropertyValue
>& rProperties
)
741 MutexGuard
aGuard( GetLinguMutex() );
742 return isValid( rWord
, LanguageTag::convertToLocale(LanguageType(static_cast<sal_uInt16
>(nLanguage
))), rProperties
);
746 uno::Reference
< linguistic2::XSpellAlternatives
> SAL_CALL
SpellCheckerDispatcher::spell(
747 const OUString
& rWord
,
749 const uno::Sequence
< beans::PropertyValue
>& rProperties
)
751 MutexGuard
aGuard( GetLinguMutex() );
752 return spell(rWord
, LanguageTag::convertToLocale(LanguageType(static_cast<sal_uInt16
>(nLanguage
))), rProperties
);
756 void SpellCheckerDispatcher::SetServiceList( const Locale
&rLocale
,
757 const Sequence
< OUString
> &rSvcImplNames
)
759 MutexGuard
aGuard( GetLinguMutex() );
762 m_pCache
->Flush(); // new services may spell differently...
764 LanguageType nLanguage
= LinguLocaleToLanguage( rLocale
);
766 sal_Int32 nLen
= rSvcImplNames
.getLength();
769 m_aSvcMap
.erase( nLanguage
);
773 LangSvcEntries_Spell
*pEntry
= m_aSvcMap
[ nLanguage
].get();
777 pEntry
->aSvcImplNames
= rSvcImplNames
;
778 pEntry
->aSvcRefs
= Sequence
< Reference
< XSpellChecker
> > ( nLen
);
782 auto pTmpEntry
= std::make_shared
<LangSvcEntries_Spell
>( rSvcImplNames
);
783 pTmpEntry
->aSvcRefs
= Sequence
< Reference
< XSpellChecker
> >( nLen
);
784 m_aSvcMap
[ nLanguage
] = pTmpEntry
;
791 SpellCheckerDispatcher::GetServiceList( const Locale
&rLocale
) const
793 MutexGuard
aGuard( GetLinguMutex() );
795 Sequence
< OUString
> aRes
;
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;
802 aRes
= pEntry
->aSvcImplNames
;
808 void SpellCheckerDispatcher::FlushSpellCache()
814 void SpellCheckerDispatcher::setCharClass(const LanguageTag
& rLanguageTag
)
816 if (m_oCharClass
&& m_oCharClass
->getLanguageTag() == rLanguageTag
)
818 m_oCharClass
.emplace( rLanguageTag
);
822 OUString
SpellCheckerDispatcher::makeLowerCase(const OUString
& aTerm
, const std::optional
<CharClass
> & pCC
)
825 return pCC
->lowercase(aTerm
);
829 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */