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: 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 90 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 102 void ProposalList::Prepend( const OUString &rText ) 103 { 104 if (!HasEntry( rText )) 105 aVec.insert( aVec.begin(), rText ); 106 } 107 108 void ProposalList::Append( const OUString &rText ) 109 { 110 if (!HasEntry( rText )) 111 aVec.push_back( rText ); 112 } 113 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 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 137 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 151 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 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 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 210 SpellCheckerDispatcher::SpellCheckerDispatcher( LngSvcMgr &rLngSvcMgr ) : 211 rMgr (rLngSvcMgr) 212 { 213 pCache = NULL; 214 } 215 216 217 SpellCheckerDispatcher::~SpellCheckerDispatcher() 218 { 219 ClearSvcList(); 220 delete pCache; 221 } 222 223 224 void SpellCheckerDispatcher::ClearSvcList() 225 { 226 // release memory for each table entry 227 SpellSvcByLangMap_t aTmp; 228 aSvcMap.swap( aTmp ); 229 } 230 231 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 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 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 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 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 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 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 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 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 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 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 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 > 837 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 855 LinguDispatcher::DspType SpellCheckerDispatcher::GetDspType() const 856 { 857 return DSP_SPELL; 858 } 859 860 void SpellCheckerDispatcher::FlushSpellCache() 861 { 862 if (pCache) 863 pCache->Flush(); 864 } 865 866 /////////////////////////////////////////////////////////////////////////// 867 868