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