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