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 throw(RuntimeException)
234 {
235 MutexGuard aGuard( GetLinguMutex() );
236
237 Sequence< Locale > aLocales( static_cast< sal_Int32 >(aSvcMap.size()) );
238 Locale *pLocales = aLocales.getArray();
239 SpellSvcByLangMap_t::const_iterator aIt;
240 for (aIt = aSvcMap.begin(); aIt != aSvcMap.end(); ++aIt)
241 {
242 *pLocales++ = CreateLocale( aIt->first );
243 }
244 return aLocales;
245 }
246
247
hasLocale(const Locale & rLocale)248 sal_Bool SAL_CALL SpellCheckerDispatcher::hasLocale( const Locale& rLocale )
249 throw(RuntimeException)
250 {
251 MutexGuard aGuard( GetLinguMutex() );
252 SpellSvcByLangMap_t::const_iterator aIt( aSvcMap.find( LocaleToLanguage( rLocale ) ) );
253 return aIt != aSvcMap.end();
254 }
255
256
257 sal_Bool SAL_CALL
isValid(const OUString & rWord,const Locale & rLocale,const PropertyValues & rProperties)258 SpellCheckerDispatcher::isValid( const OUString& rWord, const Locale& rLocale,
259 const PropertyValues& rProperties )
260 throw(IllegalArgumentException, RuntimeException)
261 {
262 MutexGuard aGuard( GetLinguMutex() );
263 return isValid_Impl( rWord, LocaleToLanguage( rLocale ), rProperties, sal_True );
264 }
265
266
267 Reference< XSpellAlternatives > SAL_CALL
spell(const OUString & rWord,const Locale & rLocale,const PropertyValues & rProperties)268 SpellCheckerDispatcher::spell( const OUString& rWord, const Locale& rLocale,
269 const PropertyValues& rProperties )
270 throw(IllegalArgumentException, RuntimeException)
271 {
272 MutexGuard aGuard( GetLinguMutex() );
273 return spell_Impl( rWord, LocaleToLanguage( rLocale ), rProperties, sal_True );
274 }
275
276
277 // returns the overall result of cross-checking with all user-dictionaries
278 // including the IgnoreAll list
lcl_GetRulingDictionaryEntry(const OUString & rWord,LanguageType nLanguage)279 static Reference< XDictionaryEntry > lcl_GetRulingDictionaryEntry(
280 const OUString &rWord,
281 LanguageType nLanguage )
282 {
283 Reference< XDictionaryEntry > xRes;
284
285 // the order of winning from top to bottom is:
286 // 1) IgnoreAll list will always win
287 // 2) Negative dictionaries will win over positive dictionaries
288 Reference< XDictionary > xIgnoreAll( GetIgnoreAllList() );
289 if (xIgnoreAll.is())
290 xRes = xIgnoreAll->getEntry( rWord );
291 if (!xRes.is())
292 {
293 Reference< XDictionaryList > xDList( GetDictionaryList() );
294 Reference< XDictionaryEntry > xNegEntry( SearchDicList( xDList,
295 rWord, nLanguage, sal_False, sal_True ) );
296 if (xNegEntry.is())
297 xRes = xNegEntry;
298 else
299 {
300 Reference< XDictionaryEntry > xPosEntry( SearchDicList( xDList,
301 rWord, nLanguage, sal_True, sal_True ) );
302 if (xPosEntry.is())
303 xRes = xPosEntry;
304 }
305 }
306
307 return xRes;
308 }
309
310
isValid_Impl(const OUString & rWord,LanguageType nLanguage,const PropertyValues & rProperties,sal_Bool bCheckDics)311 sal_Bool SpellCheckerDispatcher::isValid_Impl(
312 const OUString& rWord,
313 LanguageType nLanguage,
314 const PropertyValues& rProperties,
315 sal_Bool bCheckDics)
316 throw( RuntimeException, IllegalArgumentException )
317 {
318 MutexGuard aGuard( GetLinguMutex() );
319
320 sal_Bool bRes = sal_True;
321
322 if (nLanguage == LANGUAGE_NONE || !rWord.getLength())
323 return bRes;
324
325 // search for entry with that language
326 SpellSvcByLangMap_t::iterator aIt( aSvcMap.find( nLanguage ) );
327 LangSvcEntries_Spell *pEntry = aIt != aSvcMap.end() ? aIt->second.get() : NULL;
328
329 if (!pEntry)
330 {
331 #ifdef LINGU_EXCEPTIONS
332 throw IllegalArgumentException();
333 #endif
334 }
335 else
336 {
337 OUString aChkWord( rWord );
338 Locale aLocale( CreateLocale( nLanguage ) );
339
340 // replace typographical apostroph by ascii apostroph
341 String aSingleQuote( GetLocaleDataWrapper( nLanguage ).getQuotationMarkEnd() );
342 DBG_ASSERT( 1 == aSingleQuote.Len(), "unexpectend length of quotation mark" );
343 if (aSingleQuote.Len())
344 aChkWord = aChkWord.replace( aSingleQuote.GetChar(0), '\'' );
345
346 RemoveHyphens( aChkWord );
347 if (IsIgnoreControlChars( rProperties, GetPropSet() ))
348 RemoveControlChars( aChkWord );
349
350 sal_Int32 nLen = pEntry->aSvcRefs.getLength();
351 DBG_ASSERT( nLen == pEntry->aSvcImplNames.getLength(),
352 "lng : sequence length mismatch");
353 DBG_ASSERT( pEntry->nLastTriedSvcIndex < nLen,
354 "lng : index out of range");
355
356 sal_Int32 i = 0;
357 sal_Bool bTmpRes = sal_True;
358 sal_Bool bTmpResValid = sal_False;
359
360 // try already instantiated services first
361 {
362 const Reference< XSpellChecker > *pRef =
363 pEntry->aSvcRefs.getConstArray();
364 while (i <= pEntry->nLastTriedSvcIndex
365 && (!bTmpResValid || sal_False == bTmpRes))
366 {
367 bTmpResValid = sal_True;
368 if (pRef[i].is() && pRef[i]->hasLocale( aLocale ))
369 {
370 bTmpRes = GetCache().CheckWord( aChkWord, nLanguage );
371 if (!bTmpRes)
372 {
373 bTmpRes = pRef[i]->isValid( aChkWord, aLocale, rProperties );
374
375 // Add correct words to the cache.
376 // But not those that are correct only because of
377 // the temporary supplied settings.
378 if (bTmpRes && 0 == rProperties.getLength())
379 GetCache().AddWord( aChkWord, nLanguage );
380 }
381 }
382 else
383 bTmpResValid = sal_False;
384
385 if (bTmpResValid)
386 bRes = bTmpRes;
387
388 ++i;
389 }
390 }
391
392 // if still no result instantiate new services and try those
393 if ((!bTmpResValid || sal_False == bTmpRes)
394 && pEntry->nLastTriedSvcIndex < nLen - 1)
395 {
396 const OUString *pImplNames = pEntry->aSvcImplNames.getConstArray();
397 Reference< XSpellChecker > *pRef = pEntry->aSvcRefs .getArray();
398
399 Reference< XMultiServiceFactory > xMgr( getProcessServiceFactory() );
400 if (xMgr.is())
401 {
402 // build service initialization argument
403 Sequence< Any > aArgs(2);
404 aArgs.getArray()[0] <<= GetPropSet();
405 //! The dispatcher searches the dictionary-list
406 //! thus the service needs not to now about it
407 //aArgs.getArray()[1] <<= GetDicList();
408
409 while (i < nLen && (!bTmpResValid || sal_False == bTmpRes))
410 {
411 // create specific service via it's implementation name
412 Reference< XSpellChecker > xSpell;
413 try
414 {
415 xSpell = Reference< XSpellChecker >(
416 xMgr->createInstanceWithArguments(
417 pImplNames[i], aArgs ), UNO_QUERY );
418 }
419 catch (uno::Exception &)
420 {
421 DBG_ASSERT( 0, "createInstanceWithArguments failed" );
422 }
423 pRef [i] = xSpell;
424
425 Reference< XLinguServiceEventBroadcaster >
426 xBroadcaster( xSpell, UNO_QUERY );
427 if (xBroadcaster.is())
428 rMgr.AddLngSvcEvtBroadcaster( xBroadcaster );
429
430 bTmpResValid = sal_True;
431 if (xSpell.is() && xSpell->hasLocale( aLocale ))
432 {
433 bTmpRes = GetCache().CheckWord( aChkWord, nLanguage );
434 if (!bTmpRes)
435 {
436 bTmpRes = xSpell->isValid( aChkWord, aLocale, rProperties );
437
438 // Add correct words to the cache.
439 // But not those that are correct only because of
440 // the temporary supplied settings.
441 if (bTmpRes && 0 == rProperties.getLength())
442 GetCache().AddWord( aChkWord, nLanguage );
443 }
444 }
445 else
446 bTmpResValid = sal_False;
447
448 if (bTmpResValid)
449 bRes = bTmpRes;
450
451 pEntry->nLastTriedSvcIndex = (sal_Int16) i;
452 ++i;
453 }
454
455 // if language is not supported by any of the services
456 // remove it from the list.
457 if (i == nLen)
458 {
459 if (!SvcListHasLanguage( *pEntry, nLanguage ))
460 aSvcMap.erase( nLanguage );
461 }
462 }
463 }
464
465 // cross-check against results from dictionaries which have precedence!
466 if (bCheckDics &&
467 GetDicList().is() && IsUseDicList( rProperties, GetPropSet() ))
468 {
469 Reference< XDictionaryEntry > xTmp( lcl_GetRulingDictionaryEntry( aChkWord, nLanguage ) );
470 if (xTmp.is())
471 bRes = !xTmp->isNegative();
472 }
473 }
474
475 return bRes;
476 }
477
478
spell_Impl(const OUString & rWord,LanguageType nLanguage,const PropertyValues & rProperties,sal_Bool bCheckDics)479 Reference< XSpellAlternatives > SpellCheckerDispatcher::spell_Impl(
480 const OUString& rWord,
481 LanguageType nLanguage,
482 const PropertyValues& rProperties,
483 sal_Bool bCheckDics )
484 throw(IllegalArgumentException, RuntimeException)
485 {
486 MutexGuard aGuard( GetLinguMutex() );
487
488 Reference< XSpellAlternatives > xRes;
489
490 if (nLanguage == LANGUAGE_NONE || !rWord.getLength())
491 return xRes;
492
493 // search for entry with that language
494 SpellSvcByLangMap_t::iterator aIt( aSvcMap.find( nLanguage ) );
495 LangSvcEntries_Spell *pEntry = aIt != aSvcMap.end() ? aIt->second.get() : NULL;
496
497 if (!pEntry)
498 {
499 #ifdef LINGU_EXCEPTIONS
500 throw IllegalArgumentException();
501 #endif
502 }
503 else
504 {
505 OUString aChkWord( rWord );
506 Locale aLocale( CreateLocale( nLanguage ) );
507
508 // replace typographical apostroph by ascii apostroph
509 String aSingleQuote( GetLocaleDataWrapper( nLanguage ).getQuotationMarkEnd() );
510 DBG_ASSERT( 1 == aSingleQuote.Len(), "unexpectend length of quotation mark" );
511 if (aSingleQuote.Len())
512 aChkWord = aChkWord.replace( aSingleQuote.GetChar(0), '\'' );
513
514 RemoveHyphens( aChkWord );
515 if (IsIgnoreControlChars( rProperties, GetPropSet() ))
516 RemoveControlChars( aChkWord );
517
518 sal_Int32 nLen = pEntry->aSvcRefs.getLength();
519 DBG_ASSERT( nLen == pEntry->aSvcImplNames.getLength(),
520 "lng : sequence length mismatch");
521 DBG_ASSERT( pEntry->nLastTriedSvcIndex < nLen,
522 "lng : index out of range");
523
524 sal_Int32 i = 0;
525 Reference< XSpellAlternatives > xTmpRes;
526 sal_Bool bTmpResValid = sal_False;
527
528 // try already instantiated services first
529 {
530 const Reference< XSpellChecker > *pRef = pEntry->aSvcRefs.getConstArray();
531 sal_Int32 nNumSugestions = -1;
532 while (i <= pEntry->nLastTriedSvcIndex
533 && (!bTmpResValid || xTmpRes.is()) )
534 {
535 bTmpResValid = sal_True;
536 if (pRef[i].is() && pRef[i]->hasLocale( aLocale ))
537 {
538 sal_Bool bOK = GetCache().CheckWord( aChkWord, nLanguage );
539 if (bOK)
540 xTmpRes = NULL;
541 else
542 {
543 xTmpRes = pRef[i]->spell( aChkWord, aLocale, rProperties );
544
545 // Add correct words to the cache.
546 // But not those that are correct only because of
547 // the temporary supplied settings.
548 if (!xTmpRes.is() && 0 == rProperties.getLength())
549 GetCache().AddWord( aChkWord, nLanguage );
550 }
551 }
552 else
553 bTmpResValid = sal_False;
554
555 // return first found result if the word is not known by any checker.
556 // But if that result has no suggestions use the first one that does
557 // provide suggestions for the misspelled word.
558 if (!xRes.is() && bTmpResValid)
559 {
560 xRes = xTmpRes;
561 nNumSugestions = 0;
562 if (xRes.is())
563 nNumSugestions = xRes->getAlternatives().getLength();
564 }
565 sal_Int32 nTmpNumSugestions = 0;
566 if (xTmpRes.is() && bTmpResValid)
567 nTmpNumSugestions = xTmpRes->getAlternatives().getLength();
568 if (xRes.is() && nNumSugestions == 0 && nTmpNumSugestions > 0)
569 {
570 xRes = xTmpRes;
571 nNumSugestions = nTmpNumSugestions;
572 }
573
574 ++i;
575 }
576 }
577
578 // if still no result instantiate new services and try those
579 if ((!bTmpResValid || xTmpRes.is())
580 && pEntry->nLastTriedSvcIndex < nLen - 1)
581 {
582 const OUString *pImplNames = pEntry->aSvcImplNames.getConstArray();
583 Reference< XSpellChecker > *pRef = pEntry->aSvcRefs .getArray();
584
585 Reference< XMultiServiceFactory > xMgr( getProcessServiceFactory() );
586 if (xMgr.is())
587 {
588 // build service initialization argument
589 Sequence< Any > aArgs(2);
590 aArgs.getArray()[0] <<= GetPropSet();
591 //! The dispatcher searches the dictionary-list
592 //! thus the service needs not to now about it
593 //aArgs.getArray()[1] <<= GetDicList();
594
595 sal_Int32 nNumSugestions = -1;
596 while (i < nLen && (!bTmpResValid || xTmpRes.is()))
597 {
598 // create specific service via it's implementation name
599 Reference< XSpellChecker > xSpell;
600 try
601 {
602 xSpell = Reference< XSpellChecker >(
603 xMgr->createInstanceWithArguments(
604 pImplNames[i], aArgs ), UNO_QUERY );
605 }
606 catch (uno::Exception &)
607 {
608 DBG_ASSERT( 0, "createInstanceWithArguments failed" );
609 }
610 pRef [i] = xSpell;
611
612 Reference< XLinguServiceEventBroadcaster >
613 xBroadcaster( xSpell, UNO_QUERY );
614 if (xBroadcaster.is())
615 rMgr.AddLngSvcEvtBroadcaster( xBroadcaster );
616
617 bTmpResValid = sal_True;
618 if (xSpell.is() && xSpell->hasLocale( aLocale ))
619 {
620 sal_Bool bOK = GetCache().CheckWord( aChkWord, nLanguage );
621 if (bOK)
622 xTmpRes = NULL;
623 else
624 {
625 xTmpRes = xSpell->spell( aChkWord, aLocale, rProperties );
626
627 // Add correct words to the cache.
628 // But not those that are correct only because of
629 // the temporary supplied settings.
630 if (!xTmpRes.is() && 0 == rProperties.getLength())
631 GetCache().AddWord( aChkWord, nLanguage );
632 }
633 }
634 else
635 bTmpResValid = sal_False;
636
637 // return first found result if the word is not known by any checker.
638 // But if that result has no suggestions use the first one that does
639 // provide suggestions for the misspelled word.
640 if (!xRes.is() && bTmpResValid)
641 {
642 xRes = xTmpRes;
643 nNumSugestions = 0;
644 if (xRes.is())
645 nNumSugestions = xRes->getAlternatives().getLength();
646 }
647 sal_Int32 nTmpNumSugestions = 0;
648 if (xTmpRes.is() && bTmpResValid)
649 nTmpNumSugestions = xTmpRes->getAlternatives().getLength();
650 if (xRes.is() && nNumSugestions == 0 && nTmpNumSugestions > 0)
651 {
652 xRes = xTmpRes;
653 nNumSugestions = nTmpNumSugestions;
654 }
655
656 pEntry->nLastTriedSvcIndex = (sal_Int16) i;
657 ++i;
658 }
659
660 // if language is not supported by any of the services
661 // remove it from the list.
662 if (i == nLen)
663 {
664 if (!SvcListHasLanguage( *pEntry, nLanguage ))
665 aSvcMap.erase( nLanguage );
666 }
667 }
668 }
669
670 // if word is finally found to be correct
671 // clear previously remembered alternatives
672 if (bTmpResValid && !xTmpRes.is())
673 xRes = NULL;
674
675 // list of proposals found (to be checked against entries of
676 // neagtive dictionaries)
677 ProposalList aProposalList;
678 // Sequence< OUString > aProposals;
679 sal_Int16 eFailureType = -1; // no failure
680 if (xRes.is())
681 {
682 aProposalList.Append( xRes->getAlternatives() );
683 // aProposals = xRes->getAlternatives();
684 eFailureType = xRes->getFailureType();
685 }
686 Reference< XDictionaryList > xDList;
687 if (GetDicList().is() && IsUseDicList( rProperties, GetPropSet() ))
688 xDList = Reference< XDictionaryList >( GetDicList(), UNO_QUERY );
689
690 // cross-check against results from user-dictionaries which have precedence!
691 if (bCheckDics && xDList.is())
692 {
693 Reference< XDictionaryEntry > xTmp( lcl_GetRulingDictionaryEntry( aChkWord, nLanguage ) );
694 if (xTmp.is())
695 {
696 if (xTmp->isNegative()) // positive entry found
697 {
698 eFailureType = SpellFailure::IS_NEGATIVE_WORD;
699
700 // replacement text to be added to suggestions, if not empty
701 OUString aAddRplcTxt( xTmp->getReplacementText() );
702
703 // replacement text must not be in negative dictionary itself
704 if (aAddRplcTxt.getLength() &&
705 !SearchDicList( xDList, aAddRplcTxt, nLanguage, sal_False, sal_True ).is())
706 {
707 aProposalList.Prepend( aAddRplcTxt );
708 }
709 }
710 else // positive entry found
711 {
712 xRes = NULL;
713 eFailureType = -1; // no failure
714 }
715 }
716 }
717
718 if (eFailureType != -1) // word misspelled or found in negative user-dictionary
719 {
720 // search suitable user-dictionaries for suggestions that are
721 // similar to the misspelled word
722 std::vector< OUString > aDicListProps; // list of proposals from user-dictionaries
723 SearchSimilarText( aChkWord, nLanguage, xDList, aDicListProps );
724 aProposalList.Append( aDicListProps );
725 Sequence< OUString > aProposals = aProposalList.GetSequence();
726
727 // remove entries listed in negative dictionaries
728 // (we don't want to display suggestions that will be regarded as misspelledlater on)
729 if (bCheckDics && xDList.is())
730 SeqRemoveNegEntries( aProposals, xDList, nLanguage );
731
732 uno::Reference< linguistic2::XSetSpellAlternatives > xSetAlt( xRes, uno::UNO_QUERY );
733 if (xSetAlt.is())
734 {
735 xSetAlt->setAlternatives( aProposals );
736 xSetAlt->setFailureType( eFailureType );
737 }
738 else
739 {
740 if (xRes.is())
741 {
742 DBG_ASSERT( 0, "XSetSpellAlternatives not implemented!" );
743 }
744 else if (aProposals.getLength() > 0)
745 {
746 // no xRes but Proposals found from the user-dictionaries.
747 // Thus we need to create an xRes...
748 xRes = new linguistic::SpellAlternatives( rWord, nLanguage,
749 SpellFailure::IS_NEGATIVE_WORD, aProposals );
750 }
751 }
752 }
753 }
754
755 return xRes;
756 }
757
getLanguages()758 uno::Sequence< sal_Int16 > SAL_CALL SpellCheckerDispatcher::getLanguages( )
759 throw (uno::RuntimeException)
760 {
761 MutexGuard aGuard( GetLinguMutex() );
762 uno::Sequence< Locale > aTmp( getLocales() );
763 uno::Sequence< sal_Int16 > aRes( LocaleSeqToLangSeq( aTmp ) );
764 return aRes;
765 }
766
767
hasLanguage(sal_Int16 nLanguage)768 sal_Bool SAL_CALL SpellCheckerDispatcher::hasLanguage(
769 sal_Int16 nLanguage )
770 throw (uno::RuntimeException)
771 {
772 MutexGuard aGuard( GetLinguMutex() );
773 Locale aLocale( CreateLocale( nLanguage ) );
774 return hasLocale( aLocale );
775 }
776
777
isValid(const OUString & rWord,sal_Int16 nLanguage,const uno::Sequence<beans::PropertyValue> & rProperties)778 sal_Bool SAL_CALL SpellCheckerDispatcher::isValid(
779 const OUString& rWord,
780 sal_Int16 nLanguage,
781 const uno::Sequence< beans::PropertyValue >& rProperties )
782 throw (lang::IllegalArgumentException, uno::RuntimeException)
783 {
784 MutexGuard aGuard( GetLinguMutex() );
785 Locale aLocale( CreateLocale( nLanguage ) );
786 return isValid( rWord, aLocale, rProperties);
787 }
788
789
spell(const OUString & rWord,sal_Int16 nLanguage,const uno::Sequence<beans::PropertyValue> & rProperties)790 uno::Reference< linguistic2::XSpellAlternatives > SAL_CALL SpellCheckerDispatcher::spell(
791 const OUString& rWord,
792 sal_Int16 nLanguage,
793 const uno::Sequence< beans::PropertyValue >& rProperties )
794 throw (lang::IllegalArgumentException, uno::RuntimeException)
795 {
796 MutexGuard aGuard( GetLinguMutex() );
797 Locale aLocale( CreateLocale( nLanguage ) );
798 return spell( rWord, aLocale, rProperties);
799 }
800
801
SetServiceList(const Locale & rLocale,const Sequence<OUString> & rSvcImplNames)802 void SpellCheckerDispatcher::SetServiceList( const Locale &rLocale,
803 const Sequence< OUString > &rSvcImplNames )
804 {
805 MutexGuard aGuard( GetLinguMutex() );
806
807 if (pCache)
808 pCache->Flush(); // new services may spell differently...
809
810 sal_Int16 nLanguage = LocaleToLanguage( rLocale );
811
812 sal_Int32 nLen = rSvcImplNames.getLength();
813 if (0 == nLen)
814 // remove entry
815 aSvcMap.erase( nLanguage );
816 else
817 {
818 // modify/add entry
819 LangSvcEntries_Spell *pEntry = aSvcMap[ nLanguage ].get();
820 if (pEntry)
821 {
822 pEntry->Clear();
823 pEntry->aSvcImplNames = rSvcImplNames;
824 pEntry->aSvcRefs = Sequence< Reference < XSpellChecker > > ( nLen );
825 }
826 else
827 {
828 boost::shared_ptr< LangSvcEntries_Spell > pTmpEntry( new LangSvcEntries_Spell( rSvcImplNames ) );
829 pTmpEntry->aSvcRefs = Sequence< Reference < XSpellChecker > >( nLen );
830 aSvcMap[ nLanguage ] = pTmpEntry;
831 }
832 }
833 }
834
835
836 Sequence< OUString >
GetServiceList(const Locale & rLocale) const837 SpellCheckerDispatcher::GetServiceList( const Locale &rLocale ) const
838 {
839 MutexGuard aGuard( GetLinguMutex() );
840
841 Sequence< OUString > aRes;
842
843 // search for entry with that language and use data from that
844 sal_Int16 nLanguage = LocaleToLanguage( rLocale );
845 SpellCheckerDispatcher *pThis = (SpellCheckerDispatcher *) this;
846 const SpellSvcByLangMap_t::iterator aIt( pThis->aSvcMap.find( nLanguage ) );
847 const LangSvcEntries_Spell *pEntry = aIt != aSvcMap.end() ? aIt->second.get() : NULL;
848 if (pEntry)
849 aRes = pEntry->aSvcImplNames;
850
851 return aRes;
852 }
853
854
GetDspType() const855 LinguDispatcher::DspType SpellCheckerDispatcher::GetDspType() const
856 {
857 return DSP_SPELL;
858 }
859
FlushSpellCache()860 void SpellCheckerDispatcher::FlushSpellCache()
861 {
862 if (pCache)
863 pCache->Flush();
864 }
865
866 ///////////////////////////////////////////////////////////////////////////
867
868