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