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 : :
30 : : #include <com/sun/star/uno/Reference.h>
31 : : #include <com/sun/star/linguistic2/XSearchableDictionaryList.hpp>
32 : : #include <com/sun/star/linguistic2/SpellFailure.hpp>
33 : : #include <com/sun/star/registry/XRegistryKey.hpp>
34 : :
35 : : #include <cppuhelper/factory.hxx> // helper for factories
36 : : #include <unotools/localedatawrapper.hxx>
37 : : #include <comphelper/processfactory.hxx>
38 : : #include <tools/debug.hxx>
39 : : #include <svl/lngmisc.hxx>
40 : : #include <osl/mutex.hxx>
41 : :
42 : : #include <vector>
43 : :
44 : : #include "spelldsp.hxx"
45 : : #include "linguistic/spelldta.hxx"
46 : : #include "lngsvcmgr.hxx"
47 : : #include "linguistic/lngprops.hxx"
48 : :
49 : : using namespace osl;
50 : : using namespace com::sun::star;
51 : : using namespace com::sun::star::beans;
52 : : using namespace com::sun::star::lang;
53 : : using namespace com::sun::star::uno;
54 : : using namespace com::sun::star::linguistic2;
55 : : using namespace linguistic;
56 : :
57 : : using ::rtl::OUString;
58 : :
59 : : // ProposalList: list of proposals for misspelled words
60 : : // The order of strings in the array should be left unchanged because the
61 : : // spellchecker should have put the more likely suggestions at the top.
62 : : // New entries will be added to the end but duplicates are to be avoided.
63 : : // Removing entries is done by assigning the empty string.
64 : : // The sequence is constructed from all non empty strings in the original
65 : : // while maintaining the order.
66 : 0 : class ProposalList
67 : : {
68 : : std::vector< OUString > aVec;
69 : :
70 : : sal_Bool HasEntry( const OUString &rText ) const;
71 : :
72 : : // make copy c-tor and assignment operator private
73 : : ProposalList( const ProposalList & );
74 : : ProposalList & operator = ( const ProposalList & );
75 : :
76 : : public:
77 : 0 : ProposalList() {}
78 : :
79 : : size_t Count() const;
80 : : void Prepend( const OUString &rText );
81 : : void Append( const OUString &rNew );
82 : : void Append( const std::vector< OUString > &rNew );
83 : : void Append( const Sequence< OUString > &rNew );
84 : : Sequence< OUString > GetSequence() const;
85 : : };
86 : :
87 : :
88 : 0 : sal_Bool ProposalList::HasEntry( const OUString &rText ) const
89 : : {
90 : 0 : sal_Bool bFound = sal_False;
91 : 0 : size_t nCnt = aVec.size();
92 [ # # ][ # # ]: 0 : for (size_t i = 0; !bFound && i < nCnt; ++i)
[ # # ]
93 : : {
94 [ # # ]: 0 : if (aVec[i] == rText)
95 : 0 : bFound = sal_True;
96 : : }
97 : 0 : return bFound;
98 : : }
99 : :
100 : 0 : void ProposalList::Prepend( const OUString &rText )
101 : : {
102 [ # # ]: 0 : if (!HasEntry( rText ))
103 : 0 : aVec.insert( aVec.begin(), rText );
104 : 0 : }
105 : :
106 : 0 : void ProposalList::Append( const OUString &rText )
107 : : {
108 [ # # ]: 0 : if (!HasEntry( rText ))
109 : 0 : aVec.push_back( rText );
110 : 0 : }
111 : :
112 : 0 : void ProposalList::Append( const std::vector< OUString > &rNew )
113 : : {
114 : 0 : size_t nLen = rNew.size();
115 [ # # ]: 0 : for ( size_t i = 0; i < nLen; ++i)
116 : : {
117 : 0 : const OUString &rText = rNew[i];
118 [ # # ]: 0 : if (!HasEntry( rText ))
119 : 0 : Append( rText );
120 : : }
121 : 0 : }
122 : :
123 : 0 : void ProposalList::Append( const Sequence< OUString > &rNew )
124 : : {
125 : 0 : sal_Int32 nLen = rNew.getLength();
126 : 0 : const OUString *pNew = rNew.getConstArray();
127 [ # # ]: 0 : for (sal_Int32 i = 0; i < nLen; ++i)
128 : : {
129 : 0 : const OUString &rText = pNew[i];
130 [ # # ]: 0 : if (!HasEntry( rText ))
131 : 0 : Append( rText );
132 : : }
133 : 0 : }
134 : :
135 : 0 : size_t ProposalList::Count() const
136 : : {
137 : : // returns the number of non-empty strings in the vector
138 : :
139 : 0 : size_t nRes = 0;
140 : 0 : size_t nLen = aVec.size();
141 [ # # ]: 0 : for (size_t i = 0; i < nLen; ++i)
142 : : {
143 [ # # ]: 0 : if (!aVec[i].isEmpty())
144 : 0 : ++nRes;
145 : : }
146 : 0 : return nRes;
147 : : }
148 : :
149 : 0 : Sequence< OUString > ProposalList::GetSequence() const
150 : : {
151 : 0 : sal_Int32 nCount = Count();
152 : 0 : sal_Int32 nIdx = 0;
153 : 0 : Sequence< OUString > aRes( nCount );
154 [ # # ]: 0 : OUString *pRes = aRes.getArray();
155 : 0 : sal_Int32 nLen = aVec.size();
156 [ # # ]: 0 : for (sal_Int32 i = 0; i < nLen; ++i)
157 : : {
158 : 0 : const OUString &rText = aVec[i];
159 : : DBG_ASSERT( nIdx < nCount, "index our of range" );
160 [ # # ][ # # ]: 0 : if (nIdx < nCount && !rText.isEmpty())
[ # # ]
161 : 0 : pRes[ nIdx++ ] = rText;
162 : : }
163 : 0 : return aRes;
164 : : }
165 : :
166 : 40 : sal_Bool SvcListHasLanguage(
167 : : const LangSvcEntries_Spell &rEntry,
168 : : LanguageType nLanguage )
169 : : {
170 : 40 : sal_Bool bHasLanguage = sal_False;
171 : 40 : Locale aTmpLocale;
172 : :
173 : 40 : const Reference< XSpellChecker > *pRef = rEntry.aSvcRefs .getConstArray();
174 : 40 : sal_Int32 nLen = rEntry.aSvcRefs.getLength();
175 [ + + ][ + - ]: 80 : for (sal_Int32 k = 0; k < nLen && !bHasLanguage; ++k)
[ + + ]
176 : : {
177 [ + - ]: 40 : if (pRef[k].is())
178 : : {
179 [ + - ]: 40 : if (aTmpLocale.Language.isEmpty())
180 [ + - ]: 40 : aTmpLocale = CreateLocale( nLanguage );
181 [ + - ][ + - ]: 40 : bHasLanguage = pRef[k]->hasLocale( aTmpLocale );
182 : : }
183 : : }
184 : :
185 : 40 : return bHasLanguage;
186 : : }
187 : :
188 : 46 : SpellCheckerDispatcher::SpellCheckerDispatcher( LngSvcMgr &rLngSvcMgr ) :
189 [ + - ][ + - ]: 46 : rMgr (rLngSvcMgr)
190 : : {
191 : 46 : pCache = NULL;
192 : 46 : }
193 : :
194 : :
195 [ + - ]: 44 : SpellCheckerDispatcher::~SpellCheckerDispatcher()
196 : : {
197 [ + - ]: 44 : ClearSvcList();
198 [ + + ][ + - ]: 44 : delete pCache;
199 [ - + ]: 88 : }
200 : :
201 : :
202 : 44 : void SpellCheckerDispatcher::ClearSvcList()
203 : : {
204 : : // release memory for each table entry
205 [ + - ]: 44 : SpellSvcByLangMap_t aTmp;
206 [ + - ]: 44 : aSvcMap.swap( aTmp );
207 : 44 : }
208 : :
209 : :
210 : 0 : Sequence< Locale > SAL_CALL SpellCheckerDispatcher::getLocales()
211 : : throw(RuntimeException)
212 : : {
213 [ # # ][ # # ]: 0 : MutexGuard aGuard( GetLinguMutex() );
214 : :
215 [ # # ]: 0 : Sequence< Locale > aLocales( static_cast< sal_Int32 >(aSvcMap.size()) );
216 [ # # ]: 0 : Locale *pLocales = aLocales.getArray();
217 : 0 : SpellSvcByLangMap_t::const_iterator aIt;
218 [ # # ]: 0 : for (aIt = aSvcMap.begin(); aIt != aSvcMap.end(); ++aIt)
219 : : {
220 [ # # ]: 0 : *pLocales++ = CreateLocale( aIt->first );
221 : : }
222 [ # # ]: 0 : return aLocales;
223 : : }
224 : :
225 : :
226 : 29241 : sal_Bool SAL_CALL SpellCheckerDispatcher::hasLocale( const Locale& rLocale )
227 : : throw(RuntimeException)
228 : : {
229 [ + - ][ + - ]: 29241 : MutexGuard aGuard( GetLinguMutex() );
230 [ + - ][ + - ]: 29241 : SpellSvcByLangMap_t::const_iterator aIt( aSvcMap.find( LocaleToLanguage( rLocale ) ) );
231 [ + - ]: 29241 : return aIt != aSvcMap.end();
232 : : }
233 : :
234 : :
235 : : sal_Bool SAL_CALL
236 : 32229 : SpellCheckerDispatcher::isValid( const OUString& rWord, const Locale& rLocale,
237 : : const PropertyValues& rProperties )
238 : : throw(IllegalArgumentException, RuntimeException)
239 : : {
240 [ + - ][ + - ]: 32229 : MutexGuard aGuard( GetLinguMutex() );
241 [ + - ][ + - ]: 32229 : return isValid_Impl( rWord, LocaleToLanguage( rLocale ), rProperties, sal_True );
[ + - ]
242 : : }
243 : :
244 : :
245 : : Reference< XSpellAlternatives > SAL_CALL
246 : 0 : SpellCheckerDispatcher::spell( const OUString& rWord, const Locale& rLocale,
247 : : const PropertyValues& rProperties )
248 : : throw(IllegalArgumentException, RuntimeException)
249 : : {
250 [ # # ][ # # ]: 0 : MutexGuard aGuard( GetLinguMutex() );
251 [ # # ][ # # ]: 0 : return spell_Impl( rWord, LocaleToLanguage( rLocale ), rProperties, sal_True );
[ # # ]
252 : : }
253 : :
254 : :
255 : : // returns the overall result of cross-checking with all user-dictionaries
256 : : // including the IgnoreAll list
257 : 32229 : static Reference< XDictionaryEntry > lcl_GetRulingDictionaryEntry(
258 : : const OUString &rWord,
259 : : LanguageType nLanguage )
260 : : {
261 : 32229 : Reference< XDictionaryEntry > xRes;
262 : :
263 : : // the order of winning from top to bottom is:
264 : : // 1) IgnoreAll list will always win
265 : : // 2) Negative dictionaries will win over positive dictionaries
266 [ + - ]: 32229 : Reference< XDictionary > xIgnoreAll( GetIgnoreAllList() );
267 [ + - ]: 32229 : if (xIgnoreAll.is())
268 [ + - ][ + - ]: 32229 : xRes = xIgnoreAll->getEntry( rWord );
[ + - ]
269 [ + - ]: 32229 : if (!xRes.is())
270 : : {
271 [ + - ]: 32229 : Reference< XDictionaryList > xDList( GetDictionaryList() );
272 : : Reference< XDictionaryEntry > xNegEntry( SearchDicList( xDList,
273 [ + - ]: 32229 : rWord, nLanguage, sal_False, sal_True ) );
274 [ - + ]: 32229 : if (xNegEntry.is())
275 [ # # ]: 0 : xRes = xNegEntry;
276 : : else
277 : : {
278 : : Reference< XDictionaryEntry > xPosEntry( SearchDicList( xDList,
279 [ + - ]: 32229 : rWord, nLanguage, sal_True, sal_True ) );
280 [ + + ]: 32229 : if (xPosEntry.is())
281 [ + - ]: 32229 : xRes = xPosEntry;
282 : 32229 : }
283 : : }
284 : :
285 : 32229 : return xRes;
286 : : }
287 : :
288 : :
289 : 32229 : sal_Bool SpellCheckerDispatcher::isValid_Impl(
290 : : const OUString& rWord,
291 : : LanguageType nLanguage,
292 : : const PropertyValues& rProperties,
293 : : sal_Bool bCheckDics)
294 : : throw( RuntimeException, IllegalArgumentException )
295 : : {
296 [ + - ][ + - ]: 32229 : MutexGuard aGuard( GetLinguMutex() );
297 : :
298 : 32229 : sal_Bool bRes = sal_True;
299 : :
300 [ + - ][ - + ]: 32229 : if (nLanguage == LANGUAGE_NONE || rWord.isEmpty())
[ - + ]
301 : 0 : return bRes;
302 : :
303 : : // search for entry with that language
304 [ + - ]: 32229 : SpellSvcByLangMap_t::iterator aIt( aSvcMap.find( nLanguage ) );
305 [ + - ]: 32229 : LangSvcEntries_Spell *pEntry = aIt != aSvcMap.end() ? aIt->second.get() : NULL;
306 : :
307 [ + - ]: 32229 : if (!pEntry)
308 : : {
309 : : #ifdef LINGU_EXCEPTIONS
310 : : throw IllegalArgumentException();
311 : : #endif
312 : : }
313 : : else
314 : : {
315 : 32229 : OUString aChkWord( rWord );
316 [ + - ]: 32229 : Locale aLocale( CreateLocale( nLanguage ) );
317 : :
318 : : // replace typographical apostroph by ascii apostroph
319 [ + - ][ + - ]: 32229 : String aSingleQuote( GetLocaleDataWrapper( nLanguage ).getQuotationMarkEnd() );
[ + - ]
320 : : DBG_ASSERT( 1 == aSingleQuote.Len(), "unexpectend length of quotation mark" );
321 [ + - ]: 32229 : if (aSingleQuote.Len())
322 : 32229 : aChkWord = aChkWord.replace( aSingleQuote.GetChar(0), '\'' );
323 : :
324 [ + - ]: 32229 : RemoveHyphens( aChkWord );
325 [ + - ][ + - ]: 32229 : if (IsIgnoreControlChars( rProperties, GetPropSet() ))
[ + - ]
326 [ + - ]: 32229 : RemoveControlChars( aChkWord );
327 : :
328 : 32229 : sal_Int32 nLen = pEntry->aSvcRefs.getLength();
329 : : DBG_ASSERT( nLen == pEntry->aSvcImplNames.getLength(),
330 : : "lng : sequence length mismatch");
331 : : DBG_ASSERT( pEntry->nLastTriedSvcIndex < nLen,
332 : : "lng : index out of range");
333 : :
334 : 32229 : sal_Int32 i = 0;
335 : 32229 : sal_Bool bTmpRes = sal_True;
336 : 32229 : sal_Bool bTmpResValid = sal_False;
337 : :
338 : : // try already instantiated services first
339 : : {
340 : : const Reference< XSpellChecker > *pRef =
341 : 32229 : pEntry->aSvcRefs.getConstArray();
342 [ + + ][ - + ]: 64418 : while (i <= pEntry->nLastTriedSvcIndex
[ # # ][ + + ]
343 : : && (!bTmpResValid || sal_False == bTmpRes))
344 : : {
345 : 32189 : bTmpResValid = sal_True;
346 [ + - ][ + - ]: 32189 : if (pRef[i].is() && pRef[i]->hasLocale( aLocale ))
[ + - ][ + - ]
[ + - ]
347 : : {
348 [ + - ][ + - ]: 32189 : bTmpRes = GetCache().CheckWord( aChkWord, nLanguage );
349 [ + + ]: 32189 : if (!bTmpRes)
350 : : {
351 [ + - ][ + - ]: 14757 : bTmpRes = pRef[i]->isValid( aChkWord, aLocale, rProperties );
352 : :
353 : : // Add correct words to the cache.
354 : : // But not those that are correct only because of
355 : : // the temporary supplied settings.
356 [ + + ][ + - ]: 14757 : if (bTmpRes && 0 == rProperties.getLength())
[ + + ]
357 [ + - ][ + - ]: 10670 : GetCache().AddWord( aChkWord, nLanguage );
358 : : }
359 : : }
360 : : else
361 : 0 : bTmpResValid = sal_False;
362 : :
363 [ + - ]: 32189 : if (bTmpResValid)
364 : 32189 : bRes = bTmpRes;
365 : :
366 : 32189 : ++i;
367 : : }
368 : : }
369 : :
370 : : // if still no result instantiate new services and try those
371 [ + + ][ + + ]: 32229 : if ((!bTmpResValid || sal_False == bTmpRes)
[ + + ]
372 : : && pEntry->nLastTriedSvcIndex < nLen - 1)
373 : : {
374 : 40 : const OUString *pImplNames = pEntry->aSvcImplNames.getConstArray();
375 [ + - ]: 40 : Reference< XSpellChecker > *pRef = pEntry->aSvcRefs .getArray();
376 : :
377 : : Reference< XMultiServiceFactory > xMgr(
378 [ + - ]: 40 : comphelper::getProcessServiceFactory() );
379 [ + - ]: 40 : if (xMgr.is())
380 : : {
381 : : // build service initialization argument
382 [ + - ]: 40 : Sequence< Any > aArgs(2);
383 [ + - ][ + - ]: 40 : aArgs.getArray()[0] <<= GetPropSet();
[ + - ]
384 : :
385 [ + + ][ - + ]: 80 : while (i < nLen && (!bTmpResValid || sal_False == bTmpRes))
[ # # ][ + + ]
386 : : {
387 : : // create specific service via it's implementation name
388 : 40 : Reference< XSpellChecker > xSpell;
389 : : try
390 : : {
391 : : xSpell = Reference< XSpellChecker >(
392 [ + - ]: 40 : xMgr->createInstanceWithArguments(
393 [ + - ][ + - ]: 40 : pImplNames[i], aArgs ), UNO_QUERY );
[ + - ][ # # ]
394 : : }
395 [ # # ]: 0 : catch (uno::Exception &)
396 : : {
397 : : DBG_ASSERT( 0, "createInstanceWithArguments failed" );
398 : : }
399 [ + - ]: 40 : pRef [i] = xSpell;
400 : :
401 : : Reference< XLinguServiceEventBroadcaster >
402 [ + - ]: 40 : xBroadcaster( xSpell, UNO_QUERY );
403 [ + - ]: 40 : if (xBroadcaster.is())
404 [ + - ]: 40 : rMgr.AddLngSvcEvtBroadcaster( xBroadcaster );
405 : :
406 : 40 : bTmpResValid = sal_True;
407 [ + - ][ + - ]: 40 : if (xSpell.is() && xSpell->hasLocale( aLocale ))
[ + - ][ + - ]
[ + - ]
408 : : {
409 [ + - ][ + - ]: 40 : bTmpRes = GetCache().CheckWord( aChkWord, nLanguage );
410 [ + - ]: 40 : if (!bTmpRes)
411 : : {
412 [ + - ][ + - ]: 40 : bTmpRes = xSpell->isValid( aChkWord, aLocale, rProperties );
413 : :
414 : : // Add correct words to the cache.
415 : : // But not those that are correct only because of
416 : : // the temporary supplied settings.
417 [ + + ][ + - ]: 40 : if (bTmpRes && 0 == rProperties.getLength())
[ + + ]
418 [ + - ][ + - ]: 31 : GetCache().AddWord( aChkWord, nLanguage );
419 : : }
420 : : }
421 : : else
422 : 0 : bTmpResValid = sal_False;
423 : :
424 [ + - ]: 40 : if (bTmpResValid)
425 : 40 : bRes = bTmpRes;
426 : :
427 : 40 : pEntry->nLastTriedSvcIndex = (sal_Int16) i;
428 : 40 : ++i;
429 : 40 : }
430 : :
431 : : // if language is not supported by any of the services
432 : : // remove it from the list.
433 [ + - ]: 40 : if (i == nLen)
434 : : {
435 [ + - ][ - + ]: 40 : if (!SvcListHasLanguage( *pEntry, nLanguage ))
436 [ # # ]: 0 : aSvcMap.erase( nLanguage );
437 [ + - ]: 40 : }
438 : 40 : }
439 : : }
440 : :
441 : : // cross-check against results from dictionaries which have precedence!
442 [ + - + - ]: 128916 : if (bCheckDics &&
[ + - ][ + - ]
443 [ + - ][ + - ]: 96687 : GetDicList().is() && IsUseDicList( rProperties, GetPropSet() ))
[ + - ][ + - ]
[ + - ]
[ # # # # ]
444 : : {
445 [ + - ]: 32229 : Reference< XDictionaryEntry > xTmp( lcl_GetRulingDictionaryEntry( aChkWord, nLanguage ) );
446 [ + + ]: 32229 : if (xTmp.is())
447 [ + - ][ + - ]: 32229 : bRes = !xTmp->isNegative();
448 [ + - ]: 32229 : }
449 : : }
450 : :
451 [ + - ]: 32229 : return bRes;
452 : : }
453 : :
454 : :
455 : 0 : Reference< XSpellAlternatives > SpellCheckerDispatcher::spell_Impl(
456 : : const OUString& rWord,
457 : : LanguageType nLanguage,
458 : : const PropertyValues& rProperties,
459 : : sal_Bool bCheckDics )
460 : : throw(IllegalArgumentException, RuntimeException)
461 : : {
462 [ # # ][ # # ]: 0 : MutexGuard aGuard( GetLinguMutex() );
463 : :
464 : 0 : Reference< XSpellAlternatives > xRes;
465 : :
466 [ # # ][ # # ]: 0 : if (nLanguage == LANGUAGE_NONE || rWord.isEmpty())
[ # # ]
467 : : return xRes;
468 : :
469 : : // search for entry with that language
470 [ # # ]: 0 : SpellSvcByLangMap_t::iterator aIt( aSvcMap.find( nLanguage ) );
471 [ # # ]: 0 : LangSvcEntries_Spell *pEntry = aIt != aSvcMap.end() ? aIt->second.get() : NULL;
472 : :
473 [ # # ]: 0 : if (!pEntry)
474 : : {
475 : : #ifdef LINGU_EXCEPTIONS
476 : : throw IllegalArgumentException();
477 : : #endif
478 : : }
479 : : else
480 : : {
481 : 0 : OUString aChkWord( rWord );
482 [ # # ]: 0 : Locale aLocale( CreateLocale( nLanguage ) );
483 : :
484 : : // replace typographical apostroph by ascii apostroph
485 [ # # ][ # # ]: 0 : String aSingleQuote( GetLocaleDataWrapper( nLanguage ).getQuotationMarkEnd() );
[ # # ]
486 : : DBG_ASSERT( 1 == aSingleQuote.Len(), "unexpectend length of quotation mark" );
487 [ # # ]: 0 : if (aSingleQuote.Len())
488 : 0 : aChkWord = aChkWord.replace( aSingleQuote.GetChar(0), '\'' );
489 : :
490 [ # # ]: 0 : RemoveHyphens( aChkWord );
491 [ # # ][ # # ]: 0 : if (IsIgnoreControlChars( rProperties, GetPropSet() ))
[ # # ]
492 [ # # ]: 0 : RemoveControlChars( aChkWord );
493 : :
494 : 0 : sal_Int32 nLen = pEntry->aSvcRefs.getLength();
495 : : DBG_ASSERT( nLen == pEntry->aSvcImplNames.getLength(),
496 : : "lng : sequence length mismatch");
497 : : DBG_ASSERT( pEntry->nLastTriedSvcIndex < nLen,
498 : : "lng : index out of range");
499 : :
500 : 0 : sal_Int32 i = 0;
501 : 0 : Reference< XSpellAlternatives > xTmpRes;
502 : 0 : sal_Bool bTmpResValid = sal_False;
503 : :
504 : : // try already instantiated services first
505 : : {
506 : 0 : const Reference< XSpellChecker > *pRef = pEntry->aSvcRefs.getConstArray();
507 : 0 : sal_Int32 nNumSugestions = -1;
508 [ # # ]: 0 : while (i <= pEntry->nLastTriedSvcIndex
[ # # # # ]
[ # # ]
509 : 0 : && (!bTmpResValid || xTmpRes.is()) )
510 : : {
511 : 0 : bTmpResValid = sal_True;
512 [ # # ][ # # ]: 0 : if (pRef[i].is() && pRef[i]->hasLocale( aLocale ))
[ # # ][ # # ]
[ # # ]
513 : : {
514 [ # # ][ # # ]: 0 : sal_Bool bOK = GetCache().CheckWord( aChkWord, nLanguage );
515 [ # # ]: 0 : if (bOK)
516 [ # # ]: 0 : xTmpRes = NULL;
517 : : else
518 : : {
519 [ # # ][ # # ]: 0 : xTmpRes = pRef[i]->spell( aChkWord, aLocale, rProperties );
[ # # ]
520 : :
521 : : // Add correct words to the cache.
522 : : // But not those that are correct only because of
523 : : // the temporary supplied settings.
524 [ # # ][ # # ]: 0 : if (!xTmpRes.is() && 0 == rProperties.getLength())
[ # # ]
525 [ # # ][ # # ]: 0 : GetCache().AddWord( aChkWord, nLanguage );
526 : : }
527 : : }
528 : : else
529 : 0 : bTmpResValid = sal_False;
530 : :
531 : : // return first found result if the word is not known by any checker.
532 : : // But if that result has no suggestions use the first one that does
533 : : // provide suggestions for the misspelled word.
534 [ # # ][ # # ]: 0 : if (!xRes.is() && bTmpResValid)
[ # # ]
535 : : {
536 [ # # ]: 0 : xRes = xTmpRes;
537 : 0 : nNumSugestions = 0;
538 [ # # ]: 0 : if (xRes.is())
539 [ # # ][ # # ]: 0 : nNumSugestions = xRes->getAlternatives().getLength();
[ # # ]
540 : : }
541 : 0 : sal_Int32 nTmpNumSugestions = 0;
542 [ # # ][ # # ]: 0 : if (xTmpRes.is() && bTmpResValid)
[ # # ]
543 [ # # ][ # # ]: 0 : nTmpNumSugestions = xTmpRes->getAlternatives().getLength();
[ # # ]
544 [ # # ][ # # ]: 0 : if (xRes.is() && nNumSugestions == 0 && nTmpNumSugestions > 0)
[ # # ][ # # ]
545 : : {
546 [ # # ]: 0 : xRes = xTmpRes;
547 : 0 : nNumSugestions = nTmpNumSugestions;
548 : : }
549 : :
550 : 0 : ++i;
551 : : }
552 : : }
553 : :
554 : : // if still no result instantiate new services and try those
555 [ # # ][ # # ]: 0 : if ((!bTmpResValid || xTmpRes.is())
[ # # ][ # # ]
556 : : && pEntry->nLastTriedSvcIndex < nLen - 1)
557 : : {
558 : 0 : const OUString *pImplNames = pEntry->aSvcImplNames.getConstArray();
559 [ # # ]: 0 : Reference< XSpellChecker > *pRef = pEntry->aSvcRefs .getArray();
560 : :
561 : : Reference< XMultiServiceFactory > xMgr(
562 [ # # ]: 0 : comphelper::getProcessServiceFactory() );
563 [ # # ]: 0 : if (xMgr.is())
564 : : {
565 : : // build service initialization argument
566 [ # # ]: 0 : Sequence< Any > aArgs(2);
567 [ # # ][ # # ]: 0 : aArgs.getArray()[0] <<= GetPropSet();
[ # # ]
568 : :
569 : 0 : sal_Int32 nNumSugestions = -1;
570 [ # # ][ # # ]: 0 : while (i < nLen && (!bTmpResValid || xTmpRes.is()))
[ # # ][ # # ]
571 : : {
572 : : // create specific service via it's implementation name
573 : 0 : Reference< XSpellChecker > xSpell;
574 : : try
575 : : {
576 : : xSpell = Reference< XSpellChecker >(
577 [ # # ]: 0 : xMgr->createInstanceWithArguments(
578 [ # # ][ # # ]: 0 : pImplNames[i], aArgs ), UNO_QUERY );
[ # # ][ # # ]
579 : : }
580 [ # # ]: 0 : catch (uno::Exception &)
581 : : {
582 : : DBG_ASSERT( 0, "createInstanceWithArguments failed" );
583 : : }
584 [ # # ]: 0 : pRef [i] = xSpell;
585 : :
586 : : Reference< XLinguServiceEventBroadcaster >
587 [ # # ]: 0 : xBroadcaster( xSpell, UNO_QUERY );
588 [ # # ]: 0 : if (xBroadcaster.is())
589 [ # # ]: 0 : rMgr.AddLngSvcEvtBroadcaster( xBroadcaster );
590 : :
591 : 0 : bTmpResValid = sal_True;
592 [ # # ][ # # ]: 0 : if (xSpell.is() && xSpell->hasLocale( aLocale ))
[ # # ][ # # ]
[ # # ]
593 : : {
594 [ # # ][ # # ]: 0 : sal_Bool bOK = GetCache().CheckWord( aChkWord, nLanguage );
595 [ # # ]: 0 : if (bOK)
596 [ # # ]: 0 : xTmpRes = NULL;
597 : : else
598 : : {
599 [ # # ][ # # ]: 0 : xTmpRes = xSpell->spell( aChkWord, aLocale, rProperties );
[ # # ]
600 : :
601 : : // Add correct words to the cache.
602 : : // But not those that are correct only because of
603 : : // the temporary supplied settings.
604 [ # # ][ # # ]: 0 : if (!xTmpRes.is() && 0 == rProperties.getLength())
[ # # ]
605 [ # # ][ # # ]: 0 : GetCache().AddWord( aChkWord, nLanguage );
606 : : }
607 : : }
608 : : else
609 : 0 : bTmpResValid = sal_False;
610 : :
611 : : // return first found result if the word is not known by any checker.
612 : : // But if that result has no suggestions use the first one that does
613 : : // provide suggestions for the misspelled word.
614 [ # # ][ # # ]: 0 : if (!xRes.is() && bTmpResValid)
[ # # ]
615 : : {
616 [ # # ]: 0 : xRes = xTmpRes;
617 : 0 : nNumSugestions = 0;
618 [ # # ]: 0 : if (xRes.is())
619 [ # # ][ # # ]: 0 : nNumSugestions = xRes->getAlternatives().getLength();
[ # # ]
620 : : }
621 : 0 : sal_Int32 nTmpNumSugestions = 0;
622 [ # # ][ # # ]: 0 : if (xTmpRes.is() && bTmpResValid)
[ # # ]
623 [ # # ][ # # ]: 0 : nTmpNumSugestions = xTmpRes->getAlternatives().getLength();
[ # # ]
624 [ # # ][ # # ]: 0 : if (xRes.is() && nNumSugestions == 0 && nTmpNumSugestions > 0)
[ # # ][ # # ]
625 : : {
626 [ # # ]: 0 : xRes = xTmpRes;
627 : 0 : nNumSugestions = nTmpNumSugestions;
628 : : }
629 : :
630 : 0 : pEntry->nLastTriedSvcIndex = (sal_Int16) i;
631 : 0 : ++i;
632 : 0 : }
633 : :
634 : : // if language is not supported by any of the services
635 : : // remove it from the list.
636 [ # # ]: 0 : if (i == nLen)
637 : : {
638 [ # # ][ # # ]: 0 : if (!SvcListHasLanguage( *pEntry, nLanguage ))
639 [ # # ]: 0 : aSvcMap.erase( nLanguage );
640 [ # # ]: 0 : }
641 : 0 : }
642 : : }
643 : :
644 : : // if word is finally found to be correct
645 : : // clear previously remembered alternatives
646 [ # # ][ # # ]: 0 : if (bTmpResValid && !xTmpRes.is())
[ # # ]
647 [ # # ]: 0 : xRes = NULL;
648 : :
649 : : // list of proposals found (to be checked against entries of
650 : : // neagtive dictionaries)
651 [ # # ]: 0 : ProposalList aProposalList;
652 : 0 : sal_Int16 eFailureType = -1; // no failure
653 [ # # ]: 0 : if (xRes.is())
654 : : {
655 [ # # ][ # # ]: 0 : aProposalList.Append( xRes->getAlternatives() );
[ # # ][ # # ]
656 [ # # ][ # # ]: 0 : eFailureType = xRes->getFailureType();
657 : : }
658 : 0 : Reference< XDictionaryList > xDList;
659 [ # # ][ # # ]: 0 : if (GetDicList().is() && IsUseDicList( rProperties, GetPropSet() ))
[ # # ][ # # ]
[ # # ][ # # ]
[ # # # #
# # ][ # # ]
660 [ # # ][ # # ]: 0 : xDList = Reference< XDictionaryList >( GetDicList(), UNO_QUERY );
[ # # ]
661 : :
662 : : // cross-check against results from user-dictionaries which have precedence!
663 [ # # ][ # # ]: 0 : if (bCheckDics && xDList.is())
[ # # ]
664 : : {
665 [ # # ]: 0 : Reference< XDictionaryEntry > xTmp( lcl_GetRulingDictionaryEntry( aChkWord, nLanguage ) );
666 [ # # ]: 0 : if (xTmp.is())
667 : : {
668 [ # # ][ # # ]: 0 : if (xTmp->isNegative()) // positive entry found
[ # # ]
669 : : {
670 : 0 : eFailureType = SpellFailure::IS_NEGATIVE_WORD;
671 : :
672 : : // replacement text to be added to suggestions, if not empty
673 [ # # ][ # # ]: 0 : OUString aAddRplcTxt( xTmp->getReplacementText() );
674 : :
675 : : // replacement text must not be in negative dictionary itself
676 [ # # # # ]: 0 : if (!aAddRplcTxt.isEmpty() &&
[ # # ]
677 [ # # ][ # # ]: 0 : !SearchDicList( xDList, aAddRplcTxt, nLanguage, sal_False, sal_True ).is())
[ # # ]
678 : : {
679 [ # # ]: 0 : aProposalList.Prepend( aAddRplcTxt );
680 : 0 : }
681 : : }
682 : : else // positive entry found
683 : : {
684 [ # # ]: 0 : xRes = NULL;
685 : 0 : eFailureType = -1; // no failure
686 : : }
687 : 0 : }
688 : : }
689 : :
690 [ # # ]: 0 : if (eFailureType != -1) // word misspelled or found in negative user-dictionary
691 : : {
692 : : // search suitable user-dictionaries for suggestions that are
693 : : // similar to the misspelled word
694 [ # # ]: 0 : std::vector< OUString > aDicListProps; // list of proposals from user-dictionaries
695 [ # # ]: 0 : SearchSimilarText( aChkWord, nLanguage, xDList, aDicListProps );
696 [ # # ]: 0 : aProposalList.Append( aDicListProps );
697 [ # # ]: 0 : Sequence< OUString > aProposals = aProposalList.GetSequence();
698 : :
699 : : // remove entries listed in negative dictionaries
700 : : // (we don't want to display suggestions that will be regarded as misspelledlater on)
701 [ # # ][ # # ]: 0 : if (bCheckDics && xDList.is())
[ # # ]
702 [ # # ]: 0 : SeqRemoveNegEntries( aProposals, xDList, nLanguage );
703 : :
704 [ # # ]: 0 : uno::Reference< linguistic2::XSetSpellAlternatives > xSetAlt( xRes, uno::UNO_QUERY );
705 [ # # ]: 0 : if (xSetAlt.is())
706 : : {
707 [ # # ][ # # ]: 0 : xSetAlt->setAlternatives( aProposals );
708 [ # # ][ # # ]: 0 : xSetAlt->setFailureType( eFailureType );
709 : : }
710 : : else
711 : : {
712 [ # # ]: 0 : if (xRes.is())
713 : : {
714 : : DBG_ASSERT( 0, "XSetSpellAlternatives not implemented!" );
715 : : }
716 [ # # ]: 0 : else if (aProposals.getLength() > 0)
717 : : {
718 : : // no xRes but Proposals found from the user-dictionaries.
719 : : // Thus we need to create an xRes...
720 : : xRes = new linguistic::SpellAlternatives( rWord, nLanguage,
721 [ # # ][ # # ]: 0 : SpellFailure::IS_NEGATIVE_WORD, aProposals );
[ # # ]
722 : : }
723 [ # # ]: 0 : }
724 [ # # ]: 0 : }
725 : : }
726 : :
727 [ # # ]: 0 : return xRes;
728 : : }
729 : :
730 : 0 : uno::Sequence< sal_Int16 > SAL_CALL SpellCheckerDispatcher::getLanguages( )
731 : : throw (uno::RuntimeException)
732 : : {
733 [ # # ][ # # ]: 0 : MutexGuard aGuard( GetLinguMutex() );
734 [ # # ]: 0 : uno::Sequence< Locale > aTmp( getLocales() );
735 [ # # ]: 0 : uno::Sequence< sal_Int16 > aRes( LocaleSeqToLangSeq( aTmp ) );
736 [ # # ][ # # ]: 0 : return aRes;
737 : : }
738 : :
739 : :
740 : 29241 : sal_Bool SAL_CALL SpellCheckerDispatcher::hasLanguage(
741 : : sal_Int16 nLanguage )
742 : : throw (uno::RuntimeException)
743 : : {
744 [ + - ][ + - ]: 29241 : MutexGuard aGuard( GetLinguMutex() );
745 [ + - ]: 29241 : Locale aLocale( CreateLocale( nLanguage ) );
746 [ + - ][ + - ]: 29241 : return hasLocale( aLocale );
747 : : }
748 : :
749 : :
750 : 32229 : sal_Bool SAL_CALL SpellCheckerDispatcher::isValid(
751 : : const OUString& rWord,
752 : : sal_Int16 nLanguage,
753 : : const uno::Sequence< beans::PropertyValue >& rProperties )
754 : : throw (lang::IllegalArgumentException, uno::RuntimeException)
755 : : {
756 [ + - ][ + - ]: 32229 : MutexGuard aGuard( GetLinguMutex() );
757 [ + - ]: 32229 : Locale aLocale( CreateLocale( nLanguage ) );
758 [ + - ][ + - ]: 32229 : return isValid( rWord, aLocale, rProperties);
759 : : }
760 : :
761 : :
762 : 0 : uno::Reference< linguistic2::XSpellAlternatives > SAL_CALL SpellCheckerDispatcher::spell(
763 : : const OUString& rWord,
764 : : sal_Int16 nLanguage,
765 : : const uno::Sequence< beans::PropertyValue >& rProperties )
766 : : throw (lang::IllegalArgumentException, uno::RuntimeException)
767 : : {
768 [ # # ][ # # ]: 0 : MutexGuard aGuard( GetLinguMutex() );
769 [ # # ]: 0 : Locale aLocale( CreateLocale( nLanguage ) );
770 [ # # ][ # # ]: 0 : return spell( rWord, aLocale, rProperties);
771 : : }
772 : :
773 : :
774 : 2064 : void SpellCheckerDispatcher::SetServiceList( const Locale &rLocale,
775 : : const Sequence< OUString > &rSvcImplNames )
776 : : {
777 [ + - ][ + - ]: 2064 : MutexGuard aGuard( GetLinguMutex() );
778 : :
779 [ + + ]: 2064 : if (pCache)
780 [ + - ]: 86 : pCache->Flush(); // new services may spell differently...
781 : :
782 [ + - ]: 2064 : sal_Int16 nLanguage = LocaleToLanguage( rLocale );
783 : :
784 : 2064 : sal_Int32 nLen = rSvcImplNames.getLength();
785 [ - + ]: 2064 : if (0 == nLen)
786 : : // remove entry
787 [ # # ]: 0 : aSvcMap.erase( nLanguage );
788 : : else
789 : : {
790 : : // modify/add entry
791 [ + - ]: 2064 : LangSvcEntries_Spell *pEntry = aSvcMap[ nLanguage ].get();
792 [ + + ]: 2064 : if (pEntry)
793 : : {
794 [ + - ]: 86 : pEntry->Clear();
795 [ + - ]: 86 : pEntry->aSvcImplNames = rSvcImplNames;
796 [ + - ][ + - ]: 86 : pEntry->aSvcRefs = Sequence< Reference < XSpellChecker > > ( nLen );
[ + - ]
797 : : }
798 : : else
799 : : {
800 [ + - ][ + - ]: 1978 : boost::shared_ptr< LangSvcEntries_Spell > pTmpEntry( new LangSvcEntries_Spell( rSvcImplNames ) );
[ + - ]
801 [ + - ][ + - ]: 1978 : pTmpEntry->aSvcRefs = Sequence< Reference < XSpellChecker > >( nLen );
[ + - ]
802 [ + - ][ + - ]: 1978 : aSvcMap[ nLanguage ] = pTmpEntry;
[ + - ]
803 : : }
804 [ + - ]: 2064 : }
805 : 2064 : }
806 : :
807 : :
808 : : Sequence< OUString >
809 : 0 : SpellCheckerDispatcher::GetServiceList( const Locale &rLocale ) const
810 : : {
811 [ # # ][ # # ]: 0 : MutexGuard aGuard( GetLinguMutex() );
812 : :
813 [ # # ]: 0 : Sequence< OUString > aRes;
814 : :
815 : : // search for entry with that language and use data from that
816 [ # # ]: 0 : sal_Int16 nLanguage = LocaleToLanguage( rLocale );
817 : 0 : SpellCheckerDispatcher *pThis = (SpellCheckerDispatcher *) this;
818 [ # # ]: 0 : const SpellSvcByLangMap_t::iterator aIt( pThis->aSvcMap.find( nLanguage ) );
819 [ # # ]: 0 : const LangSvcEntries_Spell *pEntry = aIt != aSvcMap.end() ? aIt->second.get() : NULL;
820 [ # # ]: 0 : if (pEntry)
821 [ # # ]: 0 : aRes = pEntry->aSvcImplNames;
822 : :
823 [ # # ]: 0 : return aRes;
824 : : }
825 : :
826 : :
827 : 0 : LinguDispatcher::DspType SpellCheckerDispatcher::GetDspType() const
828 : : {
829 : 0 : return DSP_SPELL;
830 : : }
831 : :
832 : 2 : void SpellCheckerDispatcher::FlushSpellCache()
833 : : {
834 [ + - ]: 2 : if (pCache)
835 : 2 : pCache->Flush();
836 : 2 : }
837 : :
838 : : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|