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 #include "precompiled_linguistic.hxx" 28 29 #include <com/sun/star/container/XContentEnumerationAccess.hpp> 30 #include <com/sun/star/container/XEnumeration.hpp> 31 #include <com/sun/star/container/XNameAccess.hpp> 32 #include <com/sun/star/container/XNameContainer.hpp> 33 #include <com/sun/star/container/XNameReplace.hpp> 34 #include <com/sun/star/i18n/XBreakIterator.hpp> 35 #include <com/sun/star/lang/XComponent.hpp> 36 #include <com/sun/star/lang/XServiceInfo.hpp> 37 #include <com/sun/star/lang/XMultiServiceFactory.hpp> 38 #include <com/sun/star/linguistic2/XSupportedLocales.hpp> 39 #include <com/sun/star/linguistic2/XProofreader.hpp> 40 #include <com/sun/star/linguistic2/XProofreadingIterator.hpp> 41 #include <com/sun/star/linguistic2/SingleProofreadingError.hpp> 42 #include <com/sun/star/linguistic2/ProofreadingResult.hpp> 43 #include <com/sun/star/linguistic2/LinguServiceEvent.hpp> 44 #include <com/sun/star/linguistic2/LinguServiceEventFlags.hpp> 45 #include <com/sun/star/registry/XRegistryKey.hpp> 46 #include <com/sun/star/text/TextMarkupType.hpp> 47 #include <com/sun/star/text/TextMarkupDescriptor.hpp> 48 #include <com/sun/star/text/XTextMarkup.hpp> 49 #include <com/sun/star/text/XMultiTextMarkup.hpp> 50 #include <com/sun/star/text/XFlatParagraph.hpp> 51 #include <com/sun/star/text/XFlatParagraphIterator.hpp> 52 #include <com/sun/star/uno/XComponentContext.hpp> 53 #include <com/sun/star/lang/XSingleComponentFactory.hpp> 54 55 #include <sal/config.h> 56 #include <osl/conditn.hxx> 57 #include <osl/thread.hxx> 58 #include <cppuhelper/implbase4.hxx> 59 #include <cppuhelper/implementationentry.hxx> 60 #include <cppuhelper/interfacecontainer.h> 61 #include <cppuhelper/factory.hxx> 62 #include <i18npool/mslangid.hxx> 63 #include <unotools/processfactory.hxx> 64 #include <comphelper/extract.hxx> 65 66 #include <deque> 67 #include <map> 68 #include <vector> 69 70 #include "linguistic/misc.hxx" 71 #include "defs.hxx" 72 #include "lngopt.hxx" 73 74 #include "gciterator.hxx" 75 76 using ::rtl::OUString; 77 using namespace linguistic; 78 using namespace ::com::sun::star; 79 80 // forward declarations 81 static ::rtl::OUString GrammarCheckingIterator_getImplementationName() throw(); 82 static uno::Sequence< OUString > GrammarCheckingIterator_getSupportedServiceNames() throw(); 83 84 85 ////////////////////////////////////////////////////////////////////// 86 87 // white space list: obtained from the fonts.config.txt of a Linux system. 88 static sal_Unicode aWhiteSpaces[] = 89 { 90 0x0020, /* SPACE */ 91 0x00a0, /* NO-BREAK SPACE */ 92 0x00ad, /* SOFT HYPHEN */ 93 0x115f, /* HANGUL CHOSEONG FILLER */ 94 0x1160, /* HANGUL JUNGSEONG FILLER */ 95 0x1680, /* OGHAM SPACE MARK */ 96 0x2000, /* EN QUAD */ 97 0x2001, /* EM QUAD */ 98 0x2002, /* EN SPACE */ 99 0x2003, /* EM SPACE */ 100 0x2004, /* THREE-PER-EM SPACE */ 101 0x2005, /* FOUR-PER-EM SPACE */ 102 0x2006, /* SIX-PER-EM SPACE */ 103 0x2007, /* FIGURE SPACE */ 104 0x2008, /* PUNCTUATION SPACE */ 105 0x2009, /* THIN SPACE */ 106 0x200a, /* HAIR SPACE */ 107 0x200b, /* ZERO WIDTH SPACE */ 108 0x200c, /* ZERO WIDTH NON-JOINER */ 109 0x200d, /* ZERO WIDTH JOINER */ 110 0x200e, /* LEFT-TO-RIGHT MARK */ 111 0x200f, /* RIGHT-TO-LEFT MARK */ 112 0x2028, /* LINE SEPARATOR */ 113 0x2029, /* PARAGRAPH SEPARATOR */ 114 0x202a, /* LEFT-TO-RIGHT EMBEDDING */ 115 0x202b, /* RIGHT-TO-LEFT EMBEDDING */ 116 0x202c, /* POP DIRECTIONAL FORMATTING */ 117 0x202d, /* LEFT-TO-RIGHT OVERRIDE */ 118 0x202e, /* RIGHT-TO-LEFT OVERRIDE */ 119 0x202f, /* NARROW NO-BREAK SPACE */ 120 0x205f, /* MEDIUM MATHEMATICAL SPACE */ 121 0x2060, /* WORD JOINER */ 122 0x2061, /* FUNCTION APPLICATION */ 123 0x2062, /* INVISIBLE TIMES */ 124 0x2063, /* INVISIBLE SEPARATOR */ 125 0x206A, /* INHIBIT SYMMETRIC SWAPPING */ 126 0x206B, /* ACTIVATE SYMMETRIC SWAPPING */ 127 0x206C, /* INHIBIT ARABIC FORM SHAPING */ 128 0x206D, /* ACTIVATE ARABIC FORM SHAPING */ 129 0x206E, /* NATIONAL DIGIT SHAPES */ 130 0x206F, /* NOMINAL DIGIT SHAPES */ 131 0x3000, /* IDEOGRAPHIC SPACE */ 132 0x3164, /* HANGUL FILLER */ 133 0xfeff, /* ZERO WIDTH NO-BREAK SPACE */ 134 0xffa0, /* HALFWIDTH HANGUL FILLER */ 135 0xfff9, /* INTERLINEAR ANNOTATION ANCHOR */ 136 0xfffa, /* INTERLINEAR ANNOTATION SEPARATOR */ 137 0xfffb /* INTERLINEAR ANNOTATION TERMINATOR */ 138 }; 139 140 static int nWhiteSpaces = sizeof( aWhiteSpaces ) / sizeof( aWhiteSpaces[0] ); 141 142 static bool lcl_IsWhiteSpace( sal_Unicode cChar ) 143 { 144 bool bFound = false; 145 for (int i = 0; i < nWhiteSpaces && !bFound; ++i) 146 { 147 if (cChar == aWhiteSpaces[i]) 148 bFound = true; 149 } 150 return bFound; 151 } 152 153 static sal_Int32 lcl_SkipWhiteSpaces( const OUString &rText, sal_Int32 nStartPos ) 154 { 155 // note having nStartPos point right behind the string is OK since that one 156 // is a correct end-of-sentence position to be returned from a grammar checker... 157 158 const sal_Int32 nLen = rText.getLength(); 159 bool bIllegalArgument = false; 160 if (nStartPos < 0) 161 { 162 bIllegalArgument = true; 163 nStartPos = 0; 164 } 165 if (nStartPos > nLen) 166 { 167 bIllegalArgument = true; 168 nStartPos = nLen; 169 } 170 if (bIllegalArgument) 171 { 172 DBG_ASSERT( 0, "lcl_SkipWhiteSpaces: illegal arguments" ); 173 } 174 175 sal_Int32 nRes = nStartPos; 176 if (0 <= nStartPos && nStartPos < nLen) 177 { 178 const sal_Unicode *pText = rText.getStr() + nStartPos; 179 while (nStartPos < nLen && lcl_IsWhiteSpace( *pText )) 180 ++pText; 181 nRes = pText - rText.getStr(); 182 } 183 184 DBG_ASSERT( 0 <= nRes && nRes <= nLen, "lcl_SkipWhiteSpaces return value out of range" ); 185 return nRes; 186 } 187 188 static sal_Int32 lcl_BacktraceWhiteSpaces( const OUString &rText, sal_Int32 nStartPos ) 189 { 190 // note: having nStartPos point right behind the string is OK since that one 191 // is a correct end-of-sentence position to be returned from a grammar checker... 192 193 const sal_Int32 nLen = rText.getLength(); 194 bool bIllegalArgument = false; 195 if (nStartPos < 0) 196 { 197 bIllegalArgument = true; 198 nStartPos = 0; 199 } 200 if (nStartPos > nLen) 201 { 202 bIllegalArgument = true; 203 nStartPos = nLen; 204 } 205 if (bIllegalArgument) 206 { 207 DBG_ASSERT( 0, "lcl_BacktraceWhiteSpaces: illegal arguments" ); 208 } 209 210 sal_Int32 nRes = nStartPos; 211 sal_Int32 nPosBefore = nStartPos - 1; 212 const sal_Unicode *pStart = rText.getStr(); 213 if (0 <= nPosBefore && nPosBefore < nLen && lcl_IsWhiteSpace( pStart[ nPosBefore ] )) 214 { 215 nStartPos = nPosBefore; 216 if (0 <= nStartPos && nStartPos < nLen) 217 { 218 const sal_Unicode *pText = rText.getStr() + nStartPos; 219 while (pText > pStart && lcl_IsWhiteSpace( *pText )) 220 --pText; 221 // now add 1 since we want to point to the first char after the last char in the sentence... 222 nRes = pText - pStart + 1; 223 } 224 } 225 226 DBG_ASSERT( 0 <= nRes && nRes <= nLen, "lcl_BacktraceWhiteSpaces return value out of range" ); 227 return nRes; 228 } 229 230 ////////////////////////////////////////////////////////////////////// 231 232 extern "C" void workerfunc (void * gci) 233 { 234 ((GrammarCheckingIterator*)gci)->DequeueAndCheck(); 235 } 236 237 static lang::Locale lcl_GetPrimaryLanguageOfSentence( 238 uno::Reference< text::XFlatParagraph > xFlatPara, 239 sal_Int32 nStartIndex ) 240 { 241 //get the language of the first word 242 return xFlatPara->getLanguageOfText( nStartIndex, 1 ); 243 } 244 245 ////////////////////////////////////////////////////////////////////// 246 /* 247 class MyThread : punlic osl::Thread 248 { 249 void run () 250 { 251 DequeueAndCheck(); 252 } 253 254 void own_terminate () 255 { 256 m_bEnd = true; 257 wait (3000); 258 terminate (); 259 } 260 } 261 262 MyThread m_aQueue; 263 264 vois startGrammarChecking() 265 { 266 if (!m_aQueue.isRunning ()) 267 m_aQueue.create (); 268 } 269 270 void stopGrammarChecking () 271 { 272 if (m_aQueue.isRunning ()) 273 m_aQueue.own_terminate (); 274 } 275 */ 276 277 GrammarCheckingIterator::GrammarCheckingIterator( const uno::Reference< lang::XMultiServiceFactory > & rxMgr ) : 278 m_xMSF( rxMgr ), 279 m_bEnd( sal_False ), 280 m_aCurCheckedDocId(), 281 m_bGCServicesChecked( sal_False ), 282 m_nDocIdCounter( 0 ), 283 m_nLastEndOfSentencePos( -1 ), 284 m_aEventListeners( MyMutex::get() ), 285 m_aNotifyListeners( MyMutex::get() ) 286 { 287 osl_createThread( workerfunc, this ); 288 } 289 290 291 GrammarCheckingIterator::~GrammarCheckingIterator() 292 { 293 ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); 294 } 295 296 297 sal_Int32 GrammarCheckingIterator::NextDocId() 298 { 299 ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); 300 m_nDocIdCounter += 1; 301 return m_nDocIdCounter; 302 } 303 304 305 OUString GrammarCheckingIterator::GetOrCreateDocId( 306 const uno::Reference< lang::XComponent > &xComponent ) 307 { 308 // internal method; will always be called with locked mutex 309 310 OUString aRes; 311 if (xComponent.is()) 312 { 313 if (m_aDocIdMap.find( xComponent.get() ) != m_aDocIdMap.end()) 314 { 315 // return already existing entry 316 aRes = m_aDocIdMap[ xComponent.get() ]; 317 } 318 else // add new entry 319 { 320 sal_Int32 nRes = NextDocId(); 321 aRes = OUString::valueOf( nRes ); 322 m_aDocIdMap[ xComponent.get() ] = aRes; 323 xComponent->addEventListener( this ); 324 } 325 } 326 return aRes; 327 } 328 329 330 void GrammarCheckingIterator::AddEntry( 331 uno::WeakReference< text::XFlatParagraphIterator > xFlatParaIterator, 332 uno::WeakReference< text::XFlatParagraph > xFlatPara, 333 const OUString & rDocId, 334 sal_Int32 nStartIndex, 335 sal_Bool bAutomatic ) 336 { 337 // we may not need/have a xFlatParaIterator (e.g. if checkGrammarAtPos was called) 338 // but we always need a xFlatPara... 339 uno::Reference< text::XFlatParagraph > xPara( xFlatPara ); 340 if (xPara.is()) 341 { 342 FPEntry aNewFPEntry; 343 aNewFPEntry.m_xParaIterator = xFlatParaIterator; 344 aNewFPEntry.m_xPara = xFlatPara; 345 aNewFPEntry.m_aDocId = rDocId; 346 aNewFPEntry.m_nStartIndex = nStartIndex; 347 aNewFPEntry.m_bAutomatic = bAutomatic; 348 349 // add new entry to the end of this queue 350 ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); 351 m_aFPEntriesQueue.push_back( aNewFPEntry ); 352 353 // wake up the thread in order to do grammar checking 354 m_aWakeUpThread.set(); 355 } 356 } 357 358 359 void GrammarCheckingIterator::ProcessResult( 360 const linguistic2::ProofreadingResult &rRes, 361 const uno::Reference< text::XFlatParagraphIterator > &rxFlatParagraphIterator, 362 bool bIsAutomaticChecking ) 363 { 364 DBG_ASSERT( rRes.xFlatParagraph.is(), "xFlatParagraph is missing" ); 365 //no guard necessary as no members are used 366 sal_Bool bContinueWithNextPara = sal_False; 367 if (!rRes.xFlatParagraph.is() || rRes.xFlatParagraph->isModified()) 368 { 369 // if paragraph was modified/deleted meanwhile continue with the next one... 370 bContinueWithNextPara = sal_True; 371 } 372 else // paragraph is still unchanged... 373 { 374 // 375 // mark found errors... 376 // 377 378 sal_Int32 nTextLen = rRes.aText.getLength(); 379 bool bBoundariesOk = 0 <= rRes.nStartOfSentencePosition && rRes.nStartOfSentencePosition <= nTextLen && 380 0 <= rRes.nBehindEndOfSentencePosition && rRes.nBehindEndOfSentencePosition <= nTextLen && 381 0 <= rRes.nStartOfNextSentencePosition && rRes.nStartOfNextSentencePosition <= nTextLen && 382 rRes.nStartOfSentencePosition <= rRes.nBehindEndOfSentencePosition && 383 rRes.nBehindEndOfSentencePosition <= rRes.nStartOfNextSentencePosition; 384 (void) bBoundariesOk; 385 DBG_ASSERT( bBoundariesOk, "inconsistent sentence boundaries" ); 386 uno::Sequence< linguistic2::SingleProofreadingError > aErrors = rRes.aErrors; 387 388 uno::Reference< text::XMultiTextMarkup > xMulti( rRes.xFlatParagraph, uno::UNO_QUERY ); 389 if (xMulti.is()) // use new API for markups 390 { 391 try 392 { 393 // length = number of found errors + 1 sentence markup 394 sal_Int32 nErrors = rRes.aErrors.getLength(); 395 uno::Sequence< text::TextMarkupDescriptor > aDescriptors( nErrors + 1 ); 396 text::TextMarkupDescriptor * pDescriptors = aDescriptors.getArray(); 397 398 // at pos 0 .. nErrors-1 -> all grammar errors 399 for (sal_Int32 i = 0; i < nErrors; ++i) 400 { 401 const linguistic2::SingleProofreadingError &rError = rRes.aErrors[i]; 402 text::TextMarkupDescriptor &rDesc = aDescriptors[i]; 403 404 rDesc.nType = rError.nErrorType; 405 rDesc.nOffset = rError.nErrorStart; 406 rDesc.nLength = rError.nErrorLength; 407 408 // the proofreader may return SPELLING but right now our core 409 // does only handle PROOFREADING if the result is from the proofreader... 410 // (later on we may wish to color spelling errors found by the proofreader 411 // differently for example. But no special handling right now. 412 if (rDesc.nType == text::TextMarkupType::SPELLCHECK) 413 rDesc.nType = text::TextMarkupType::PROOFREADING; 414 } 415 416 // at pos nErrors -> sentence markup 417 // nSentenceLength: includes the white-spaces following the sentence end... 418 const sal_Int32 nSentenceLength = rRes.nStartOfNextSentencePosition - rRes.nStartOfSentencePosition; 419 pDescriptors[ nErrors ].nType = text::TextMarkupType::SENTENCE; 420 pDescriptors[ nErrors ].nOffset = rRes.nStartOfSentencePosition; 421 pDescriptors[ nErrors ].nLength = nSentenceLength; 422 423 xMulti->commitMultiTextMarkup( aDescriptors ) ; 424 } 425 catch (lang::IllegalArgumentException &) 426 { 427 DBG_ERROR( "commitMultiTextMarkup: IllegalArgumentException exception caught" ); 428 } 429 } 430 431 // other sentences left to be checked in this paragraph? 432 if (rRes.nStartOfNextSentencePosition < rRes.aText.getLength()) 433 { 434 AddEntry( rxFlatParagraphIterator, rRes.xFlatParagraph, rRes.aDocumentIdentifier, rRes.nStartOfNextSentencePosition, bIsAutomaticChecking ); 435 } 436 else // current paragraph finished 437 { 438 // set "already checked" flag for the current flat paragraph 439 if (rRes.xFlatParagraph.is()) 440 rRes.xFlatParagraph->setChecked( text::TextMarkupType::PROOFREADING, true ); 441 442 bContinueWithNextPara = sal_True; 443 } 444 } 445 446 if (bContinueWithNextPara) 447 { 448 // we need to continue with the next paragraph 449 uno::Reference< text::XFlatParagraph > xFlatParaNext; 450 if (rxFlatParagraphIterator.is()) 451 xFlatParaNext = rxFlatParagraphIterator->getNextPara(); 452 { 453 AddEntry( rxFlatParagraphIterator, xFlatParaNext, rRes.aDocumentIdentifier, 0, bIsAutomaticChecking ); 454 } 455 } 456 } 457 458 459 uno::Reference< linguistic2::XProofreader > GrammarCheckingIterator::GetGrammarChecker( 460 const lang::Locale &rLocale ) 461 { 462 (void) rLocale; 463 uno::Reference< linguistic2::XProofreader > xRes; 464 465 // ---- THREAD SAFE START ---- 466 ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); 467 468 // check supported locales for each grammarchecker if not already done 469 if (!m_bGCServicesChecked) 470 { 471 //GetAvailableGCSvcs_Impl(); 472 GetConfiguredGCSvcs_Impl(); 473 //GetMatchingGCSvcs_Impl(); 474 m_bGCServicesChecked = sal_True; 475 } 476 477 const LanguageType nLang = MsLangId::convertLocaleToLanguage( rLocale ); 478 GCImplNames_t::const_iterator aLangIt( m_aGCImplNamesByLang.find( nLang ) ); 479 if (aLangIt != m_aGCImplNamesByLang.end()) // matching configured language found? 480 { 481 OUString aSvcImplName( aLangIt->second ); 482 GCReferences_t::const_iterator aImplNameIt( m_aGCReferencesByService.find( aSvcImplName ) ); 483 if (aImplNameIt != m_aGCReferencesByService.end()) // matching impl name found? 484 { 485 xRes = aImplNameIt->second; 486 } 487 else // the service is to be instatiated here for the first time... 488 { 489 try 490 { 491 uno::Reference< lang::XMultiServiceFactory > xMgr( 492 utl::getProcessServiceFactory(), uno::UNO_QUERY_THROW ); 493 uno::Reference< linguistic2::XProofreader > xGC( 494 xMgr->createInstance( aSvcImplName ), uno::UNO_QUERY_THROW ); 495 uno::Reference< linguistic2::XSupportedLocales > xSuppLoc( xGC, uno::UNO_QUERY_THROW ); 496 497 if (xSuppLoc->hasLocale( rLocale )) 498 { 499 m_aGCReferencesByService[ aSvcImplName ] = xGC; 500 xRes = xGC; 501 502 uno::Reference< linguistic2::XLinguServiceEventBroadcaster > xBC( xGC, uno::UNO_QUERY ); 503 if (xBC.is()) 504 xBC->addLinguServiceEventListener( this ); 505 } 506 else 507 { 508 DBG_ASSERT( 0, "grammar checker does not support required locale" ); 509 } 510 } 511 catch (uno::Exception &) 512 { 513 DBG_ASSERT( 0, "instantiating grammar checker failed" ); 514 } 515 } 516 } 517 // ---- THREAD SAFE END ---- 518 519 return xRes; 520 } 521 522 523 void GrammarCheckingIterator::DequeueAndCheck() 524 { 525 uno::Sequence< sal_Int32 > aLangPortions; 526 uno::Sequence< lang::Locale > aLangPortionsLocale; 527 528 // ---- THREAD SAFE START ---- 529 bool bEnd = false; 530 { 531 ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); 532 bEnd = m_bEnd; 533 } 534 // ---- THREAD SAFE END ---- 535 while (!bEnd) 536 { 537 // ---- THREAD SAFE START ---- 538 bool bQueueEmpty = false; 539 { 540 ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); 541 bQueueEmpty = m_aFPEntriesQueue.empty(); 542 } 543 // ---- THREAD SAFE END ---- 544 545 if (!bQueueEmpty) 546 { 547 uno::Reference< text::XFlatParagraphIterator > xFPIterator; 548 uno::Reference< text::XFlatParagraph > xFlatPara; 549 FPEntry aFPEntryItem; 550 OUString aCurDocId; 551 sal_Bool bModified = sal_False; 552 // ---- THREAD SAFE START ---- 553 { 554 ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); 555 aFPEntryItem = m_aFPEntriesQueue.front(); 556 xFPIterator = aFPEntryItem.m_xParaIterator; 557 xFlatPara = aFPEntryItem.m_xPara; 558 m_aCurCheckedDocId = aFPEntryItem.m_aDocId; 559 aCurDocId = m_aCurCheckedDocId; 560 561 m_aFPEntriesQueue.pop_front(); 562 } 563 // ---- THREAD SAFE END ---- 564 565 if (xFlatPara.is() && xFPIterator.is()) 566 { 567 OUString aCurTxt( xFlatPara->getText() ); 568 lang::Locale aCurLocale = lcl_GetPrimaryLanguageOfSentence( xFlatPara, aFPEntryItem.m_nStartIndex ); 569 570 bModified = xFlatPara->isModified(); 571 if (!bModified) 572 { 573 // ---- THREAD SAFE START ---- 574 ::osl::ClearableGuard< ::osl::Mutex > aGuard( MyMutex::get() ); 575 576 sal_Int32 nStartPos = aFPEntryItem.m_nStartIndex; 577 sal_Int32 nSuggestedEnd = GetSuggestedEndOfSentence( aCurTxt, nStartPos, aCurLocale ); 578 DBG_ASSERT( (nSuggestedEnd == 0 && aCurTxt.getLength() == 0) || nSuggestedEnd > nStartPos, 579 "nSuggestedEndOfSentencePos calculation failed?" ); 580 581 linguistic2::ProofreadingResult aRes; 582 583 uno::Reference< linguistic2::XProofreader > xGC( GetGrammarChecker( aCurLocale ), uno::UNO_QUERY ); 584 if (xGC.is()) 585 { 586 aGuard.clear(); 587 uno::Sequence< beans::PropertyValue > aEmptyProps; 588 aRes = xGC->doProofreading( aCurDocId, aCurTxt, aCurLocale, nStartPos, nSuggestedEnd, aEmptyProps ); 589 590 //!! work-around to prevent looping if the grammar checker 591 //!! failed to properly identify the sentence end 592 if (aRes.nBehindEndOfSentencePosition <= nStartPos) 593 { 594 DBG_ASSERT( 0, "!! Grammarchecker failed to provide end of sentence !!" ); 595 aRes.nBehindEndOfSentencePosition = nSuggestedEnd; 596 } 597 598 aRes.xFlatParagraph = xFlatPara; 599 aRes.nStartOfSentencePosition = nStartPos; 600 } 601 else 602 { 603 // no grammar checker -> no error 604 // but we need to provide the data below in order to continue with the next sentence 605 aRes.aDocumentIdentifier = aCurDocId; 606 aRes.xFlatParagraph = xFlatPara; 607 aRes.aText = aCurTxt; 608 aRes.aLocale = aCurLocale; 609 aRes.nStartOfSentencePosition = nStartPos; 610 aRes.nBehindEndOfSentencePosition = nSuggestedEnd; 611 } 612 aRes.nStartOfNextSentencePosition = lcl_SkipWhiteSpaces( aCurTxt, aRes.nBehindEndOfSentencePosition ); 613 aRes.nBehindEndOfSentencePosition = lcl_BacktraceWhiteSpaces( aCurTxt, aRes.nStartOfNextSentencePosition ); 614 615 //guard has to be cleared as ProcessResult calls out of this class 616 aGuard.clear(); 617 ProcessResult( aRes, xFPIterator, aFPEntryItem.m_bAutomatic ); 618 // ---- THREAD SAFE END ---- 619 } 620 else 621 { 622 // the paragraph changed meanwhile... (and maybe is still edited) 623 // thus we simply continue to ask for the next to be checked. 624 uno::Reference< text::XFlatParagraph > xFlatParaNext( xFPIterator->getNextPara() ); 625 AddEntry( xFPIterator, xFlatParaNext, aCurDocId, 0, aFPEntryItem.m_bAutomatic ); 626 } 627 } 628 629 // ---- THREAD SAFE START ---- 630 { 631 ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); 632 m_aCurCheckedDocId = OUString(); 633 } 634 // ---- THREAD SAFE END ---- 635 } 636 else 637 { 638 // ---- THREAD SAFE START ---- 639 { 640 ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); 641 // Check queue state again 642 if (m_aFPEntriesQueue.empty()) 643 m_aWakeUpThread.reset(); 644 } 645 // ---- THREAD SAFE END ---- 646 647 //if the queue is empty 648 // IMPORTANT: Don't call condition.wait() with locked 649 // mutex. Otherwise you would keep out other threads 650 // to add entries to the queue! A condition is thread- 651 // safe implemented. 652 m_aWakeUpThread.wait(); 653 } 654 655 // ---- THREAD SAFE START ---- 656 { 657 ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); 658 bEnd = m_bEnd; 659 } 660 // ---- THREAD SAFE END ---- 661 } 662 663 //!! This one must be the very last statement to call in this function !! 664 m_aRequestEndThread.set(); 665 } 666 667 668 void SAL_CALL GrammarCheckingIterator::startProofreading( 669 const uno::Reference< ::uno::XInterface > & xDoc, 670 const uno::Reference< text::XFlatParagraphIteratorProvider > & xIteratorProvider ) 671 throw (uno::RuntimeException, lang::IllegalArgumentException) 672 { 673 // get paragraph to start checking with 674 const bool bAutomatic = true; 675 uno::Reference<text::XFlatParagraphIterator> xFPIterator = xIteratorProvider->getFlatParagraphIterator( 676 text::TextMarkupType::PROOFREADING, bAutomatic ); 677 uno::Reference< text::XFlatParagraph > xPara( xFPIterator.is()? xFPIterator->getFirstPara() : NULL ); 678 uno::Reference< lang::XComponent > xComponent( xDoc, uno::UNO_QUERY ); 679 680 // ---- THREAD SAFE START ---- 681 ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); 682 if (xPara.is() && xComponent.is()) 683 { 684 OUString aDocId = GetOrCreateDocId( xComponent ); 685 686 // create new entry and add it to queue 687 AddEntry( xFPIterator, xPara, aDocId, 0, bAutomatic ); 688 } 689 // ---- THREAD SAFE END ---- 690 } 691 692 693 linguistic2::ProofreadingResult SAL_CALL GrammarCheckingIterator::checkSentenceAtPosition( 694 const uno::Reference< uno::XInterface >& xDoc, 695 const uno::Reference< text::XFlatParagraph >& xFlatPara, 696 const OUString& rText, 697 const lang::Locale& rLocale, 698 sal_Int32 nStartOfSentencePos, 699 sal_Int32 nSuggestedEndOfSentencePos, 700 sal_Int32 nErrorPosInPara ) 701 throw (lang::IllegalArgumentException, uno::RuntimeException) 702 { 703 (void) rLocale; 704 705 // for the context menu... 706 707 linguistic2::ProofreadingResult aRes; 708 709 uno::Reference< lang::XComponent > xComponent( xDoc, uno::UNO_QUERY ); 710 if (xFlatPara.is() && xComponent.is() && 711 ( nErrorPosInPara < 0 || nErrorPosInPara < rText.getLength())) 712 { 713 // iterate through paragraph until we find the sentence we are interested in 714 linguistic2::ProofreadingResult aTmpRes; 715 sal_Int32 nStartPos = nStartOfSentencePos >= 0 ? nStartOfSentencePos : 0; 716 717 bool bFound = false; 718 do 719 { 720 lang::Locale aCurLocale = lcl_GetPrimaryLanguageOfSentence( xFlatPara, nStartPos ); 721 sal_Int32 nOldStartOfSentencePos = nStartPos; 722 uno::Reference< linguistic2::XProofreader > xGC; 723 OUString aDocId; 724 725 // ---- THREAD SAFE START ---- 726 { 727 ::osl::ClearableGuard< ::osl::Mutex > aGuard( MyMutex::get() ); 728 aDocId = GetOrCreateDocId( xComponent ); 729 nSuggestedEndOfSentencePos = GetSuggestedEndOfSentence( rText, nStartPos, aCurLocale ); 730 DBG_ASSERT( nSuggestedEndOfSentencePos > nStartPos, "nSuggestedEndOfSentencePos calculation failed?" ); 731 732 xGC = GetGrammarChecker( aCurLocale ); 733 } 734 // ---- THREAD SAFE START ---- 735 sal_Int32 nEndPos = -1; 736 if (xGC.is()) 737 { 738 uno::Sequence< beans::PropertyValue > aEmptyProps; 739 aTmpRes = xGC->doProofreading( aDocId, rText, aCurLocale, nStartPos, nSuggestedEndOfSentencePos, aEmptyProps ); 740 741 //!! work-around to prevent looping if the grammar checker 742 //!! failed to properly identify the sentence end 743 if (aTmpRes.nBehindEndOfSentencePosition <= nStartPos) 744 { 745 DBG_ASSERT( 0, "!! Grammarchecker failed to provide end of sentence !!" ); 746 aTmpRes.nBehindEndOfSentencePosition = nSuggestedEndOfSentencePos; 747 } 748 749 aTmpRes.xFlatParagraph = xFlatPara; 750 aTmpRes.nStartOfSentencePosition = nStartPos; 751 nEndPos = aTmpRes.nBehindEndOfSentencePosition; 752 753 if ((nErrorPosInPara< 0 || nStartPos <= nErrorPosInPara) && nErrorPosInPara < nEndPos) 754 bFound = true; 755 } 756 if (nEndPos == -1) // no result from grammar checker 757 nEndPos = nSuggestedEndOfSentencePos; 758 nStartPos = lcl_SkipWhiteSpaces( rText, nEndPos ); 759 aTmpRes.nBehindEndOfSentencePosition = nEndPos; 760 aTmpRes.nStartOfNextSentencePosition = nStartPos; 761 aTmpRes.nBehindEndOfSentencePosition = lcl_BacktraceWhiteSpaces( rText, aTmpRes.nStartOfNextSentencePosition ); 762 763 // prevent endless loop by forcefully advancing if needs be... 764 if (nStartPos <= nOldStartOfSentencePos) 765 { 766 DBG_ASSERT( 0, "end-of-sentence detection failed?" ); 767 nStartPos = nOldStartOfSentencePos + 1; 768 } 769 } 770 while (!bFound && nStartPos < rText.getLength()); 771 772 if (bFound && !xFlatPara->isModified()) 773 aRes = aTmpRes; 774 } 775 776 return aRes; 777 } 778 779 780 sal_Int32 GrammarCheckingIterator::GetSuggestedEndOfSentence( 781 const OUString &rText, 782 sal_Int32 nSentenceStartPos, 783 const lang::Locale &rLocale ) 784 { 785 // internal method; will always be called with locked mutex 786 787 uno::Reference< i18n::XBreakIterator > xBreakIterator; 788 if (!m_xBreakIterator.is()) 789 { 790 uno::Reference< lang::XMultiServiceFactory > xMSF = ::comphelper::getProcessServiceFactory(); 791 if ( xMSF.is() ) 792 xBreakIterator = uno::Reference < i18n::XBreakIterator >( xMSF->createInstance( 793 ::rtl::OUString::createFromAscii("com.sun.star.i18n.BreakIterator") ), uno::UNO_QUERY ); 794 } 795 sal_Int32 nTextLen = rText.getLength(); 796 sal_Int32 nEndPosition = nTextLen; 797 if (m_xBreakIterator.is()) 798 { 799 sal_Int32 nTmpStartPos = nSentenceStartPos; 800 do 801 { 802 nEndPosition = nTextLen; 803 if (nTmpStartPos < nTextLen) 804 nEndPosition = m_xBreakIterator->endOfSentence( rText, nTmpStartPos, rLocale ); 805 if (nEndPosition < 0) 806 nEndPosition = nTextLen; 807 808 ++nTmpStartPos; 809 } 810 while (nEndPosition <= nSentenceStartPos && nEndPosition < nTextLen); 811 if (nEndPosition > nTextLen) 812 nEndPosition = nTextLen; 813 } 814 return nEndPosition; 815 } 816 817 818 void SAL_CALL GrammarCheckingIterator::resetIgnoreRules( ) 819 throw (uno::RuntimeException) 820 { 821 GCReferences_t::iterator aIt( m_aGCReferencesByService.begin() ); 822 while (aIt != m_aGCReferencesByService.end()) 823 { 824 uno::Reference< linguistic2::XProofreader > xGC( aIt->second ); 825 if (xGC.is()) 826 xGC->resetIgnoreRules(); 827 ++aIt; 828 } 829 } 830 831 832 sal_Bool SAL_CALL GrammarCheckingIterator::isProofreading( 833 const uno::Reference< uno::XInterface >& xDoc ) 834 throw (uno::RuntimeException) 835 { 836 // ---- THREAD SAFE START ---- 837 ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); 838 839 sal_Bool bRes = sal_False; 840 841 uno::Reference< lang::XComponent > xComponent( xDoc, uno::UNO_QUERY ); 842 if (xComponent.is()) 843 { 844 // if the component was already used in one of the two calls to check text 845 // i.e. in startGrammarChecking or checkGrammarAtPos it will be found in the 846 // m_aDocIdMap unless the document already disposed. 847 // If it is not found then it is not yet being checked (or requested to being checked) 848 const DocMap_t::const_iterator aIt( m_aDocIdMap.find( xComponent.get() ) ); 849 if (aIt != m_aDocIdMap.end()) 850 { 851 // check in document is checked automatically in the background... 852 OUString aDocId = aIt->second; 853 if (m_aCurCheckedDocId.getLength() > 0 && m_aCurCheckedDocId == aDocId) 854 { 855 // an entry for that document was dequed and is currently being checked. 856 bRes = sal_True; 857 } 858 else 859 { 860 // we need to check if there is an entry for that document in the queue... 861 // That is the document is going to be checked sooner or later. 862 863 sal_Int32 nSize = m_aFPEntriesQueue.size(); 864 for (sal_Int32 i = 0; i < nSize && !bRes; ++i) 865 { 866 if (aDocId == m_aFPEntriesQueue[i].m_aDocId) 867 bRes = sal_True; 868 } 869 } 870 } 871 } 872 // ---- THREAD SAFE END ---- 873 874 return bRes; 875 } 876 877 878 void SAL_CALL GrammarCheckingIterator::processLinguServiceEvent( 879 const linguistic2::LinguServiceEvent& rLngSvcEvent ) 880 throw (uno::RuntimeException) 881 { 882 if (rLngSvcEvent.nEvent == linguistic2::LinguServiceEventFlags::PROOFREAD_AGAIN) 883 { 884 try 885 { 886 uno::Reference< uno::XInterface > xThis( dynamic_cast< XLinguServiceEventBroadcaster * >(this) ); 887 linguistic2::LinguServiceEvent aEvent( xThis, linguistic2::LinguServiceEventFlags::PROOFREAD_AGAIN ); 888 m_aNotifyListeners.notifyEach( 889 &linguistic2::XLinguServiceEventListener::processLinguServiceEvent, 890 aEvent); 891 } 892 catch (uno::RuntimeException &) 893 { 894 throw; 895 } 896 catch (::uno::Exception &rE) 897 { 898 (void) rE; 899 // ignore 900 DBG_WARNING1("processLinguServiceEvent: exception:\n%s", 901 OUStringToOString(rE.Message, RTL_TEXTENCODING_UTF8).getStr()); 902 } 903 } 904 } 905 906 907 sal_Bool SAL_CALL GrammarCheckingIterator::addLinguServiceEventListener( 908 const uno::Reference< linguistic2::XLinguServiceEventListener >& xListener ) 909 throw (uno::RuntimeException) 910 { 911 if (xListener.is()) 912 { 913 // ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); 914 m_aNotifyListeners.addInterface( xListener ); 915 } 916 return sal_True; 917 } 918 919 920 sal_Bool SAL_CALL GrammarCheckingIterator::removeLinguServiceEventListener( 921 const uno::Reference< linguistic2::XLinguServiceEventListener >& xListener ) 922 throw (uno::RuntimeException) 923 { 924 if (xListener.is()) 925 { 926 // ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); 927 m_aNotifyListeners.removeInterface( xListener ); 928 } 929 return sal_True; 930 } 931 932 933 void SAL_CALL GrammarCheckingIterator::dispose() 934 throw (uno::RuntimeException) 935 { 936 lang::EventObject aEvt( (linguistic2::XProofreadingIterator *) this ); 937 m_aEventListeners.disposeAndClear( aEvt ); 938 939 // 940 // now end the thread... 941 // 942 m_aRequestEndThread.reset(); 943 // ---- THREAD SAFE START ---- 944 { 945 ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); 946 m_bEnd = sal_True; 947 } 948 // ---- THREAD SAFE END ---- 949 m_aWakeUpThread.set(); 950 const TimeValue aTime = { 3, 0 }; // wait 3 seconds... 951 m_aRequestEndThread.wait( &aTime ); 952 // if the call ends because of time-out we will end anyway... 953 954 955 // ---- THREAD SAFE START ---- 956 { 957 ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); 958 959 // releaase all UNO references 960 961 m_xMSF.clear(); 962 m_xBreakIterator.clear(); 963 964 // clear containers with UNO references AND have those references released 965 GCReferences_t aTmpEmpty1; 966 DocMap_t aTmpEmpty2; 967 FPQueue_t aTmpEmpty3; 968 m_aGCReferencesByService.swap( aTmpEmpty1 ); 969 m_aDocIdMap.swap( aTmpEmpty2 ); 970 m_aFPEntriesQueue.swap( aTmpEmpty3 ); 971 } 972 // ---- THREAD SAFE END ---- 973 } 974 975 976 void SAL_CALL GrammarCheckingIterator::addEventListener( 977 const uno::Reference< lang::XEventListener >& xListener ) 978 throw (uno::RuntimeException) 979 { 980 if (xListener.is()) 981 { 982 // ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); 983 m_aEventListeners.addInterface( xListener ); 984 } 985 } 986 987 988 void SAL_CALL GrammarCheckingIterator::removeEventListener( 989 const uno::Reference< lang::XEventListener >& xListener ) 990 throw (uno::RuntimeException) 991 { 992 if (xListener.is()) 993 { 994 // ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); 995 m_aEventListeners.removeInterface( xListener ); 996 } 997 } 998 999 1000 void SAL_CALL GrammarCheckingIterator::disposing( const lang::EventObject &rSource ) 1001 throw (uno::RuntimeException) 1002 { 1003 // if the component (document) is disposing release all references 1004 //!! There is no need to remove entries from the queue that are from this document 1005 //!! since the respectives xFlatParagraphs should become invalid (isModified() == true) 1006 //!! and the call to xFlatParagraphIterator->getNextPara() will result in an empty reference. 1007 //!! And if an entry is currently checked by a grammar checker upon return the results 1008 //!! should be ignored. 1009 //!! Also GetOrCreateDocId will not use that very same Id again... 1010 //!! All of the above resulting in that we only have to get rid of the implementation pointer here. 1011 uno::Reference< lang::XComponent > xDoc( rSource.Source, uno::UNO_QUERY ); 1012 if (xDoc.is()) 1013 { 1014 // ---- THREAD SAFE START ---- 1015 ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); 1016 m_aDocIdMap.erase( xDoc.get() ); 1017 // ---- THREAD SAFE END ---- 1018 } 1019 } 1020 1021 1022 uno::Reference< util::XChangesBatch > GrammarCheckingIterator::GetUpdateAccess() const 1023 { 1024 if (!m_xUpdateAccess.is()) 1025 { 1026 try 1027 { 1028 // get configuration provider 1029 uno::Reference< lang::XMultiServiceFactory > xConfigurationProvider; 1030 uno::Reference< lang::XMultiServiceFactory > xMgr = utl::getProcessServiceFactory(); 1031 if (xMgr.is()) 1032 { 1033 xConfigurationProvider = uno::Reference< lang::XMultiServiceFactory > ( 1034 xMgr->createInstance( OUString( RTL_CONSTASCII_USTRINGPARAM( 1035 "com.sun.star.configuration.ConfigurationProvider" ) ) ), 1036 uno::UNO_QUERY_THROW ) ; 1037 } 1038 1039 // get configuration update access 1040 beans::PropertyValue aValue; 1041 aValue.Name = A2OU( "nodepath" ); 1042 aValue.Value = uno::makeAny( A2OU("org.openoffice.Office.Linguistic/ServiceManager") ); 1043 uno::Sequence< uno::Any > aProps(1); 1044 aProps[0] <<= aValue; 1045 m_xUpdateAccess = uno::Reference< util::XChangesBatch >( 1046 xConfigurationProvider->createInstanceWithArguments( 1047 A2OU( "com.sun.star.configuration.ConfigurationUpdateAccess" ), aProps ), 1048 uno::UNO_QUERY_THROW ); 1049 } 1050 catch (uno::Exception &) 1051 { 1052 } 1053 } 1054 1055 return m_xUpdateAccess; 1056 } 1057 1058 1059 void GrammarCheckingIterator::GetConfiguredGCSvcs_Impl() 1060 { 1061 GCImplNames_t aTmpGCImplNamesByLang; 1062 1063 try 1064 { 1065 // get node names (locale iso strings) for configured grammar checkers 1066 uno::Reference< container::XNameAccess > xNA( GetUpdateAccess(), uno::UNO_QUERY_THROW ); 1067 xNA.set( xNA->getByName( A2OU("GrammarCheckerList") ), uno::UNO_QUERY_THROW ); 1068 const uno::Sequence< OUString > aElementNames( xNA->getElementNames() ); 1069 const OUString *pElementNames = aElementNames.getConstArray(); 1070 1071 sal_Int32 nLen = aElementNames.getLength(); 1072 for (sal_Int32 i = 0; i < nLen; ++i) 1073 { 1074 uno::Sequence< OUString > aImplNames; 1075 uno::Any aTmp( xNA->getByName( pElementNames[i] ) ); 1076 if (aTmp >>= aImplNames) 1077 { 1078 if (aImplNames.getLength() > 0) 1079 { 1080 // only the first entry is used, there should be only one grammar checker per language 1081 const OUString aImplName( aImplNames[0] ); 1082 const LanguageType nLang = MsLangId::convertIsoStringToLanguage( pElementNames[i] ); 1083 aTmpGCImplNamesByLang[ nLang ] = aImplName; 1084 } 1085 } 1086 else 1087 { 1088 DBG_ASSERT( 0, "failed to get aImplNames. Wrong type?" ); 1089 } 1090 } 1091 } 1092 catch (uno::Exception &) 1093 { 1094 DBG_ASSERT( 0, "exception caught. Failed to get configured services" ); 1095 } 1096 1097 { 1098 // ---- THREAD SAFE START ---- 1099 ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); 1100 m_aGCImplNamesByLang = aTmpGCImplNamesByLang; 1101 // ---- THREAD SAFE END ---- 1102 } 1103 } 1104 1105 /* 1106 void GrammarCheckingIterator::GetMatchingGCSvcs_Impl() 1107 { 1108 GCImplNames_t aTmpGCImplNamesByLang; 1109 1110 try 1111 { 1112 // get node names (locale iso strings) for configured grammar checkers 1113 uno::Reference< container::XNameAccess > xNA( GetUpdateAccess(), uno::UNO_QUERY_THROW ); 1114 xNA.set( xNA->getByName( A2OU("GrammarCheckers") ), uno::UNO_QUERY_THROW ); 1115 const uno::Sequence< OUString > aGCImplNames( xNA->getElementNames() ); 1116 const OUString *pGCImplNames = aGCImplNames.getConstArray(); 1117 1118 sal_Int32 nLen = aGCImplNames.getLength(); 1119 for (sal_Int32 i = 0; i < nLen; ++i) 1120 { 1121 uno::Reference< container::XNameAccess > xTmpNA( xNA->getByName( pGCImplNames[i] ), uno::UNO_QUERY_THROW ); 1122 uno::Any aTmp( xTmpNA->getByName( A2OU("Locales") ) ); 1123 uno::Sequence< OUString > aIsoLocaleNames; 1124 if (aTmp >>= aIsoLocaleNames) 1125 { 1126 const OUString *pIsoLocaleNames = aIsoLocaleNames.getConstArray(); 1127 for (sal_Int32 k = 0; k < aIsoLocaleNames.getLength(); ++k) 1128 { 1129 // if there are more grammar checkers for one language, for the time being, 1130 // the last one found here will win... 1131 const LanguageType nLang = MsLangId::convertIsoStringToLanguage( pIsoLocaleNames[k] ); 1132 aTmpGCImplNamesByLang[ nLang ] = pGCImplNames[i]; 1133 } 1134 } 1135 else 1136 { 1137 DBG_ASSERT( 0, "failed to get aImplNames. Wrong type?" ); 1138 } 1139 } 1140 } 1141 catch (uno::Exception &) 1142 { 1143 DBG_ASSERT( 0, "exception caught. Failed to get matching grammar checker services" ); 1144 } 1145 1146 { 1147 // ---- THREAD SAFE START ---- 1148 ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); 1149 m_aGCImplNamesByLang = aTmpGCImplNamesByLang; 1150 // ---- THREAD SAFE END ---- 1151 } 1152 } 1153 */ 1154 1155 /* 1156 void GrammarCheckingIterator::GetAvailableGCSvcs_Impl() 1157 { 1158 // internal method; will always be called with locked mutex 1159 if (m_xMSF.is()) 1160 { 1161 uno::Reference< container::XContentEnumerationAccess > xEnumAccess( m_xMSF, uno::UNO_QUERY ); 1162 uno::Reference< container::XEnumeration > xEnum; 1163 if (xEnumAccess.is()) 1164 xEnum = xEnumAccess->createContentEnumeration( A2OU( SN_GRAMMARCHECKER ) ); 1165 1166 if (xEnum.is()) 1167 { 1168 while (xEnum->hasMoreElements()) 1169 { 1170 uno::Any aCurrent = xEnum->nextElement(); 1171 uno::Reference< lang::XSingleComponentFactory > xCompFactory; 1172 uno::Reference< lang::XSingleServiceFactory > xFactory; 1173 1174 uno::Reference< uno::XComponentContext > xContext; 1175 uno::Reference< beans::XPropertySet > xProps( m_xMSF, uno::UNO_QUERY ); 1176 xProps->getPropertyValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "DefaultContext" ))) >>= xContext; 1177 1178 if ( xContext.is() && 1179 (cppu::extractInterface( xCompFactory, aCurrent ) || 1180 cppu::extractInterface( xFactory, aCurrent )) ) 1181 { 1182 try 1183 { 1184 uno::Reference< linguistic2::XProofreader > xSvc( ( xCompFactory.is() ? xCompFactory->createInstanceWithContext( xContext ) : xFactory->createInstance() ), uno::UNO_QUERY ); 1185 if (xSvc.is()) 1186 { 1187 OUString aImplName; 1188 uno::Reference< XServiceInfo > xInfo( xSvc, uno::UNO_QUERY ); 1189 if (xInfo.is()) 1190 aImplName = xInfo->getImplementationName(); 1191 DBG_ASSERT( aImplName.getLength(), "empty implementation name" ); 1192 uno::Reference< linguistic2::XSupportedLocales > xSuppLoc( xSvc, uno::UNO_QUERY ); 1193 DBG_ASSERT( xSuppLoc.is(), "interfaces not supported" ); 1194 if (xSuppLoc.is() && aImplName.getLength() > 0) 1195 { 1196 uno::Sequence< lang::Locale > aLocaleSequence( xSuppLoc->getLocales() ); 1197 // ---- THREAD SAFE START ---- 1198 ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); 1199 m_aGCLocalesByService[ aImplName ] = aLocaleSequence; 1200 m_aGCReferencesByService[ aImplName ] = xSvc; 1201 // ---- THREAD SAFE END ---- 1202 } 1203 } 1204 } 1205 catch (uno::Exception &) 1206 { 1207 DBG_ASSERT( 0, "instantiating grammar checker failed" ); 1208 } 1209 } 1210 } 1211 } 1212 } 1213 } 1214 */ 1215 1216 1217 sal_Bool SAL_CALL GrammarCheckingIterator::supportsService( 1218 const OUString & rServiceName ) 1219 throw(uno::RuntimeException) 1220 { 1221 uno::Sequence< OUString > aSNL = getSupportedServiceNames(); 1222 const OUString * pArray = aSNL.getConstArray(); 1223 for( sal_Int32 i = 0; i < aSNL.getLength(); ++i ) 1224 if( pArray[i] == rServiceName ) 1225 return sal_True; 1226 return sal_False; 1227 } 1228 1229 1230 OUString SAL_CALL GrammarCheckingIterator::getImplementationName( ) throw (uno::RuntimeException) 1231 { 1232 return GrammarCheckingIterator_getImplementationName(); 1233 } 1234 1235 1236 uno::Sequence< OUString > SAL_CALL GrammarCheckingIterator::getSupportedServiceNames( ) throw (uno::RuntimeException) 1237 { 1238 return GrammarCheckingIterator_getSupportedServiceNames(); 1239 } 1240 1241 1242 void GrammarCheckingIterator::SetServiceList( 1243 const lang::Locale &rLocale, 1244 const uno::Sequence< OUString > &rSvcImplNames ) 1245 { 1246 ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); 1247 1248 LanguageType nLanguage = LocaleToLanguage( rLocale ); 1249 OUString aImplName; 1250 if (rSvcImplNames.getLength() > 0) 1251 aImplName = rSvcImplNames[0]; // there is only one grammar checker per language 1252 1253 if (nLanguage != LANGUAGE_NONE && nLanguage != LANGUAGE_DONTKNOW) 1254 { 1255 if (aImplName.getLength() > 0) 1256 m_aGCImplNamesByLang[ nLanguage ] = aImplName; 1257 else 1258 m_aGCImplNamesByLang.erase( nLanguage ); 1259 } 1260 } 1261 1262 1263 uno::Sequence< OUString > GrammarCheckingIterator::GetServiceList( 1264 const lang::Locale &rLocale ) const 1265 { 1266 ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() ); 1267 1268 uno::Sequence< OUString > aRes(1); 1269 1270 OUString aImplName; // there is only one grammar checker per language 1271 LanguageType nLang = LocaleToLanguage( rLocale ); 1272 GCImplNames_t::const_iterator aIt( m_aGCImplNamesByLang.find( nLang ) ); 1273 if (aIt != m_aGCImplNamesByLang.end()) 1274 aImplName = aIt->second; 1275 1276 if (aImplName.getLength() > 0) 1277 aRes[0] = aImplName; 1278 else 1279 aRes.realloc(0); 1280 1281 return aRes; 1282 } 1283 1284 1285 LinguDispatcher::DspType GrammarCheckingIterator::GetDspType() const 1286 { 1287 return DSP_GRAMMAR; 1288 } 1289 1290 1291 /////////////////////////////////////////////////////////////////////////// 1292 1293 1294 static OUString GrammarCheckingIterator_getImplementationName() throw() 1295 { 1296 return A2OU( "com.sun.star.lingu2.ProofreadingIterator" ); 1297 } 1298 1299 1300 static uno::Sequence< OUString > GrammarCheckingIterator_getSupportedServiceNames() throw() 1301 { 1302 uno::Sequence< OUString > aSNS( 1 ); 1303 aSNS.getArray()[0] = A2OU( SN_GRAMMARCHECKINGITERATOR ); 1304 return aSNS; 1305 } 1306 1307 1308 static uno::Reference< uno::XInterface > SAL_CALL GrammarCheckingIterator_createInstance( 1309 const uno::Reference< lang::XMultiServiceFactory > & rxSMgr ) 1310 throw(uno::Exception) 1311 { 1312 return static_cast< ::cppu::OWeakObject * >(new GrammarCheckingIterator( rxSMgr )); 1313 } 1314 1315 1316 void * SAL_CALL GrammarCheckingIterator_getFactory( 1317 const sal_Char *pImplName, 1318 lang::XMultiServiceFactory *pServiceManager, 1319 void * /*pRegistryKey*/ ) 1320 { 1321 void * pRet = 0; 1322 if ( !GrammarCheckingIterator_getImplementationName().compareToAscii( pImplName ) ) 1323 { 1324 uno::Reference< lang::XSingleServiceFactory > xFactory = 1325 cppu::createOneInstanceFactory( 1326 pServiceManager, 1327 GrammarCheckingIterator_getImplementationName(), 1328 GrammarCheckingIterator_createInstance, 1329 GrammarCheckingIterator_getSupportedServiceNames()); 1330 // acquire, because we return an interface pointer instead of a reference 1331 xFactory->acquire(); 1332 pRet = xFactory.get(); 1333 } 1334 return pRet; 1335 } 1336