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
lcl_IsWhiteSpace(sal_Unicode cChar)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
lcl_SkipWhiteSpaces(const OUString & rText,sal_Int32 nStartPos)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
lcl_BacktraceWhiteSpaces(const OUString & rText,sal_Int32 nStartPos)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
workerfunc(void * gci)228 extern "C" void workerfunc (void * gci)
229 {
230 ((GrammarCheckingIterator*)gci)->DequeueAndCheck();
231 }
232
lcl_GetPrimaryLanguageOfSentence(uno::Reference<text::XFlatParagraph> xFlatPara,sal_Int32 nStartIndex)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
GrammarCheckingIterator(const uno::Reference<lang::XMultiServiceFactory> & rxMgr)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
~GrammarCheckingIterator()287 GrammarCheckingIterator::~GrammarCheckingIterator()
288 {
289 ::osl::Guard< ::osl::Mutex > aGuard( MyMutex::get() );
290 }
291
292
NextDocId()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
GetOrCreateDocId(const uno::Reference<lang::XComponent> & xComponent)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
AddEntry(uno::WeakReference<text::XFlatParagraphIterator> xFlatParaIterator,uno::WeakReference<text::XFlatParagraph> xFlatPara,const OUString & rDocId,sal_Int32 nStartIndex,sal_Bool bAutomatic)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
ProcessResult(const linguistic2::ProofreadingResult & rRes,const uno::Reference<text::XFlatParagraphIterator> & rxFlatParagraphIterator,bool bIsAutomaticChecking)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
GetGrammarChecker(const lang::Locale & rLocale)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
DequeueAndCheck()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
startProofreading(const uno::Reference<::uno::XInterface> & xDoc,const uno::Reference<text::XFlatParagraphIteratorProvider> & xIteratorProvider)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
checkSentenceAtPosition(const uno::Reference<uno::XInterface> & xDoc,const uno::Reference<text::XFlatParagraph> & xFlatPara,const OUString & rText,const lang::Locale & rLocale,sal_Int32 nStartOfSentencePos,sal_Int32 nSuggestedEndOfSentencePos,sal_Int32 nErrorPosInPara)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
GetSuggestedEndOfSentence(const OUString & rText,sal_Int32 nSentenceStartPos,const lang::Locale & rLocale)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
resetIgnoreRules()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
isProofreading(const uno::Reference<uno::XInterface> & xDoc)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
processLinguServiceEvent(const linguistic2::LinguServiceEvent & rLngSvcEvent)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
addLinguServiceEventListener(const uno::Reference<linguistic2::XLinguServiceEventListener> & xListener)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
removeLinguServiceEventListener(const uno::Reference<linguistic2::XLinguServiceEventListener> & xListener)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
dispose()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
addEventListener(const uno::Reference<lang::XEventListener> & xListener)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
removeEventListener(const uno::Reference<lang::XEventListener> & xListener)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
disposing(const lang::EventObject & rSource)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
GetUpdateAccess() const1018 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
GetConfiguredGCSvcs_Impl()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
supportsService(const OUString & rServiceName)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
getImplementationName()1226 OUString SAL_CALL GrammarCheckingIterator::getImplementationName( ) throw (uno::RuntimeException)
1227 {
1228 return GrammarCheckingIterator_getImplementationName();
1229 }
1230
1231
getSupportedServiceNames()1232 uno::Sequence< OUString > SAL_CALL GrammarCheckingIterator::getSupportedServiceNames( ) throw (uno::RuntimeException)
1233 {
1234 return GrammarCheckingIterator_getSupportedServiceNames();
1235 }
1236
1237
SetServiceList(const lang::Locale & rLocale,const uno::Sequence<OUString> & rSvcImplNames)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
GetServiceList(const lang::Locale & rLocale) const1259 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
GetDspType() const1281 LinguDispatcher::DspType GrammarCheckingIterator::GetDspType() const
1282 {
1283 return DSP_GRAMMAR;
1284 }
1285
1286
1287 ///////////////////////////////////////////////////////////////////////////
1288
1289
GrammarCheckingIterator_getImplementationName()1290 static OUString GrammarCheckingIterator_getImplementationName() throw()
1291 {
1292 return A2OU( "com.sun.star.lingu2.ProofreadingIterator" );
1293 }
1294
1295
GrammarCheckingIterator_getSupportedServiceNames()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
GrammarCheckingIterator_createInstance(const uno::Reference<lang::XMultiServiceFactory> & rxSMgr)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
GrammarCheckingIterator_getFactory(const sal_Char * pImplName,lang::XMultiServiceFactory * pServiceManager,void *)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