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