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