xref: /trunk/main/linguistic/source/gciterator.cxx (revision b504c543a5c0014f61d361c52d736b627c675f94)
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