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