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
;
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.
59 std::vector
< OUString
> aVec
;
61 bool HasEntry( const OUString
&rText
) const;
65 ProposalList(const ProposalList
&) = delete;
66 ProposalList
& operator=(const ProposalList
&) = delete;
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
80 size_t nCnt
= aVec
.size();
81 for (size_t i
= 0; !bFound
&& i
< nCnt
; ++i
)
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
))
112 void ProposalList::Append( const Sequence
< OUString
> &rNew
)
114 for (const OUString
& rText
: rNew
)
116 if (!HasEntry( rText
))
121 size_t ProposalList::Count() const
123 // returns the number of non-empty strings in the vector
126 size_t nLen
= aVec
.size();
127 for (size_t i
= 0; i
< nLen
; ++i
)
129 if (!aVec
[i
].isEmpty())
135 std::vector
< OUString
> ProposalList::GetVector() const
137 sal_Int32 nCount
= Count();
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
;
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
) :
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();
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() );
226 xRes
= xIgnoreAll
->getEntry( rWord
);
229 Reference
< XSearchableDictionaryList
> xDList( GetDictionaryList() );
230 Reference
< XDictionaryEntry
> xNegEntry( SearchDicList( xDList
,
231 rWord
, nLanguage
, false, true ) );
236 Reference
< XDictionaryEntry
> xPosEntry( SearchDicList( xDList
,
237 rWord
, nLanguage
, true, true ) );
247 bool SpellCheckerDispatcher::isValid_Impl(
248 const OUString
& rWord
,
249 LanguageType nLanguage
,
250 const PropertyValues
& rProperties
)
252 MutexGuard
aGuard( GetLinguMutex() );
256 if (LinguIsUnspecified( nLanguage
) || rWord
.isEmpty())
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;
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");
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
))
296 if (pRef
[i
].is() && pRef
[i
]->hasLocale( aLocale
))
298 bTmpRes
= GetCache().CheckWord( aChkWord
, nLanguage
);
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
);
311 bTmpResValid
= false;
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
),
344 catch (uno::Exception
&)
346 SAL_WARN( "linguistic", "createInstanceWithArguments failed" );
350 Reference
< XLinguServiceEventBroadcaster
>
351 xBroadcaster( xSpell
, UNO_QUERY
);
352 if (xBroadcaster
.is())
353 m_rMgr
.AddLngSvcEvtBroadcaster( xBroadcaster
);
356 if (xSpell
.is() && xSpell
->hasLocale( aLocale
))
358 bTmpRes
= GetCache().CheckWord( aChkWord
, nLanguage
);
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
);
370 bTmpResValid
= false;
374 pEntry
->nLastTriedSvcIndex
= static_cast<sal_Int16
>(i
);
378 // if language is not supported by any of the services
379 // remove it from the list.
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
) );
392 bRes
= !xTmp
->isNegative();
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
) );
399 bRes
= !xTmp2
->isNegative();
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())
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;
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");
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()) )
459 if (pRef
[i
].is() && pRef
[i
]->hasLocale( aLocale
))
461 bool bOK
= GetCache().CheckWord( aChkWord
, nLanguage
);
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
);
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
)
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)
494 nNumSugestions
= nTmpNumSugestions
;
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
),
526 catch (uno::Exception
&)
528 SAL_WARN( "linguistic", "createInstanceWithArguments failed" );
532 Reference
< XLinguServiceEventBroadcaster
>
533 xBroadcaster( xSpell
, UNO_QUERY
);
534 if (xBroadcaster
.is())
535 m_rMgr
.AddLngSvcEvtBroadcaster( xBroadcaster
);
538 if (xSpell
.is() && xSpell
->hasLocale( aLocale
))
540 bool bOK
= GetCache().CheckWord( aChkWord
, nLanguage
);
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
);
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
)
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)
573 nNumSugestions
= nTmpNumSugestions
;
576 pEntry
->nLastTriedSvcIndex
= static_cast<sal_Int16
>(i
);
580 // if language is not supported by any of the services
581 // remove it from the list.
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())
594 // list of proposals found (to be checked against entries of
595 // negative dictionaries)
596 ProposalList aProposalList
;
597 sal_Int16 eFailureType
= -1; // no failure
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!
610 Reference
< XDictionaryEntry
> xTmp( lcl_GetRulingDictionaryEntry( aChkWord
, nLanguage
) );
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
630 eFailureType
= -1; // no failure
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
) );
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())
655 case CapType::INITCAP
:
656 aProposalList
.Prepend( m_pCharClass
->titlecase(aAddRplcTxt
) );
658 case CapType::ALLCAP
:
659 aProposalList
.Prepend( m_pCharClass
->uppercase(aAddRplcTxt
) );
662 /* can't happen because of if ct == above */
667 else // positive entry found
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)
689 SeqRemoveNegEntries( aProposals
, xDList
, nLanguage
);
691 uno::Reference
< linguistic2::XSetSpellAlternatives
> xSetAlt( xRes
, uno::UNO_QUERY
);
694 xSetAlt
->setAlternatives( comphelper::containerToSequence(aProposals
) );
695 xSetAlt
->setFailureType( eFailureType
);
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
) );
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
) );
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
,
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
,
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() );
760 m_pCache
->Flush(); // new services may spell differently...
762 LanguageType nLanguage
= LinguLocaleToLanguage( rLocale
);
764 sal_Int32 nLen
= rSvcImplNames
.getLength();
767 m_aSvcMap
.erase( nLanguage
);
771 LangSvcEntries_Spell
*pEntry
= m_aSvcMap
[ nLanguage
].get();
775 pEntry
->aSvcImplNames
= rSvcImplNames
;
776 pEntry
->aSvcRefs
= Sequence
< Reference
< XSpellChecker
> > ( nLen
);
780 std::shared_ptr
< LangSvcEntries_Spell
> pTmpEntry( new LangSvcEntries_Spell( rSvcImplNames
) );
781 pTmpEntry
->aSvcRefs
= Sequence
< Reference
< XSpellChecker
> >( nLen
);
782 m_aSvcMap
[ nLanguage
] = pTmpEntry
;
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;
800 aRes
= pEntry
->aSvcImplNames
;
806 void SpellCheckerDispatcher::FlushSpellCache()
812 void SpellCheckerDispatcher::setCharClass(const LanguageTag
& rLanguageTag
)
815 m_pCharClass
.reset( new CharClass(rLanguageTag
) );
816 m_pCharClass
->setLanguageTag(rLanguageTag
);
820 OUString
SpellCheckerDispatcher::makeLowerCase(const OUString
& aTerm
, CharClass
const * pCC
)
823 return pCC
->lowercase(aTerm
);
827 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */