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