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