Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / linguistic / source / spelldsp.cxx
blobba0c45f08bc5f72c1dcc17d7e94ffa7b89e602a7
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
35 #include <vector>
37 #include "spelldsp.hxx"
38 #include <linguistic/spelldta.hxx>
39 #include "lngsvcmgr.hxx"
41 using namespace osl;
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;
50 // ProposalList: list of proposals for misspelled words
51 // The order of strings in the array should be left unchanged because the
52 // spellchecker should have put the more likely suggestions at the top.
53 // New entries will be added to the end but duplicates are to be avoided.
54 // Removing entries is done by assigning the empty string.
55 // The sequence is constructed from all non empty strings in the original
56 // while maintaining the order.
57 class ProposalList
59 std::vector< OUString > aVec;
61 bool HasEntry( const OUString &rText ) const;
63 public:
64 ProposalList() {}
65 ProposalList(const ProposalList&) = delete;
66 ProposalList& operator=(const ProposalList&) = delete;
68 size_t Count() const;
69 void Prepend( const OUString &rText );
70 void Append( const OUString &rNew );
71 void Append( const std::vector< OUString > &rNew );
72 void Append( const Sequence< OUString > &rNew );
73 std::vector< OUString > GetVector() const;
77 bool ProposalList::HasEntry( const OUString &rText ) const
79 bool bFound = false;
80 size_t nCnt = aVec.size();
81 for (size_t i = 0; !bFound && i < nCnt; ++i)
83 if (aVec[i] == rText)
84 bFound = true;
86 return bFound;
89 void ProposalList::Prepend( const OUString &rText )
91 if (!HasEntry( rText ))
92 aVec.insert( aVec.begin(), rText );
95 void ProposalList::Append( const OUString &rText )
97 if (!HasEntry( rText ))
98 aVec.push_back( rText );
101 void ProposalList::Append( const std::vector< OUString > &rNew )
103 size_t nLen = rNew.size();
104 for ( size_t i = 0; i < nLen; ++i)
106 const OUString &rText = rNew[i];
107 if (!HasEntry( rText ))
108 Append( rText );
112 void ProposalList::Append( const Sequence< OUString > &rNew )
114 for (const OUString& rText : rNew)
116 if (!HasEntry( rText ))
117 Append( rText );
121 size_t ProposalList::Count() const
123 // returns the number of non-empty strings in the vector
125 size_t nRes = 0;
126 size_t nLen = aVec.size();
127 for (size_t i = 0; i < nLen; ++i)
129 if (!aVec[i].isEmpty())
130 ++nRes;
132 return nRes;
135 std::vector< OUString > ProposalList::GetVector() const
137 sal_Int32 nCount = Count();
138 sal_Int32 nIdx = 0;
139 std::vector< OUString > aRes( nCount );
140 sal_Int32 nLen = aVec.size();
141 for (sal_Int32 i = 0; i < nLen; ++i)
143 const OUString &rText = aVec[i];
144 DBG_ASSERT( nIdx < nCount, "index out of range" );
145 if (nIdx < nCount && !rText.isEmpty())
146 aRes[ nIdx++ ] = rText;
148 return aRes;
151 static bool SvcListHasLanguage(
152 const LangSvcEntries_Spell &rEntry,
153 LanguageType nLanguage )
155 Locale aTmpLocale = LanguageTag::convertToLocale( nLanguage );
157 return std::any_of(rEntry.aSvcRefs.begin(), rEntry.aSvcRefs.end(),
158 [&aTmpLocale](const Reference<XSpellChecker>& rRef) {
159 return rRef.is() && rRef->hasLocale( aTmpLocale ); });
162 SpellCheckerDispatcher::SpellCheckerDispatcher( LngSvcMgr &rLngSvcMgr ) :
163 m_rMgr (rLngSvcMgr)
168 SpellCheckerDispatcher::~SpellCheckerDispatcher()
173 Sequence< Locale > SAL_CALL SpellCheckerDispatcher::getLocales()
175 MutexGuard aGuard( GetLinguMutex() );
177 std::vector<Locale> aLocales;
178 aLocales.reserve(m_aSvcMap.size());
180 std::transform(m_aSvcMap.begin(), m_aSvcMap.end(), std::back_inserter(aLocales),
181 [](SpellSvcByLangMap_t::const_reference elem) { return LanguageTag::convertToLocale(elem.first); });
183 return comphelper::containerToSequence(aLocales);
187 sal_Bool SAL_CALL SpellCheckerDispatcher::hasLocale( const Locale& rLocale )
189 MutexGuard aGuard( GetLinguMutex() );
190 SpellSvcByLangMap_t::const_iterator aIt( m_aSvcMap.find( LinguLocaleToLanguage( rLocale ) ) );
191 return aIt != m_aSvcMap.end();
195 sal_Bool SAL_CALL
196 SpellCheckerDispatcher::isValid( const OUString& rWord, const Locale& rLocale,
197 const css::uno::Sequence< ::css::beans::PropertyValue >& rProperties )
199 MutexGuard aGuard( GetLinguMutex() );
200 return isValid_Impl( rWord, LinguLocaleToLanguage( rLocale ), rProperties );
204 Reference< XSpellAlternatives > SAL_CALL
205 SpellCheckerDispatcher::spell( const OUString& rWord, const Locale& rLocale,
206 const css::uno::Sequence< ::css::beans::PropertyValue >& rProperties )
208 MutexGuard aGuard( GetLinguMutex() );
209 return spell_Impl( rWord, LinguLocaleToLanguage( rLocale ), rProperties );
213 // returns the overall result of cross-checking with all user-dictionaries
214 // including the IgnoreAll list
215 static Reference< XDictionaryEntry > lcl_GetRulingDictionaryEntry(
216 const OUString &rWord,
217 LanguageType nLanguage )
219 Reference< XDictionaryEntry > xRes;
221 // the order of winning from top to bottom is:
222 // 1) IgnoreAll list will always win
223 // 2) Negative dictionaries will win over positive dictionaries
224 Reference< XDictionary > xIgnoreAll( GetIgnoreAllList() );
225 if (xIgnoreAll.is())
226 xRes = xIgnoreAll->getEntry( rWord );
227 if (!xRes.is())
229 Reference< XSearchableDictionaryList > xDList( GetDictionaryList() );
230 Reference< XDictionaryEntry > xNegEntry( SearchDicList( xDList,
231 rWord, nLanguage, false, true ) );
232 if (xNegEntry.is())
233 xRes = xNegEntry;
234 else
236 Reference< XDictionaryEntry > xPosEntry( SearchDicList( xDList,
237 rWord, nLanguage, true, true ) );
238 if (xPosEntry.is())
239 xRes = xPosEntry;
243 return xRes;
247 bool SpellCheckerDispatcher::isValid_Impl(
248 const OUString& rWord,
249 LanguageType nLanguage,
250 const PropertyValues& rProperties)
252 MutexGuard aGuard( GetLinguMutex() );
254 bool bRes = true;
256 if (LinguIsUnspecified( nLanguage) || rWord.isEmpty())
257 return bRes;
259 // search for entry with that language
260 SpellSvcByLangMap_t::iterator aIt( m_aSvcMap.find( nLanguage ) );
261 LangSvcEntries_Spell *pEntry = aIt != m_aSvcMap.end() ? aIt->second.get() : nullptr;
263 if (pEntry)
265 OUString aChkWord( rWord );
266 Locale aLocale( LanguageTag::convertToLocale( nLanguage ) );
268 // replace typographical apostroph by ascii apostroph
269 OUString aSingleQuote( GetLocaleDataWrapper( nLanguage ).getQuotationMarkEnd() );
270 DBG_ASSERT( 1 == aSingleQuote.getLength(), "unexpected length of quotation mark" );
271 if (!aSingleQuote.isEmpty())
272 aChkWord = aChkWord.replace( aSingleQuote[0], '\'' );
274 RemoveHyphens( aChkWord );
275 if (IsIgnoreControlChars( rProperties, GetPropSet() ))
276 RemoveControlChars( aChkWord );
278 sal_Int32 nLen = pEntry->aSvcRefs.getLength();
279 DBG_ASSERT( nLen == pEntry->aSvcImplNames.getLength(),
280 "lng : sequence length mismatch");
281 DBG_ASSERT( pEntry->nLastTriedSvcIndex < nLen,
282 "lng : index out of range");
284 sal_Int32 i = 0;
285 bool bTmpRes = true;
286 bool bTmpResValid = false;
288 // try already instantiated services first
290 const Reference< XSpellChecker > *pRef =
291 pEntry->aSvcRefs.getConstArray();
292 while (i <= pEntry->nLastTriedSvcIndex
293 && (!bTmpResValid || !bTmpRes))
295 bTmpResValid = true;
296 if (pRef[i].is() && pRef[i]->hasLocale( aLocale ))
298 bTmpRes = GetCache().CheckWord( aChkWord, nLanguage );
299 if (!bTmpRes)
301 bTmpRes = pRef[i]->isValid( aChkWord, aLocale, rProperties );
303 // Add correct words to the cache.
304 // But not those that are correct only because of
305 // the temporary supplied settings.
306 if (bTmpRes && !rProperties.hasElements())
307 GetCache().AddWord( aChkWord, nLanguage );
310 else
311 bTmpResValid = false;
313 if (bTmpResValid)
314 bRes = bTmpRes;
316 ++i;
320 // if still no result instantiate new services and try those
321 if ((!bTmpResValid || !bTmpRes)
322 && pEntry->nLastTriedSvcIndex < nLen - 1)
324 const OUString *pImplNames = pEntry->aSvcImplNames.getConstArray();
325 Reference< XSpellChecker > *pRef = pEntry->aSvcRefs .getArray();
327 Reference< XComponentContext > xContext(
328 comphelper::getProcessComponentContext() );
330 // build service initialization argument
331 Sequence< Any > aArgs(2);
332 aArgs.getArray()[0] <<= GetPropSet();
334 while (i < nLen && (!bTmpResValid || !bTmpRes))
336 // create specific service via it's implementation name
337 Reference< XSpellChecker > xSpell;
340 xSpell.set( xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
341 pImplNames[i], aArgs, xContext ),
342 UNO_QUERY );
344 catch (uno::Exception &)
346 SAL_WARN( "linguistic", "createInstanceWithArguments failed" );
348 pRef [i] = xSpell;
350 Reference< XLinguServiceEventBroadcaster >
351 xBroadcaster( xSpell, UNO_QUERY );
352 if (xBroadcaster.is())
353 m_rMgr.AddLngSvcEvtBroadcaster( xBroadcaster );
355 bTmpResValid = true;
356 if (xSpell.is() && xSpell->hasLocale( aLocale ))
358 bTmpRes = GetCache().CheckWord( aChkWord, nLanguage );
359 if (!bTmpRes)
361 bTmpRes = xSpell->isValid( aChkWord, aLocale, rProperties );
362 // Add correct words to the cache.
363 // But not those that are correct only because of
364 // the temporary supplied settings.
365 if (bTmpRes && !rProperties.hasElements())
366 GetCache().AddWord( aChkWord, nLanguage );
369 else
370 bTmpResValid = false;
371 if (bTmpResValid)
372 bRes = bTmpRes;
374 pEntry->nLastTriedSvcIndex = static_cast<sal_Int16>(i);
375 ++i;
378 // if language is not supported by any of the services
379 // remove it from the list.
380 if (i == nLen)
382 if (!SvcListHasLanguage( *pEntry, nLanguage ))
383 m_aSvcMap.erase( nLanguage );
387 // cross-check against results from dictionaries which have precedence!
388 if (GetDicList().is() && IsUseDicList( rProperties, GetPropSet() ))
390 Reference< XDictionaryEntry > xTmp( lcl_GetRulingDictionaryEntry( aChkWord, nLanguage ) );
391 if (xTmp.is()) {
392 bRes = !xTmp->isNegative();
393 } else {
394 setCharClass(LanguageTag(nLanguage));
395 CapType ct = capitalType(aChkWord, m_pCharClass.get());
396 if (ct == CapType::INITCAP || ct == CapType::ALLCAP) {
397 Reference< XDictionaryEntry > xTmp2( lcl_GetRulingDictionaryEntry( makeLowerCase(aChkWord, m_pCharClass.get()), nLanguage ) );
398 if (xTmp2.is()) {
399 bRes = !xTmp2->isNegative();
406 return bRes;
410 Reference< XSpellAlternatives > SpellCheckerDispatcher::spell_Impl(
411 const OUString& rWord,
412 LanguageType nLanguage,
413 const PropertyValues& rProperties )
415 MutexGuard aGuard( GetLinguMutex() );
417 Reference< XSpellAlternatives > xRes;
419 if (LinguIsUnspecified( nLanguage) || rWord.isEmpty())
420 return xRes;
422 // search for entry with that language
423 SpellSvcByLangMap_t::iterator aIt( m_aSvcMap.find( nLanguage ) );
424 LangSvcEntries_Spell *pEntry = aIt != m_aSvcMap.end() ? aIt->second.get() : nullptr;
426 if (pEntry)
428 OUString aChkWord( rWord );
429 Locale aLocale( LanguageTag::convertToLocale( nLanguage ) );
431 // replace typographical apostroph by ascii apostroph
432 OUString aSingleQuote( GetLocaleDataWrapper( nLanguage ).getQuotationMarkEnd() );
433 DBG_ASSERT( 1 == aSingleQuote.getLength(), "unexpected length of quotation mark" );
434 if (!aSingleQuote.isEmpty())
435 aChkWord = aChkWord.replace( aSingleQuote[0], '\'' );
437 RemoveHyphens( aChkWord );
438 if (IsIgnoreControlChars( rProperties, GetPropSet() ))
439 RemoveControlChars( aChkWord );
441 sal_Int32 nLen = pEntry->aSvcRefs.getLength();
442 DBG_ASSERT( nLen == pEntry->aSvcImplNames.getLength(),
443 "lng : sequence length mismatch");
444 DBG_ASSERT( pEntry->nLastTriedSvcIndex < nLen,
445 "lng : index out of range");
447 sal_Int32 i = 0;
448 Reference< XSpellAlternatives > xTmpRes;
449 bool bTmpResValid = false;
451 // try already instantiated services first
453 const Reference< XSpellChecker > *pRef = pEntry->aSvcRefs.getConstArray();
454 sal_Int32 nNumSugestions = -1;
455 while (i <= pEntry->nLastTriedSvcIndex
456 && (!bTmpResValid || xTmpRes.is()) )
458 bTmpResValid = true;
459 if (pRef[i].is() && pRef[i]->hasLocale( aLocale ))
461 bool bOK = GetCache().CheckWord( aChkWord, nLanguage );
462 if (bOK)
463 xTmpRes = nullptr;
464 else
466 xTmpRes = pRef[i]->spell( aChkWord, aLocale, rProperties );
468 // Add correct words to the cache.
469 // But not those that are correct only because of
470 // the temporary supplied settings.
471 if (!xTmpRes.is() && !rProperties.hasElements())
472 GetCache().AddWord( aChkWord, nLanguage );
475 else
476 bTmpResValid = false;
478 // return first found result if the word is not known by any checker.
479 // But if that result has no suggestions use the first one that does
480 // provide suggestions for the misspelled word.
481 if (!xRes.is() && bTmpResValid)
483 xRes = xTmpRes;
484 nNumSugestions = 0;
485 if (xRes.is())
486 nNumSugestions = xRes->getAlternatives().getLength();
488 sal_Int32 nTmpNumSugestions = 0;
489 if (xTmpRes.is() && bTmpResValid)
490 nTmpNumSugestions = xTmpRes->getAlternatives().getLength();
491 if (xRes.is() && nNumSugestions == 0 && nTmpNumSugestions > 0)
493 xRes = xTmpRes;
494 nNumSugestions = nTmpNumSugestions;
497 ++i;
501 // if still no result instantiate new services and try those
502 if ((!bTmpResValid || xTmpRes.is())
503 && pEntry->nLastTriedSvcIndex < nLen - 1)
505 const OUString *pImplNames = pEntry->aSvcImplNames.getConstArray();
506 Reference< XSpellChecker > *pRef = pEntry->aSvcRefs .getArray();
508 Reference< XComponentContext > xContext(
509 comphelper::getProcessComponentContext() );
511 // build service initialization argument
512 Sequence< Any > aArgs(2);
513 aArgs.getArray()[0] <<= GetPropSet();
515 sal_Int32 nNumSugestions = -1;
516 while (i < nLen && (!bTmpResValid || xTmpRes.is()))
518 // create specific service via it's implementation name
519 Reference< XSpellChecker > xSpell;
522 xSpell.set( xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
523 pImplNames[i], aArgs, xContext ),
524 UNO_QUERY );
526 catch (uno::Exception &)
528 SAL_WARN( "linguistic", "createInstanceWithArguments failed" );
530 pRef [i] = xSpell;
532 Reference< XLinguServiceEventBroadcaster >
533 xBroadcaster( xSpell, UNO_QUERY );
534 if (xBroadcaster.is())
535 m_rMgr.AddLngSvcEvtBroadcaster( xBroadcaster );
537 bTmpResValid = true;
538 if (xSpell.is() && xSpell->hasLocale( aLocale ))
540 bool bOK = GetCache().CheckWord( aChkWord, nLanguage );
541 if (bOK)
542 xTmpRes = nullptr;
543 else
545 xTmpRes = xSpell->spell( aChkWord, aLocale, rProperties );
547 // Add correct words to the cache.
548 // But not those that are correct only because of
549 // the temporary supplied settings.
550 if (!xTmpRes.is() && !rProperties.hasElements())
551 GetCache().AddWord( aChkWord, nLanguage );
554 else
555 bTmpResValid = false;
557 // return first found result if the word is not known by any checker.
558 // But if that result has no suggestions use the first one that does
559 // provide suggestions for the misspelled word.
560 if (!xRes.is() && bTmpResValid)
562 xRes = xTmpRes;
563 nNumSugestions = 0;
564 if (xRes.is())
565 nNumSugestions = xRes->getAlternatives().getLength();
567 sal_Int32 nTmpNumSugestions = 0;
568 if (xTmpRes.is() && bTmpResValid)
569 nTmpNumSugestions = xTmpRes->getAlternatives().getLength();
570 if (xRes.is() && nNumSugestions == 0 && nTmpNumSugestions > 0)
572 xRes = xTmpRes;
573 nNumSugestions = nTmpNumSugestions;
576 pEntry->nLastTriedSvcIndex = static_cast<sal_Int16>(i);
577 ++i;
580 // if language is not supported by any of the services
581 // remove it from the list.
582 if (i == nLen)
584 if (!SvcListHasLanguage( *pEntry, nLanguage ))
585 m_aSvcMap.erase( nLanguage );
589 // if word is finally found to be correct
590 // clear previously remembered alternatives
591 if (bTmpResValid && !xTmpRes.is())
592 xRes = nullptr;
594 // list of proposals found (to be checked against entries of
595 // negative dictionaries)
596 ProposalList aProposalList;
597 sal_Int16 eFailureType = -1; // no failure
598 if (xRes.is())
600 aProposalList.Append( xRes->getAlternatives() );
601 eFailureType = xRes->getFailureType();
603 Reference< XSearchableDictionaryList > xDList;
604 if (GetDicList().is() && IsUseDicList( rProperties, GetPropSet() ))
605 xDList = GetDicList();
607 // cross-check against results from user-dictionaries which have precedence!
608 if (xDList.is())
610 Reference< XDictionaryEntry > xTmp( lcl_GetRulingDictionaryEntry( aChkWord, nLanguage ) );
611 if (xTmp.is())
613 if (xTmp->isNegative()) // negative entry found
615 eFailureType = SpellFailure::IS_NEGATIVE_WORD;
617 // replacement text to be added to suggestions, if not empty
618 OUString aAddRplcTxt( xTmp->getReplacementText() );
620 // replacement text must not be in negative dictionary itself
621 if (!aAddRplcTxt.isEmpty() &&
622 !SearchDicList( xDList, aAddRplcTxt, nLanguage, false, true ).is())
624 aProposalList.Prepend( aAddRplcTxt );
627 else // positive entry found
629 xRes = nullptr;
630 eFailureType = -1; // no failure
633 else
635 setCharClass(LanguageTag(nLanguage));
636 CapType ct = capitalType(aChkWord, m_pCharClass.get());
637 if (ct == CapType::INITCAP || ct == CapType::ALLCAP)
639 Reference< XDictionaryEntry > xTmp2( lcl_GetRulingDictionaryEntry( makeLowerCase(aChkWord, m_pCharClass.get()), nLanguage ) );
640 if (xTmp2.is())
642 if (xTmp2->isNegative()) // negative entry found
644 eFailureType = SpellFailure::IS_NEGATIVE_WORD;
646 // replacement text to be added to suggestions, if not empty
647 OUString aAddRplcTxt( xTmp2->getReplacementText() );
649 // replacement text must not be in negative dictionary itself
650 if (!aAddRplcTxt.isEmpty() &&
651 !SearchDicList( xDList, aAddRplcTxt, nLanguage, false, true ).is())
653 switch ( ct )
655 case CapType::INITCAP:
656 aProposalList.Prepend( m_pCharClass->titlecase(aAddRplcTxt) );
657 break;
658 case CapType::ALLCAP:
659 aProposalList.Prepend( m_pCharClass->uppercase(aAddRplcTxt) );
660 break;
661 default:
662 /* can't happen because of if ct == above */
663 break;
667 else // positive entry found
669 xRes = nullptr;
670 eFailureType = -1; // no failure
677 if (eFailureType != -1) // word misspelled or found in negative user-dictionary
679 // search suitable user-dictionaries for suggestions that are
680 // similar to the misspelled word
681 std::vector< OUString > aDicListProps; // list of proposals from user-dictionaries
682 SearchSimilarText( aChkWord, nLanguage, xDList, aDicListProps );
683 aProposalList.Append( aDicListProps );
684 std::vector< OUString > aProposals = aProposalList.GetVector();
686 // remove entries listed in negative dictionaries
687 // (we don't want to display suggestions that will be regarded as misspelled later on)
688 if (xDList.is())
689 SeqRemoveNegEntries( aProposals, xDList, nLanguage );
691 uno::Reference< linguistic2::XSetSpellAlternatives > xSetAlt( xRes, uno::UNO_QUERY );
692 if (xSetAlt.is())
694 xSetAlt->setAlternatives( comphelper::containerToSequence(aProposals) );
695 xSetAlt->setFailureType( eFailureType );
697 else
699 if (xRes.is())
701 SAL_WARN( "linguistic", "XSetSpellAlternatives not implemented!" );
703 else if (!aProposals.empty())
705 // no xRes but Proposals found from the user-dictionaries.
706 // Thus we need to create an xRes...
707 xRes = new linguistic::SpellAlternatives( rWord, nLanguage,
708 comphelper::containerToSequence(aProposals) );
714 return xRes;
717 uno::Sequence< sal_Int16 > SAL_CALL SpellCheckerDispatcher::getLanguages( )
719 MutexGuard aGuard( GetLinguMutex() );
720 uno::Sequence< Locale > aTmp( getLocales() );
721 uno::Sequence< sal_Int16 > aRes( LocaleSeqToLangSeq( aTmp ) );
722 return aRes;
726 sal_Bool SAL_CALL SpellCheckerDispatcher::hasLanguage(
727 sal_Int16 nLanguage )
729 MutexGuard aGuard( GetLinguMutex() );
730 return hasLocale( LanguageTag::convertToLocale(LanguageType(static_cast<sal_uInt16>(nLanguage))));
734 sal_Bool SAL_CALL SpellCheckerDispatcher::isValid(
735 const OUString& rWord,
736 sal_Int16 nLanguage,
737 const uno::Sequence< beans::PropertyValue >& rProperties )
739 MutexGuard aGuard( GetLinguMutex() );
740 return isValid( rWord, LanguageTag::convertToLocale(LanguageType(static_cast<sal_uInt16>(nLanguage))), rProperties);
744 uno::Reference< linguistic2::XSpellAlternatives > SAL_CALL SpellCheckerDispatcher::spell(
745 const OUString& rWord,
746 sal_Int16 nLanguage,
747 const uno::Sequence< beans::PropertyValue >& rProperties )
749 MutexGuard aGuard( GetLinguMutex() );
750 return spell(rWord, LanguageTag::convertToLocale(LanguageType(static_cast<sal_uInt16>(nLanguage))), rProperties);
754 void SpellCheckerDispatcher::SetServiceList( const Locale &rLocale,
755 const Sequence< OUString > &rSvcImplNames )
757 MutexGuard aGuard( GetLinguMutex() );
759 if (m_pCache)
760 m_pCache->Flush(); // new services may spell differently...
762 LanguageType nLanguage = LinguLocaleToLanguage( rLocale );
764 sal_Int32 nLen = rSvcImplNames.getLength();
765 if (0 == nLen)
766 // remove entry
767 m_aSvcMap.erase( nLanguage );
768 else
770 // modify/add entry
771 LangSvcEntries_Spell *pEntry = m_aSvcMap[ nLanguage ].get();
772 if (pEntry)
774 pEntry->Clear();
775 pEntry->aSvcImplNames = rSvcImplNames;
776 pEntry->aSvcRefs = Sequence< Reference < XSpellChecker > > ( nLen );
778 else
780 std::shared_ptr< LangSvcEntries_Spell > pTmpEntry( new LangSvcEntries_Spell( rSvcImplNames ) );
781 pTmpEntry->aSvcRefs = Sequence< Reference < XSpellChecker > >( nLen );
782 m_aSvcMap[ nLanguage ] = pTmpEntry;
788 Sequence< OUString >
789 SpellCheckerDispatcher::GetServiceList( const Locale &rLocale ) const
791 MutexGuard aGuard( GetLinguMutex() );
793 Sequence< OUString > aRes;
795 // search for entry with that language and use data from that
796 LanguageType nLanguage = LinguLocaleToLanguage( rLocale );
797 const SpellSvcByLangMap_t::const_iterator aIt( m_aSvcMap.find( nLanguage ) );
798 const LangSvcEntries_Spell *pEntry = aIt != m_aSvcMap.end() ? aIt->second.get() : nullptr;
799 if (pEntry)
800 aRes = pEntry->aSvcImplNames;
802 return aRes;
806 void SpellCheckerDispatcher::FlushSpellCache()
808 if (m_pCache)
809 m_pCache->Flush();
812 void SpellCheckerDispatcher::setCharClass(const LanguageTag& rLanguageTag)
814 if (!m_pCharClass)
815 m_pCharClass.reset( new CharClass(rLanguageTag) );
816 m_pCharClass->setLanguageTag(rLanguageTag);
820 OUString SpellCheckerDispatcher::makeLowerCase(const OUString& aTerm, CharClass const * pCC)
822 if (pCC)
823 return pCC->lowercase(aTerm);
824 return aTerm;
827 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */