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