Update ooo320-m1
[ooovba.git] / linguistic / source / spelldsp.cxx
blobe88a509ccb5cce0da825ad7b3feb656ae01723c6
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: spelldsp.cxx,v $
10 * $Revision: 1.23 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_linguistic.hxx"
34 #include <com/sun/star/uno/Reference.h>
35 #include <com/sun/star/linguistic2/XSearchableDictionaryList.hpp>
36 #include <com/sun/star/linguistic2/SpellFailure.hpp>
37 #include <com/sun/star/registry/XRegistryKey.hpp>
39 #include <cppuhelper/factory.hxx> // helper for factories
40 #include <unotools/localedatawrapper.hxx>
41 #include <unotools/processfactory.hxx>
42 #include <tools/debug.hxx>
43 #include <svtools/lngmisc.hxx>
44 #include <osl/mutex.hxx>
46 #include <vector>
48 #include "spelldsp.hxx"
49 #include "spelldta.hxx"
50 #include "lngsvcmgr.hxx"
51 #include "lngprops.hxx"
54 using namespace utl;
55 using namespace osl;
56 using namespace rtl;
57 using namespace com::sun::star;
58 using namespace com::sun::star::beans;
59 using namespace com::sun::star::lang;
60 using namespace com::sun::star::uno;
61 using namespace com::sun::star::linguistic2;
62 using namespace linguistic;
64 ///////////////////////////////////////////////////////////////////////////
65 // ProposalList: list of proposals for misspelled words
66 // The order of strings in the array should be left unchanged because the
67 // spellchecker should have put the more likely suggestions at the top.
68 // New entries will be added to the end but duplicates are to be avoided.
69 // Removing entries is done by assigning the empty string.
70 // The sequence is constructed from all non empty strings in the original
71 // while maintaining the order.
73 class ProposalList
75 std::vector< OUString > aVec;
77 BOOL HasEntry( const OUString &rText ) const;
79 // make copy c-tor and assignment operator private
80 ProposalList( const ProposalList & );
81 ProposalList & operator = ( const ProposalList & );
83 public:
84 ProposalList() {}
86 //size_t Size() const { return aVec.size(); }
87 size_t Count() const;
88 void Prepend( const OUString &rText );
89 void Append( const OUString &rNew );
90 void Append( const std::vector< OUString > &rNew );
91 void Append( const Sequence< OUString > &rNew );
92 void Remove( const OUString &rText );
93 Sequence< OUString > GetSequence() const;
97 BOOL ProposalList::HasEntry( const OUString &rText ) const
99 BOOL bFound = FALSE;
100 size_t nCnt = aVec.size();
101 for (size_t i = 0; !bFound && i < nCnt; ++i)
103 if (aVec[i] == rText)
104 bFound = TRUE;
106 return bFound;
109 void ProposalList::Prepend( const OUString &rText )
111 if (!HasEntry( rText ))
112 aVec.insert( aVec.begin(), rText );
115 void ProposalList::Append( const OUString &rText )
117 if (!HasEntry( rText ))
118 aVec.push_back( rText );
121 void ProposalList::Append( const std::vector< OUString > &rNew )
123 size_t nLen = rNew.size();
124 for ( size_t i = 0; i < nLen; ++i)
126 const OUString &rText = rNew[i];
127 if (!HasEntry( rText ))
128 Append( rText );
132 void ProposalList::Append( const Sequence< OUString > &rNew )
134 INT32 nLen = rNew.getLength();
135 const OUString *pNew = rNew.getConstArray();
136 for (INT32 i = 0; i < nLen; ++i)
138 const OUString &rText = pNew[i];
139 if (!HasEntry( rText ))
140 Append( rText );
144 size_t ProposalList::Count() const
146 // returns the number of non-empty strings in the vector
148 size_t nRes = 0;
149 size_t nLen = aVec.size();
150 for (size_t i = 0; i < nLen; ++i)
152 if (aVec[i].getLength() != 0)
153 ++nRes;
155 return nRes;
158 Sequence< OUString > ProposalList::GetSequence() const
160 INT32 nCount = Count();
161 INT32 nIdx = 0;
162 Sequence< OUString > aRes( nCount );
163 OUString *pRes = aRes.getArray();
164 INT32 nLen = aVec.size();
165 for (INT32 i = 0; i < nLen; ++i)
167 const OUString &rText = aVec[i];
168 DBG_ASSERT( nIdx < nCount, "index our of range" );
169 if (nIdx < nCount && rText.getLength() > 0)
170 pRes[ nIdx++ ] = rText;
172 return aRes;
175 void ProposalList::Remove( const OUString &rText )
177 size_t nLen = aVec.size();
178 for (size_t i = 0; i < nLen; ++i)
180 OUString &rEntry = aVec[i];
181 if (rEntry == rText)
183 rEntry = OUString();
184 break; // there should be only one matching entry
190 ///////////////////////////////////////////////////////////////////////////
192 BOOL SvcListHasLanguage(
193 const LangSvcEntries_Spell &rEntry,
194 LanguageType nLanguage )
196 BOOL bHasLanguage = FALSE;
197 Locale aTmpLocale;
199 const Reference< XSpellChecker > *pRef = rEntry.aSvcRefs .getConstArray();
200 sal_Int32 nLen = rEntry.aSvcRefs.getLength();
201 for (INT32 k = 0; k < nLen && !bHasLanguage; ++k)
203 if (pRef[k].is())
205 if (0 == aTmpLocale.Language.getLength())
206 aTmpLocale = CreateLocale( nLanguage );
207 bHasLanguage = pRef[k]->hasLocale( aTmpLocale );
211 return bHasLanguage;
214 ///////////////////////////////////////////////////////////////////////////
217 SpellCheckerDispatcher::SpellCheckerDispatcher( LngSvcMgr &rLngSvcMgr ) :
218 rMgr (rLngSvcMgr)
220 pCache = NULL;
224 SpellCheckerDispatcher::~SpellCheckerDispatcher()
226 ClearSvcList();
227 delete pCache;
231 void SpellCheckerDispatcher::ClearSvcList()
233 // release memory for each table entry
234 SpellSvcByLangMap_t aTmp;
235 aSvcMap.swap( aTmp );
239 Sequence< Locale > SAL_CALL SpellCheckerDispatcher::getLocales()
240 throw(RuntimeException)
242 MutexGuard aGuard( GetLinguMutex() );
244 Sequence< Locale > aLocales( static_cast< sal_Int32 >(aSvcMap.size()) );
245 Locale *pLocales = aLocales.getArray();
246 SpellSvcByLangMap_t::const_iterator aIt;
247 for (aIt = aSvcMap.begin(); aIt != aSvcMap.end(); ++aIt)
249 *pLocales++ = CreateLocale( aIt->first );
251 return aLocales;
255 sal_Bool SAL_CALL SpellCheckerDispatcher::hasLocale( const Locale& rLocale )
256 throw(RuntimeException)
258 MutexGuard aGuard( GetLinguMutex() );
259 SpellSvcByLangMap_t::const_iterator aIt( aSvcMap.find( LocaleToLanguage( rLocale ) ) );
260 return aIt != aSvcMap.end();
264 sal_Bool SAL_CALL
265 SpellCheckerDispatcher::isValid( const OUString& rWord, const Locale& rLocale,
266 const PropertyValues& rProperties )
267 throw(IllegalArgumentException, RuntimeException)
269 MutexGuard aGuard( GetLinguMutex() );
270 return isValid_Impl( rWord, LocaleToLanguage( rLocale ), rProperties, TRUE );
274 Reference< XSpellAlternatives > SAL_CALL
275 SpellCheckerDispatcher::spell( const OUString& rWord, const Locale& rLocale,
276 const PropertyValues& rProperties )
277 throw(IllegalArgumentException, RuntimeException)
279 MutexGuard aGuard( GetLinguMutex() );
280 return spell_Impl( rWord, LocaleToLanguage( rLocale ), rProperties, TRUE );
284 // returns the overall result of cross-checking with all user-dictionaries
285 // including the IgnoreAll list
286 static Reference< XDictionaryEntry > lcl_GetRulingDictionaryEntry(
287 const OUString &rWord,
288 LanguageType nLanguage )
290 Reference< XDictionaryEntry > xRes;
292 // the order of winning from top to bottom is:
293 // 1) IgnoreAll list will always win
294 // 2) Negative dictionaries will win over positive dictionaries
295 Reference< XDictionary > xIgnoreAll( GetIgnoreAllList() );
296 if (xIgnoreAll.is())
297 xRes = xIgnoreAll->getEntry( rWord );
298 if (!xRes.is())
300 Reference< XDictionaryList > xDList( GetDictionaryList() );
301 Reference< XDictionaryEntry > xNegEntry( SearchDicList( xDList,
302 rWord, nLanguage, FALSE, TRUE ) );
303 if (xNegEntry.is())
304 xRes = xNegEntry;
305 else
307 Reference< XDictionaryEntry > xPosEntry( SearchDicList( xDList,
308 rWord, nLanguage, TRUE, TRUE ) );
309 if (xPosEntry.is())
310 xRes = xPosEntry;
314 return xRes;
318 BOOL SpellCheckerDispatcher::isValid_Impl(
319 const OUString& rWord,
320 LanguageType nLanguage,
321 const PropertyValues& rProperties,
322 BOOL bCheckDics)
323 throw( RuntimeException, IllegalArgumentException )
325 MutexGuard aGuard( GetLinguMutex() );
327 BOOL bRes = TRUE;
329 if (nLanguage == LANGUAGE_NONE || !rWord.getLength())
330 return bRes;
332 // search for entry with that language
333 LangSvcEntries_Spell *pEntry = aSvcMap[ nLanguage ].get();
335 if (!pEntry)
337 #ifdef LINGU_EXCEPTIONS
338 throw IllegalArgumentException();
339 #endif
341 else
343 OUString aChkWord( rWord );
344 Locale aLocale( CreateLocale( nLanguage ) );
346 // replace typographical apostroph by ascii apostroph
347 String aSingleQuote( GetLocaleDataWrapper( nLanguage ).getQuotationMarkEnd() );
348 DBG_ASSERT( 1 == aSingleQuote.Len(), "unexpectend length of quotation mark" );
349 if (aSingleQuote.Len())
350 aChkWord = aChkWord.replace( aSingleQuote.GetChar(0), '\'' );
352 RemoveHyphens( aChkWord );
353 if (IsIgnoreControlChars( rProperties, GetPropSet() ))
354 RemoveControlChars( aChkWord );
356 INT32 nLen = pEntry->aSvcRefs.getLength();
357 DBG_ASSERT( nLen == pEntry->aSvcImplNames.getLength(),
358 "lng : sequence length mismatch");
359 DBG_ASSERT( pEntry->nLastTriedSvcIndex < nLen,
360 "lng : index out of range");
362 INT32 i = 0;
363 BOOL bTmpRes = TRUE;
364 BOOL bTmpResValid = FALSE;
366 // try already instantiated services first
368 const Reference< XSpellChecker > *pRef =
369 pEntry->aSvcRefs.getConstArray();
370 while (i <= pEntry->nLastTriedSvcIndex
371 && (!bTmpResValid || FALSE == bTmpRes))
373 bTmpResValid = TRUE;
374 if (pRef[i].is() && pRef[i]->hasLocale( aLocale ))
376 bTmpRes = GetCache().CheckWord( aChkWord, nLanguage );
377 if (!bTmpRes)
379 bTmpRes = pRef[i]->isValid( aChkWord, aLocale, rProperties );
381 // Add correct words to the cache.
382 // But not those that are correct only because of
383 // the temporary supplied settings.
384 if (bTmpRes && 0 == rProperties.getLength())
385 GetCache().AddWord( aChkWord, nLanguage );
388 else
389 bTmpResValid = FALSE;
391 if (bTmpResValid)
392 bRes = bTmpRes;
394 ++i;
398 // if still no result instantiate new services and try those
399 if ((!bTmpResValid || FALSE == bTmpRes)
400 && pEntry->nLastTriedSvcIndex < nLen - 1)
402 const OUString *pImplNames = pEntry->aSvcImplNames.getConstArray();
403 Reference< XSpellChecker > *pRef = pEntry->aSvcRefs .getArray();
405 Reference< XMultiServiceFactory > xMgr( getProcessServiceFactory() );
406 if (xMgr.is())
408 // build service initialization argument
409 Sequence< Any > aArgs(2);
410 aArgs.getArray()[0] <<= GetPropSet();
411 //! The dispatcher searches the dictionary-list
412 //! thus the service needs not to now about it
413 //aArgs.getArray()[1] <<= GetDicList();
415 while (i < nLen && (!bTmpResValid || FALSE == bTmpRes))
417 // create specific service via it's implementation name
418 Reference< XSpellChecker > xSpell;
421 xSpell = Reference< XSpellChecker >(
422 xMgr->createInstanceWithArguments(
423 pImplNames[i], aArgs ), UNO_QUERY );
425 catch (uno::Exception &)
427 DBG_ASSERT( 0, "createInstanceWithArguments failed" );
429 pRef [i] = xSpell;
431 Reference< XLinguServiceEventBroadcaster >
432 xBroadcaster( xSpell, UNO_QUERY );
433 if (xBroadcaster.is())
434 rMgr.AddLngSvcEvtBroadcaster( xBroadcaster );
436 bTmpResValid = TRUE;
437 if (xSpell.is() && xSpell->hasLocale( aLocale ))
439 bTmpRes = GetCache().CheckWord( aChkWord, nLanguage );
440 if (!bTmpRes)
442 bTmpRes = xSpell->isValid( aChkWord, aLocale, rProperties );
444 // Add correct words to the cache.
445 // But not those that are correct only because of
446 // the temporary supplied settings.
447 if (bTmpRes && 0 == rProperties.getLength())
448 GetCache().AddWord( aChkWord, nLanguage );
451 else
452 bTmpResValid = FALSE;
454 if (bTmpResValid)
455 bRes = bTmpRes;
457 pEntry->nLastTriedSvcIndex = (INT16) i;
458 ++i;
461 // if language is not supported by any of the services
462 // remove it from the list.
463 if (i == nLen)
465 if (!SvcListHasLanguage( *pEntry, nLanguage ))
466 aSvcMap.erase( nLanguage );
471 // cross-check against results from dictionaries which have precedence!
472 if (bCheckDics &&
473 GetDicList().is() && IsUseDicList( rProperties, GetPropSet() ))
475 Reference< XDictionaryEntry > xTmp( lcl_GetRulingDictionaryEntry( aChkWord, nLanguage ) );
476 if (xTmp.is())
477 bRes = !xTmp->isNegative();
481 return bRes;
485 Reference< XSpellAlternatives > SpellCheckerDispatcher::spell_Impl(
486 const OUString& rWord,
487 LanguageType nLanguage,
488 const PropertyValues& rProperties,
489 BOOL bCheckDics )
490 throw(IllegalArgumentException, RuntimeException)
492 MutexGuard aGuard( GetLinguMutex() );
494 Reference< XSpellAlternatives > xRes;
496 if (nLanguage == LANGUAGE_NONE || !rWord.getLength())
497 return xRes;
499 // search for entry with that language
500 LangSvcEntries_Spell *pEntry = aSvcMap[ nLanguage ].get();
502 if (!pEntry)
504 #ifdef LINGU_EXCEPTIONS
505 throw IllegalArgumentException();
506 #endif
508 else
510 OUString aChkWord( rWord );
511 Locale aLocale( CreateLocale( nLanguage ) );
513 // replace typographical apostroph by ascii apostroph
514 String aSingleQuote( GetLocaleDataWrapper( nLanguage ).getQuotationMarkEnd() );
515 DBG_ASSERT( 1 == aSingleQuote.Len(), "unexpectend length of quotation mark" );
516 if (aSingleQuote.Len())
517 aChkWord = aChkWord.replace( aSingleQuote.GetChar(0), '\'' );
519 RemoveHyphens( aChkWord );
520 if (IsIgnoreControlChars( rProperties, GetPropSet() ))
521 RemoveControlChars( aChkWord );
523 INT32 nLen = pEntry->aSvcRefs.getLength();
524 DBG_ASSERT( nLen == pEntry->aSvcImplNames.getLength(),
525 "lng : sequence length mismatch");
526 DBG_ASSERT( pEntry->nLastTriedSvcIndex < nLen,
527 "lng : index out of range");
529 INT32 i = 0;
530 Reference< XSpellAlternatives > xTmpRes;
531 BOOL bTmpResValid = FALSE;
533 // try already instantiated services first
535 const Reference< XSpellChecker > *pRef = pEntry->aSvcRefs.getConstArray();
536 sal_Int32 nNumSugestions = -1;
537 while (i <= pEntry->nLastTriedSvcIndex
538 && (!bTmpResValid || xTmpRes.is()) )
540 bTmpResValid = TRUE;
541 if (pRef[i].is() && pRef[i]->hasLocale( aLocale ))
543 BOOL bOK = GetCache().CheckWord( aChkWord, nLanguage );
544 if (bOK)
545 xTmpRes = NULL;
546 else
548 xTmpRes = pRef[i]->spell( aChkWord, aLocale, rProperties );
550 // Add correct words to the cache.
551 // But not those that are correct only because of
552 // the temporary supplied settings.
553 if (!xTmpRes.is() && 0 == rProperties.getLength())
554 GetCache().AddWord( aChkWord, nLanguage );
557 else
558 bTmpResValid = FALSE;
560 // return first found result if the word is not known by any checker.
561 // But if that result has no suggestions use the first one that does
562 // provide suggestions for the misspelled word.
563 if (!xRes.is() && bTmpResValid)
565 xRes = xTmpRes;
566 nNumSugestions = 0;
567 if (xRes.is())
568 nNumSugestions = xRes->getAlternatives().getLength();
570 sal_Int32 nTmpNumSugestions = 0;
571 if (xTmpRes.is() && bTmpResValid)
572 nTmpNumSugestions = xTmpRes->getAlternatives().getLength();
573 if (xRes.is() && nNumSugestions == 0 && nTmpNumSugestions > 0)
575 xRes = xTmpRes;
576 nNumSugestions = nTmpNumSugestions;
579 ++i;
583 // if still no result instantiate new services and try those
584 if ((!bTmpResValid || xTmpRes.is())
585 && pEntry->nLastTriedSvcIndex < nLen - 1)
587 const OUString *pImplNames = pEntry->aSvcImplNames.getConstArray();
588 Reference< XSpellChecker > *pRef = pEntry->aSvcRefs .getArray();
590 Reference< XMultiServiceFactory > xMgr( getProcessServiceFactory() );
591 if (xMgr.is())
593 // build service initialization argument
594 Sequence< Any > aArgs(2);
595 aArgs.getArray()[0] <<= GetPropSet();
596 //! The dispatcher searches the dictionary-list
597 //! thus the service needs not to now about it
598 //aArgs.getArray()[1] <<= GetDicList();
600 sal_Int32 nNumSugestions = -1;
601 while (i < nLen && (!bTmpResValid || xTmpRes.is()))
603 // create specific service via it's implementation name
604 Reference< XSpellChecker > xSpell;
607 xSpell = Reference< XSpellChecker >(
608 xMgr->createInstanceWithArguments(
609 pImplNames[i], aArgs ), UNO_QUERY );
611 catch (uno::Exception &)
613 DBG_ASSERT( 0, "createInstanceWithArguments failed" );
615 pRef [i] = xSpell;
617 Reference< XLinguServiceEventBroadcaster >
618 xBroadcaster( xSpell, UNO_QUERY );
619 if (xBroadcaster.is())
620 rMgr.AddLngSvcEvtBroadcaster( xBroadcaster );
622 bTmpResValid = TRUE;
623 if (xSpell.is() && xSpell->hasLocale( aLocale ))
625 BOOL bOK = GetCache().CheckWord( aChkWord, nLanguage );
626 if (bOK)
627 xTmpRes = NULL;
628 else
630 xTmpRes = xSpell->spell( aChkWord, aLocale, rProperties );
632 // Add correct words to the cache.
633 // But not those that are correct only because of
634 // the temporary supplied settings.
635 if (!xTmpRes.is() && 0 == rProperties.getLength())
636 GetCache().AddWord( aChkWord, nLanguage );
639 else
640 bTmpResValid = FALSE;
642 // return first found result if the word is not known by any checker.
643 // But if that result has no suggestions use the first one that does
644 // provide suggestions for the misspelled word.
645 if (!xRes.is() && bTmpResValid)
647 xRes = xTmpRes;
648 nNumSugestions = 0;
649 if (xRes.is())
650 nNumSugestions = xRes->getAlternatives().getLength();
652 sal_Int32 nTmpNumSugestions = 0;
653 if (xTmpRes.is() && bTmpResValid)
654 nTmpNumSugestions = xTmpRes->getAlternatives().getLength();
655 if (xRes.is() && nNumSugestions == 0 && nTmpNumSugestions > 0)
657 xRes = xTmpRes;
658 nNumSugestions = nTmpNumSugestions;
661 pEntry->nLastTriedSvcIndex = (INT16) i;
662 ++i;
665 // if language is not supported by any of the services
666 // remove it from the list.
667 if (i == nLen)
669 if (!SvcListHasLanguage( *pEntry, nLanguage ))
670 aSvcMap.erase( nLanguage );
675 // if word is finally found to be correct
676 // clear previously remembered alternatives
677 if (bTmpResValid && !xTmpRes.is())
678 xRes = NULL;
680 // list of proposals found (to be checked against entries of
681 // neagtive dictionaries)
682 ProposalList aProposalList;
683 // Sequence< OUString > aProposals;
684 INT16 eFailureType = -1; // no failure
685 if (xRes.is())
687 aProposalList.Append( xRes->getAlternatives() );
688 // aProposals = xRes->getAlternatives();
689 eFailureType = xRes->getFailureType();
691 Reference< XDictionaryList > xDList;
692 if (GetDicList().is() && IsUseDicList( rProperties, GetPropSet() ))
693 xDList = Reference< XDictionaryList >( GetDicList(), UNO_QUERY );
695 // cross-check against results from user-dictionaries which have precedence!
696 if (bCheckDics && xDList.is())
698 Reference< XDictionaryEntry > xTmp( lcl_GetRulingDictionaryEntry( aChkWord, nLanguage ) );
699 if (xTmp.is())
701 if (xTmp->isNegative()) // positive entry found
703 eFailureType = SpellFailure::IS_NEGATIVE_WORD;
705 // replacement text to be added to suggestions, if not empty
706 OUString aAddRplcTxt( xTmp->getReplacementText() );
708 // replacement text must not be in negative dictionary itself
709 if (aAddRplcTxt.getLength() &&
710 !SearchDicList( xDList, aAddRplcTxt, nLanguage, FALSE, TRUE ).is())
712 aProposalList.Prepend( aAddRplcTxt );
715 else // positive entry found
717 xRes = NULL;
718 eFailureType = -1; // no failure
723 if (eFailureType != -1) // word misspelled or found in negative user-dictionary
725 // search suitable user-dictionaries for suggestions that are
726 // similar to the misspelled word
727 std::vector< OUString > aDicListProps; // list of proposals from user-dictionaries
728 SearchSimilarText( aChkWord, nLanguage, xDList, aDicListProps );
729 aProposalList.Append( aDicListProps );
730 Sequence< OUString > aProposals = aProposalList.GetSequence();
732 // remove entries listed in negative dictionaries
733 // (we don't want to display suggestions that will be regarded as misspelledlater on)
734 if (bCheckDics && xDList.is())
735 SeqRemoveNegEntries( aProposals, xDList, nLanguage );
737 uno::Reference< linguistic2::XSetSpellAlternatives > xSetAlt( xRes, uno::UNO_QUERY );
738 if (xSetAlt.is())
740 xSetAlt->setAlternatives( aProposals );
741 xSetAlt->setFailureType( eFailureType );
743 else
745 if (xRes.is())
747 DBG_ASSERT( 0, "XSetSpellAlternatives not implemented!" );
749 else if (aProposals.getLength() > 0)
751 // no xRes but Proposals found from the user-dictionaries.
752 // Thus we need to create an xRes...
753 xRes = new linguistic::SpellAlternatives( rWord, nLanguage,
754 SpellFailure::IS_NEGATIVE_WORD, aProposals );
760 return xRes;
763 uno::Sequence< sal_Int16 > SAL_CALL SpellCheckerDispatcher::getLanguages( )
764 throw (uno::RuntimeException)
766 MutexGuard aGuard( GetLinguMutex() );
767 uno::Sequence< Locale > aTmp( getLocales() );
768 uno::Sequence< INT16 > aRes( LocaleSeqToLangSeq( aTmp ) );
769 return aRes;
773 sal_Bool SAL_CALL SpellCheckerDispatcher::hasLanguage(
774 sal_Int16 nLanguage )
775 throw (uno::RuntimeException)
777 MutexGuard aGuard( GetLinguMutex() );
778 Locale aLocale( CreateLocale( nLanguage ) );
779 return hasLocale( aLocale );
783 sal_Bool SAL_CALL SpellCheckerDispatcher::isValid(
784 const OUString& rWord,
785 sal_Int16 nLanguage,
786 const uno::Sequence< beans::PropertyValue >& rProperties )
787 throw (lang::IllegalArgumentException, uno::RuntimeException)
789 MutexGuard aGuard( GetLinguMutex() );
790 Locale aLocale( CreateLocale( nLanguage ) );
791 return isValid( rWord, aLocale, rProperties);
795 uno::Reference< linguistic2::XSpellAlternatives > SAL_CALL SpellCheckerDispatcher::spell(
796 const OUString& rWord,
797 sal_Int16 nLanguage,
798 const uno::Sequence< beans::PropertyValue >& rProperties )
799 throw (lang::IllegalArgumentException, uno::RuntimeException)
801 MutexGuard aGuard( GetLinguMutex() );
802 Locale aLocale( CreateLocale( nLanguage ) );
803 return spell( rWord, aLocale, rProperties);
807 void SpellCheckerDispatcher::SetServiceList( const Locale &rLocale,
808 const Sequence< OUString > &rSvcImplNames )
810 MutexGuard aGuard( GetLinguMutex() );
812 if (pCache)
813 pCache->Flush(); // new services may spell differently...
815 INT16 nLanguage = LocaleToLanguage( rLocale );
817 INT32 nLen = rSvcImplNames.getLength();
818 if (0 == nLen)
819 // remove entry
820 aSvcMap.erase( nLanguage );
821 else
823 // modify/add entry
824 LangSvcEntries_Spell *pEntry = aSvcMap[ nLanguage ].get();
825 if (pEntry)
827 pEntry->Clear();
828 pEntry->aSvcImplNames = rSvcImplNames;
829 pEntry->aSvcRefs = Sequence< Reference < XSpellChecker > > ( nLen );
831 else
833 boost::shared_ptr< LangSvcEntries_Spell > pTmpEntry( new LangSvcEntries_Spell( rSvcImplNames ) );
834 pTmpEntry->aSvcRefs = Sequence< Reference < XSpellChecker > >( nLen );
835 aSvcMap[ nLanguage ] = pTmpEntry;
841 Sequence< OUString >
842 SpellCheckerDispatcher::GetServiceList( const Locale &rLocale ) const
844 MutexGuard aGuard( GetLinguMutex() );
846 Sequence< OUString > aRes;
848 // search for entry with that language and use data from that
849 INT16 nLanguage = LocaleToLanguage( rLocale );
850 SpellCheckerDispatcher *pThis = (SpellCheckerDispatcher *) this;
851 const LangSvcEntries_Spell *pEntry = pThis->aSvcMap[ nLanguage ].get();
852 if (pEntry)
853 aRes = pEntry->aSvcImplNames;
855 return aRes;
859 LinguDispatcher::DspType SpellCheckerDispatcher::GetDspType() const
861 return DSP_SPELL;
864 void SpellCheckerDispatcher::FlushSpellCache()
866 if (pCache)
867 pCache->Flush();
870 ///////////////////////////////////////////////////////////////////////////