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

Generated by: LCOV version 1.10