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