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