1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_linguistic.hxx"
26
27 #include <com/sun/star/uno/Reference.h>
28 #include <com/sun/star/linguistic2/XSearchableDictionaryList.hpp>
29 #include <com/sun/star/linguistic2/SpellFailure.hpp>
30 #include <com/sun/star/registry/XRegistryKey.hpp>
31
32 #include <cppuhelper/factory.hxx> // helper for factories
33 #include <unotools/localedatawrapper.hxx>
34 #include <unotools/processfactory.hxx>
35 #include <tools/debug.hxx>
36 #include <svl/lngmisc.hxx>
37 #include <osl/mutex.hxx>
38
39 #include <vector>
40
41 #include "spelldsp.hxx"
42 #include "linguistic/spelldta.hxx"
43 #include "lngsvcmgr.hxx"
44 #include "linguistic/lngprops.hxx"
45
46
47 using namespace utl;
48 using namespace osl;
49 using namespace rtl;
50 using namespace com::sun::star;
51 using namespace com::sun::star::beans;
52 using namespace com::sun::star::lang;
53 using namespace com::sun::star::uno;
54 using namespace com::sun::star::linguistic2;
55 using namespace linguistic;
56
57 ///////////////////////////////////////////////////////////////////////////
58 // ProposalList: list of proposals for misspelled words
59 // The order of strings in the array should be left unchanged because the
60 // spellchecker should have put the more likely suggestions at the top.
61 // New entries will be added to the end but duplicates are to be avoided.
62 // Removing entries is done by assigning the empty string.
63 // The sequence is constructed from all non empty strings in the original
64 // while maintaining the order.
65 //
66 class ProposalList
67 {
68 std::vector< OUString > aVec;
69
70 sal_Bool HasEntry( const OUString &rText ) const;
71
72 // make copy c-tor and assignment operator private
73 ProposalList( const ProposalList & );
74 ProposalList & operator = ( const ProposalList & );
75
76 public:
ProposalList()77 ProposalList() {}
78
79 //size_t Size() const { return aVec.size(); }
80 size_t Count() const;
81 void Prepend( const OUString &rText );
82 void Append( const OUString &rNew );
83 void Append( const std::vector< OUString > &rNew );
84 void Append( const Sequence< OUString > &rNew );
85 void Remove( const OUString &rText );
86 Sequence< OUString > GetSequence() const;
87 };
88
89
HasEntry(const OUString & rText) const90 sal_Bool ProposalList::HasEntry( const OUString &rText ) const
91 {
92 sal_Bool bFound = sal_False;
93 size_t nCnt = aVec.size();
94 for (size_t i = 0; !bFound && i < nCnt; ++i)
95 {
96 if (aVec[i] == rText)
97 bFound = sal_True;
98 }
99 return bFound;
100 }
101
Prepend(const OUString & rText)102 void ProposalList::Prepend( const OUString &rText )
103 {
104 if (!HasEntry( rText ))
105 aVec.insert( aVec.begin(), rText );
106 }
107
Append(const OUString & rText)108 void ProposalList::Append( const OUString &rText )
109 {
110 if (!HasEntry( rText ))
111 aVec.push_back( rText );
112 }
113
Append(const std::vector<OUString> & rNew)114 void ProposalList::Append( const std::vector< OUString > &rNew )
115 {
116 size_t nLen = rNew.size();
117 for ( size_t i = 0; i < nLen; ++i)
118 {
119 const OUString &rText = rNew[i];
120 if (!HasEntry( rText ))
121 Append( rText );
122 }
123 }
124
Append(const Sequence<OUString> & rNew)125 void ProposalList::Append( const Sequence< OUString > &rNew )
126 {
127 sal_Int32 nLen = rNew.getLength();
128 const OUString *pNew = rNew.getConstArray();
129 for (sal_Int32 i = 0; i < nLen; ++i)
130 {
131 const OUString &rText = pNew[i];
132 if (!HasEntry( rText ))
133 Append( rText );
134 }
135 }
136
Count() const137 size_t ProposalList::Count() const
138 {
139 // returns the number of non-empty strings in the vector
140
141 size_t nRes = 0;
142 size_t nLen = aVec.size();
143 for (size_t i = 0; i < nLen; ++i)
144 {
145 if (aVec[i].getLength() != 0)
146 ++nRes;
147 }
148 return nRes;
149 }
150
GetSequence() const151 Sequence< OUString > ProposalList::GetSequence() const
152 {
153 sal_Int32 nCount = Count();
154 sal_Int32 nIdx = 0;
155 Sequence< OUString > aRes( nCount );
156 OUString *pRes = aRes.getArray();
157 sal_Int32 nLen = aVec.size();
158 for (sal_Int32 i = 0; i < nLen; ++i)
159 {
160 const OUString &rText = aVec[i];
161 DBG_ASSERT( nIdx < nCount, "index our of range" );
162 if (nIdx < nCount && rText.getLength() > 0)
163 pRes[ nIdx++ ] = rText;
164 }
165 return aRes;
166 }
167
Remove(const OUString & rText)168 void ProposalList::Remove( const OUString &rText )
169 {
170 size_t nLen = aVec.size();
171 for (size_t i = 0; i < nLen; ++i)
172 {
173 OUString &rEntry = aVec[i];
174 if (rEntry == rText)
175 {
176 rEntry = OUString();
177 break; // there should be only one matching entry
178 }
179 }
180 }
181
182
183 ///////////////////////////////////////////////////////////////////////////
184
SvcListHasLanguage(const LangSvcEntries_Spell & rEntry,LanguageType nLanguage)185 sal_Bool SvcListHasLanguage(
186 const LangSvcEntries_Spell &rEntry,
187 LanguageType nLanguage )
188 {
189 sal_Bool bHasLanguage = sal_False;
190 Locale aTmpLocale;
191
192 const Reference< XSpellChecker > *pRef = rEntry.aSvcRefs .getConstArray();
193 sal_Int32 nLen = rEntry.aSvcRefs.getLength();
194 for (sal_Int32 k = 0; k < nLen && !bHasLanguage; ++k)
195 {
196 if (pRef[k].is())
197 {
198 if (0 == aTmpLocale.Language.getLength())
199 aTmpLocale = CreateLocale( nLanguage );
200 bHasLanguage = pRef[k]->hasLocale( aTmpLocale );
201 }
202 }
203
204 return bHasLanguage;
205 }
206
207 ///////////////////////////////////////////////////////////////////////////
208
209
SpellCheckerDispatcher(LngSvcMgr & rLngSvcMgr)210 SpellCheckerDispatcher::SpellCheckerDispatcher( LngSvcMgr &rLngSvcMgr ) :
211 rMgr (rLngSvcMgr)
212 {
213 pCache = NULL;
214 }
215
216
~SpellCheckerDispatcher()217 SpellCheckerDispatcher::~SpellCheckerDispatcher()
218 {
219 ClearSvcList();
220 delete pCache;
221 }
222
223
ClearSvcList()224 void SpellCheckerDispatcher::ClearSvcList()
225 {
226 // release memory for each table entry
227 SpellSvcByLangMap_t aTmp;
228 aSvcMap.swap( aTmp );
229 }
230
231
getLocales()232 Sequence< Locale > SAL_CALL SpellCheckerDispatcher::getLocales()
233 {
234 MutexGuard aGuard( GetLinguMutex() );
235
236 Sequence< Locale > aLocales( static_cast< sal_Int32 >(aSvcMap.size()) );
237 Locale *pLocales = aLocales.getArray();
238 SpellSvcByLangMap_t::const_iterator aIt;
239 for (aIt = aSvcMap.begin(); aIt != aSvcMap.end(); ++aIt)
240 {
241 *pLocales++ = CreateLocale( aIt->first );
242 }
243 return aLocales;
244 }
245
246
hasLocale(const Locale & rLocale)247 sal_Bool SAL_CALL SpellCheckerDispatcher::hasLocale( const Locale& rLocale )
248 {
249 MutexGuard aGuard( GetLinguMutex() );
250 SpellSvcByLangMap_t::const_iterator aIt( aSvcMap.find( LocaleToLanguage( rLocale ) ) );
251 return aIt != aSvcMap.end();
252 }
253
254
255 sal_Bool SAL_CALL
isValid(const OUString & rWord,const Locale & rLocale,const PropertyValues & rProperties)256 SpellCheckerDispatcher::isValid( const OUString& rWord, const Locale& rLocale,
257 const PropertyValues& rProperties )
258 {
259 MutexGuard aGuard( GetLinguMutex() );
260 return isValid_Impl( rWord, LocaleToLanguage( rLocale ), rProperties, sal_True );
261 }
262
263
264 Reference< XSpellAlternatives > SAL_CALL
spell(const OUString & rWord,const Locale & rLocale,const PropertyValues & rProperties)265 SpellCheckerDispatcher::spell( const OUString& rWord, const Locale& rLocale,
266 const PropertyValues& rProperties )
267 {
268 MutexGuard aGuard( GetLinguMutex() );
269 return spell_Impl( rWord, LocaleToLanguage( rLocale ), rProperties, sal_True );
270 }
271
272
273 // returns the overall result of cross-checking with all user-dictionaries
274 // including the IgnoreAll list
lcl_GetRulingDictionaryEntry(const OUString & rWord,LanguageType nLanguage)275 static Reference< XDictionaryEntry > lcl_GetRulingDictionaryEntry(
276 const OUString &rWord,
277 LanguageType nLanguage )
278 {
279 Reference< XDictionaryEntry > xRes;
280
281 // the order of winning from top to bottom is:
282 // 1) IgnoreAll list will always win
283 // 2) Negative dictionaries will win over positive dictionaries
284 Reference< XDictionary > xIgnoreAll( GetIgnoreAllList() );
285 if (xIgnoreAll.is())
286 xRes = xIgnoreAll->getEntry( rWord );
287 if (!xRes.is())
288 {
289 Reference< XDictionaryList > xDList( GetDictionaryList() );
290 Reference< XDictionaryEntry > xNegEntry( SearchDicList( xDList,
291 rWord, nLanguage, sal_False, sal_True ) );
292 if (xNegEntry.is())
293 xRes = xNegEntry;
294 else
295 {
296 Reference< XDictionaryEntry > xPosEntry( SearchDicList( xDList,
297 rWord, nLanguage, sal_True, sal_True ) );
298 if (xPosEntry.is())
299 xRes = xPosEntry;
300 }
301 }
302
303 return xRes;
304 }
305
306
isValid_Impl(const OUString & rWord,LanguageType nLanguage,const PropertyValues & rProperties,sal_Bool bCheckDics)307 sal_Bool SpellCheckerDispatcher::isValid_Impl(
308 const OUString& rWord,
309 LanguageType nLanguage,
310 const PropertyValues& rProperties,
311 sal_Bool bCheckDics)
312 {
313 MutexGuard aGuard( GetLinguMutex() );
314
315 sal_Bool bRes = sal_True;
316
317 if (nLanguage == LANGUAGE_NONE || !rWord.getLength())
318 return bRes;
319
320 // search for entry with that language
321 SpellSvcByLangMap_t::iterator aIt( aSvcMap.find( nLanguage ) );
322 LangSvcEntries_Spell *pEntry = aIt != aSvcMap.end() ? aIt->second.get() : NULL;
323
324 if (!pEntry)
325 {
326 #ifdef LINGU_EXCEPTIONS
327 throw IllegalArgumentException();
328 #endif
329 }
330 else
331 {
332 OUString aChkWord( rWord );
333 Locale aLocale( CreateLocale( nLanguage ) );
334
335 // replace typographical apostrophe by ascii apostrophe
336 String aSingleQuote( GetLocaleDataWrapper( nLanguage ).getQuotationMarkEnd() );
337 DBG_ASSERT( 1 == aSingleQuote.Len(), "unexpected length of quotation mark" );
338 if (aSingleQuote.Len())
339 aChkWord = aChkWord.replace( aSingleQuote.GetChar(0), '\'' );
340
341 RemoveHyphens( aChkWord );
342 if (IsIgnoreControlChars( rProperties, GetPropSet() ))
343 RemoveControlChars( aChkWord );
344
345 sal_Int32 nLen = pEntry->aSvcRefs.getLength();
346 DBG_ASSERT( nLen == pEntry->aSvcImplNames.getLength(),
347 "lng : sequence length mismatch");
348 DBG_ASSERT( pEntry->nLastTriedSvcIndex < nLen,
349 "lng : index out of range");
350
351 sal_Int32 i = 0;
352 sal_Bool bTmpRes = sal_True;
353 sal_Bool bTmpResValid = sal_False;
354
355 // try already instantiated services first
356 {
357 const Reference< XSpellChecker > *pRef =
358 pEntry->aSvcRefs.getConstArray();
359 while (i <= pEntry->nLastTriedSvcIndex
360 && (!bTmpResValid || sal_False == bTmpRes))
361 {
362 bTmpResValid = sal_True;
363 if (pRef[i].is() && pRef[i]->hasLocale( aLocale ))
364 {
365 bTmpRes = GetCache().CheckWord( aChkWord, nLanguage );
366 if (!bTmpRes)
367 {
368 bTmpRes = pRef[i]->isValid( aChkWord, aLocale, rProperties );
369
370 // Add correct words to the cache.
371 // But not those that are correct only because of
372 // the temporary supplied settings.
373 if (bTmpRes && 0 == rProperties.getLength())
374 GetCache().AddWord( aChkWord, nLanguage );
375 }
376 }
377 else
378 bTmpResValid = sal_False;
379
380 if (bTmpResValid)
381 bRes = bTmpRes;
382
383 ++i;
384 }
385 }
386
387 // if still no result instantiate new services and try those
388 if ((!bTmpResValid || sal_False == bTmpRes)
389 && pEntry->nLastTriedSvcIndex < nLen - 1)
390 {
391 const OUString *pImplNames = pEntry->aSvcImplNames.getConstArray();
392 Reference< XSpellChecker > *pRef = pEntry->aSvcRefs .getArray();
393
394 Reference< XMultiServiceFactory > xMgr( getProcessServiceFactory() );
395 if (xMgr.is())
396 {
397 // build service initialization argument
398 Sequence< Any > aArgs(2);
399 aArgs.getArray()[0] <<= GetPropSet();
400 //! The dispatcher searches the dictionary-list
401 //! thus the service needs not to now about it
402 //aArgs.getArray()[1] <<= GetDicList();
403
404 while (i < nLen && (!bTmpResValid || sal_False == bTmpRes))
405 {
406 // create specific service via it's implementation name
407 Reference< XSpellChecker > xSpell;
408 try
409 {
410 xSpell = Reference< XSpellChecker >(
411 xMgr->createInstanceWithArguments(
412 pImplNames[i], aArgs ), UNO_QUERY );
413 }
414 catch (uno::Exception &)
415 {
416 DBG_ASSERT( 0, "createInstanceWithArguments failed" );
417 }
418 pRef [i] = xSpell;
419
420 Reference< XLinguServiceEventBroadcaster >
421 xBroadcaster( xSpell, UNO_QUERY );
422 if (xBroadcaster.is())
423 rMgr.AddLngSvcEvtBroadcaster( xBroadcaster );
424
425 bTmpResValid = sal_True;
426 if (xSpell.is() && xSpell->hasLocale( aLocale ))
427 {
428 bTmpRes = GetCache().CheckWord( aChkWord, nLanguage );
429 if (!bTmpRes)
430 {
431 bTmpRes = xSpell->isValid( aChkWord, aLocale, rProperties );
432
433 // Add correct words to the cache.
434 // But not those that are correct only because of
435 // the temporary supplied settings.
436 if (bTmpRes && 0 == rProperties.getLength())
437 GetCache().AddWord( aChkWord, nLanguage );
438 }
439 }
440 else
441 bTmpResValid = sal_False;
442
443 if (bTmpResValid)
444 bRes = bTmpRes;
445
446 pEntry->nLastTriedSvcIndex = (sal_Int16) i;
447 ++i;
448 }
449
450 // if language is not supported by any of the services
451 // remove it from the list.
452 if (i == nLen)
453 {
454 if (!SvcListHasLanguage( *pEntry, nLanguage ))
455 aSvcMap.erase( nLanguage );
456 }
457 }
458 }
459
460 // cross-check against results from dictionaries which have precedence!
461 if (bCheckDics &&
462 GetDicList().is() && IsUseDicList( rProperties, GetPropSet() ))
463 {
464 Reference< XDictionaryEntry > xTmp( lcl_GetRulingDictionaryEntry( aChkWord, nLanguage ) );
465 if (xTmp.is())
466 bRes = !xTmp->isNegative();
467 }
468 }
469
470 return bRes;
471 }
472
473
spell_Impl(const OUString & rWord,LanguageType nLanguage,const PropertyValues & rProperties,sal_Bool bCheckDics)474 Reference< XSpellAlternatives > SpellCheckerDispatcher::spell_Impl(
475 const OUString& rWord,
476 LanguageType nLanguage,
477 const PropertyValues& rProperties,
478 sal_Bool bCheckDics )
479 {
480 MutexGuard aGuard( GetLinguMutex() );
481
482 Reference< XSpellAlternatives > xRes;
483
484 if (nLanguage == LANGUAGE_NONE || !rWord.getLength())
485 return xRes;
486
487 // search for entry with that language
488 SpellSvcByLangMap_t::iterator aIt( aSvcMap.find( nLanguage ) );
489 LangSvcEntries_Spell *pEntry = aIt != aSvcMap.end() ? aIt->second.get() : NULL;
490
491 if (!pEntry)
492 {
493 #ifdef LINGU_EXCEPTIONS
494 throw IllegalArgumentException();
495 #endif
496 }
497 else
498 {
499 OUString aChkWord( rWord );
500 Locale aLocale( CreateLocale( nLanguage ) );
501
502 // replace typographical apostrophe by ascii apostrophe
503 String aSingleQuote( GetLocaleDataWrapper( nLanguage ).getQuotationMarkEnd() );
504 DBG_ASSERT( 1 == aSingleQuote.Len(), "unexpected length of quotation mark" );
505 if (aSingleQuote.Len())
506 aChkWord = aChkWord.replace( aSingleQuote.GetChar(0), '\'' );
507
508 RemoveHyphens( aChkWord );
509 if (IsIgnoreControlChars( rProperties, GetPropSet() ))
510 RemoveControlChars( aChkWord );
511
512 sal_Int32 nLen = pEntry->aSvcRefs.getLength();
513 DBG_ASSERT( nLen == pEntry->aSvcImplNames.getLength(),
514 "lng : sequence length mismatch");
515 DBG_ASSERT( pEntry->nLastTriedSvcIndex < nLen,
516 "lng : index out of range");
517
518 sal_Int32 i = 0;
519 Reference< XSpellAlternatives > xTmpRes;
520 sal_Bool bTmpResValid = sal_False;
521
522 // try already instantiated services first
523 {
524 const Reference< XSpellChecker > *pRef = pEntry->aSvcRefs.getConstArray();
525 sal_Int32 nNumSugestions = -1;
526 while (i <= pEntry->nLastTriedSvcIndex
527 && (!bTmpResValid || xTmpRes.is()) )
528 {
529 bTmpResValid = sal_True;
530 if (pRef[i].is() && pRef[i]->hasLocale( aLocale ))
531 {
532 sal_Bool bOK = GetCache().CheckWord( aChkWord, nLanguage );
533 if (bOK)
534 xTmpRes = NULL;
535 else
536 {
537 xTmpRes = pRef[i]->spell( aChkWord, aLocale, rProperties );
538
539 // Add correct words to the cache.
540 // But not those that are correct only because of
541 // the temporary supplied settings.
542 if (!xTmpRes.is() && 0 == rProperties.getLength())
543 GetCache().AddWord( aChkWord, nLanguage );
544 }
545 }
546 else
547 bTmpResValid = sal_False;
548
549 // return first found result if the word is not known by any checker.
550 // But if that result has no suggestions use the first one that does
551 // provide suggestions for the misspelled word.
552 if (!xRes.is() && bTmpResValid)
553 {
554 xRes = xTmpRes;
555 nNumSugestions = 0;
556 if (xRes.is())
557 nNumSugestions = xRes->getAlternatives().getLength();
558 }
559 sal_Int32 nTmpNumSugestions = 0;
560 if (xTmpRes.is() && bTmpResValid)
561 nTmpNumSugestions = xTmpRes->getAlternatives().getLength();
562 if (xRes.is() && nNumSugestions == 0 && nTmpNumSugestions > 0)
563 {
564 xRes = xTmpRes;
565 nNumSugestions = nTmpNumSugestions;
566 }
567
568 ++i;
569 }
570 }
571
572 // if still no result instantiate new services and try those
573 if ((!bTmpResValid || xTmpRes.is())
574 && pEntry->nLastTriedSvcIndex < nLen - 1)
575 {
576 const OUString *pImplNames = pEntry->aSvcImplNames.getConstArray();
577 Reference< XSpellChecker > *pRef = pEntry->aSvcRefs .getArray();
578
579 Reference< XMultiServiceFactory > xMgr( getProcessServiceFactory() );
580 if (xMgr.is())
581 {
582 // build service initialization argument
583 Sequence< Any > aArgs(2);
584 aArgs.getArray()[0] <<= GetPropSet();
585 //! The dispatcher searches the dictionary-list
586 //! thus the service needs not to now about it
587 //aArgs.getArray()[1] <<= GetDicList();
588
589 sal_Int32 nNumSugestions = -1;
590 while (i < nLen && (!bTmpResValid || xTmpRes.is()))
591 {
592 // create specific service via it's implementation name
593 Reference< XSpellChecker > xSpell;
594 try
595 {
596 xSpell = Reference< XSpellChecker >(
597 xMgr->createInstanceWithArguments(
598 pImplNames[i], aArgs ), UNO_QUERY );
599 }
600 catch (uno::Exception &)
601 {
602 DBG_ASSERT( 0, "createInstanceWithArguments failed" );
603 }
604 pRef [i] = xSpell;
605
606 Reference< XLinguServiceEventBroadcaster >
607 xBroadcaster( xSpell, UNO_QUERY );
608 if (xBroadcaster.is())
609 rMgr.AddLngSvcEvtBroadcaster( xBroadcaster );
610
611 bTmpResValid = sal_True;
612 if (xSpell.is() && xSpell->hasLocale( aLocale ))
613 {
614 sal_Bool bOK = GetCache().CheckWord( aChkWord, nLanguage );
615 if (bOK)
616 xTmpRes = NULL;
617 else
618 {
619 xTmpRes = xSpell->spell( aChkWord, aLocale, rProperties );
620
621 // Add correct words to the cache.
622 // But not those that are correct only because of
623 // the temporary supplied settings.
624 if (!xTmpRes.is() && 0 == rProperties.getLength())
625 GetCache().AddWord( aChkWord, nLanguage );
626 }
627 }
628 else
629 bTmpResValid = sal_False;
630
631 // return first found result if the word is not known by any checker.
632 // But if that result has no suggestions use the first one that does
633 // provide suggestions for the misspelled word.
634 if (!xRes.is() && bTmpResValid)
635 {
636 xRes = xTmpRes;
637 nNumSugestions = 0;
638 if (xRes.is())
639 nNumSugestions = xRes->getAlternatives().getLength();
640 }
641 sal_Int32 nTmpNumSugestions = 0;
642 if (xTmpRes.is() && bTmpResValid)
643 nTmpNumSugestions = xTmpRes->getAlternatives().getLength();
644 if (xRes.is() && nNumSugestions == 0 && nTmpNumSugestions > 0)
645 {
646 xRes = xTmpRes;
647 nNumSugestions = nTmpNumSugestions;
648 }
649
650 pEntry->nLastTriedSvcIndex = (sal_Int16) i;
651 ++i;
652 }
653
654 // if language is not supported by any of the services
655 // remove it from the list.
656 if (i == nLen)
657 {
658 if (!SvcListHasLanguage( *pEntry, nLanguage ))
659 aSvcMap.erase( nLanguage );
660 }
661 }
662 }
663
664 // if word is finally found to be correct
665 // clear previously remembered alternatives
666 if (bTmpResValid && !xTmpRes.is())
667 xRes = NULL;
668
669 // list of proposals found (to be checked against entries of
670 // negative dictionaries)
671 ProposalList aProposalList;
672 // Sequence< OUString > aProposals;
673 sal_Int16 eFailureType = -1; // no failure
674 if (xRes.is())
675 {
676 aProposalList.Append( xRes->getAlternatives() );
677 // aProposals = xRes->getAlternatives();
678 eFailureType = xRes->getFailureType();
679 }
680 Reference< XDictionaryList > xDList;
681 if (GetDicList().is() && IsUseDicList( rProperties, GetPropSet() ))
682 xDList = Reference< XDictionaryList >( GetDicList(), UNO_QUERY );
683
684 // cross-check against results from user-dictionaries which have precedence!
685 if (bCheckDics && xDList.is())
686 {
687 Reference< XDictionaryEntry > xTmp( lcl_GetRulingDictionaryEntry( aChkWord, nLanguage ) );
688 if (xTmp.is())
689 {
690 if (xTmp->isNegative()) // positive entry found
691 {
692 eFailureType = SpellFailure::IS_NEGATIVE_WORD;
693
694 // replacement text to be added to suggestions, if not empty
695 OUString aAddRplcTxt( xTmp->getReplacementText() );
696
697 // replacement text must not be in negative dictionary itself
698 if (aAddRplcTxt.getLength() &&
699 !SearchDicList( xDList, aAddRplcTxt, nLanguage, sal_False, sal_True ).is())
700 {
701 aProposalList.Prepend( aAddRplcTxt );
702 }
703 }
704 else // positive entry found
705 {
706 xRes = NULL;
707 eFailureType = -1; // no failure
708 }
709 }
710 }
711
712 if (eFailureType != -1) // word misspelled or found in negative user-dictionary
713 {
714 // search suitable user-dictionaries for suggestions that are
715 // similar to the misspelled word
716 std::vector< OUString > aDicListProps; // list of proposals from user-dictionaries
717 SearchSimilarText( aChkWord, nLanguage, xDList, aDicListProps );
718 aProposalList.Append( aDicListProps );
719 Sequence< OUString > aProposals = aProposalList.GetSequence();
720
721 // remove entries listed in negative dictionaries
722 // (we don't want to display suggestions that will be regarded as misspelledlater on)
723 if (bCheckDics && xDList.is())
724 SeqRemoveNegEntries( aProposals, xDList, nLanguage );
725
726 uno::Reference< linguistic2::XSetSpellAlternatives > xSetAlt( xRes, uno::UNO_QUERY );
727 if (xSetAlt.is())
728 {
729 xSetAlt->setAlternatives( aProposals );
730 xSetAlt->setFailureType( eFailureType );
731 }
732 else
733 {
734 if (xRes.is())
735 {
736 DBG_ASSERT( 0, "XSetSpellAlternatives not implemented!" );
737 }
738 else if (aProposals.getLength() > 0)
739 {
740 // no xRes but Proposals found from the user-dictionaries.
741 // Thus we need to create an xRes...
742 xRes = new linguistic::SpellAlternatives( rWord, nLanguage,
743 SpellFailure::IS_NEGATIVE_WORD, aProposals );
744 }
745 }
746 }
747 }
748
749 return xRes;
750 }
751
getLanguages()752 uno::Sequence< sal_Int16 > SAL_CALL SpellCheckerDispatcher::getLanguages( )
753 {
754 MutexGuard aGuard( GetLinguMutex() );
755 uno::Sequence< Locale > aTmp( getLocales() );
756 uno::Sequence< sal_Int16 > aRes( LocaleSeqToLangSeq( aTmp ) );
757 return aRes;
758 }
759
760
hasLanguage(sal_Int16 nLanguage)761 sal_Bool SAL_CALL SpellCheckerDispatcher::hasLanguage(
762 sal_Int16 nLanguage )
763 {
764 MutexGuard aGuard( GetLinguMutex() );
765 Locale aLocale( CreateLocale( nLanguage ) );
766 return hasLocale( aLocale );
767 }
768
769
isValid(const OUString & rWord,sal_Int16 nLanguage,const uno::Sequence<beans::PropertyValue> & rProperties)770 sal_Bool SAL_CALL SpellCheckerDispatcher::isValid(
771 const OUString& rWord,
772 sal_Int16 nLanguage,
773 const uno::Sequence< beans::PropertyValue >& rProperties )
774 {
775 MutexGuard aGuard( GetLinguMutex() );
776 Locale aLocale( CreateLocale( nLanguage ) );
777 return isValid( rWord, aLocale, rProperties);
778 }
779
780
spell(const OUString & rWord,sal_Int16 nLanguage,const uno::Sequence<beans::PropertyValue> & rProperties)781 uno::Reference< linguistic2::XSpellAlternatives > SAL_CALL SpellCheckerDispatcher::spell(
782 const OUString& rWord,
783 sal_Int16 nLanguage,
784 const uno::Sequence< beans::PropertyValue >& rProperties )
785 {
786 MutexGuard aGuard( GetLinguMutex() );
787 Locale aLocale( CreateLocale( nLanguage ) );
788 return spell( rWord, aLocale, rProperties);
789 }
790
791
SetServiceList(const Locale & rLocale,const Sequence<OUString> & rSvcImplNames)792 void SpellCheckerDispatcher::SetServiceList( const Locale &rLocale,
793 const Sequence< OUString > &rSvcImplNames )
794 {
795 MutexGuard aGuard( GetLinguMutex() );
796
797 if (pCache)
798 pCache->Flush(); // new services may spell differently...
799
800 sal_Int16 nLanguage = LocaleToLanguage( rLocale );
801
802 sal_Int32 nLen = rSvcImplNames.getLength();
803 if (0 == nLen)
804 // remove entry
805 aSvcMap.erase( nLanguage );
806 else
807 {
808 // modify/add entry
809 LangSvcEntries_Spell *pEntry = aSvcMap[ nLanguage ].get();
810 if (pEntry)
811 {
812 pEntry->Clear();
813 pEntry->aSvcImplNames = rSvcImplNames;
814 pEntry->aSvcRefs = Sequence< Reference < XSpellChecker > > ( nLen );
815 }
816 else
817 {
818 boost::shared_ptr< LangSvcEntries_Spell > pTmpEntry( new LangSvcEntries_Spell( rSvcImplNames ) );
819 pTmpEntry->aSvcRefs = Sequence< Reference < XSpellChecker > >( nLen );
820 aSvcMap[ nLanguage ] = pTmpEntry;
821 }
822 }
823 }
824
825
826 Sequence< OUString >
GetServiceList(const Locale & rLocale) const827 SpellCheckerDispatcher::GetServiceList( const Locale &rLocale ) const
828 {
829 MutexGuard aGuard( GetLinguMutex() );
830
831 Sequence< OUString > aRes;
832
833 // search for entry with that language and use data from that
834 sal_Int16 nLanguage = LocaleToLanguage( rLocale );
835 SpellCheckerDispatcher *pThis = (SpellCheckerDispatcher *) this;
836 const SpellSvcByLangMap_t::iterator aIt( pThis->aSvcMap.find( nLanguage ) );
837 const LangSvcEntries_Spell *pEntry = aIt != aSvcMap.end() ? aIt->second.get() : NULL;
838 if (pEntry)
839 aRes = pEntry->aSvcImplNames;
840
841 return aRes;
842 }
843
844
GetDspType() const845 LinguDispatcher::DspType SpellCheckerDispatcher::GetDspType() const
846 {
847 return DSP_SPELL;
848 }
849
FlushSpellCache()850 void SpellCheckerDispatcher::FlushSpellCache()
851 {
852 if (pCache)
853 pCache->Flush();
854 }
855
856 ///////////////////////////////////////////////////////////////////////////
857