LCOV - code coverage report
Current view: top level - linguistic/source - spelldsp.cxx (source / functions) Hit Total Coverage
Test: commit c8344322a7af75b84dd3ca8f78b05543a976dfd5 Lines: 151 400 37.8 %
Date: 2015-06-13 12:38:46 Functions: 14 31 45.2 %
Legend: Lines: hit not hit

          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: */

Generated by: LCOV version 1.11