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

Generated by: LCOV version 1.10