diff options
Diffstat (limited to 'linguistic/source/spelldsp.cxx')
-rw-r--r-- | linguistic/source/spelldsp.cxx | 733 |
1 files changed, 733 insertions, 0 deletions
diff --git a/linguistic/source/spelldsp.cxx b/linguistic/source/spelldsp.cxx new file mode 100644 index 000000000000..729d224f1793 --- /dev/null +++ b/linguistic/source/spelldsp.cxx @@ -0,0 +1,733 @@ +/************************************************************************* + * + * $RCSfile: spelldsp.cxx,v $ + * + * $Revision: 1.1.1.1 $ + * + * last change: $Author: hr $ $Date: 2000-11-17 12:37:42 $ + * + * The Contents of this file are made available subject to the terms of + * either of the following licenses + * + * - GNU Lesser General Public License Version 2.1 + * - Sun Industry Standards Source License Version 1.1 + * + * Sun Microsystems Inc., October, 2000 + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2000 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * + * Sun Industry Standards Source License Version 1.1 + * ================================================= + * The contents of this file are subject to the Sun Industry Standards + * Source License Version 1.1 (the "License"); You may not use this file + * except in compliance with the License. You may obtain a copy of the + * License at http://www.openoffice.org/license.html. + * + * Software provided under this License is provided on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, + * WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS, + * MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING. + * See the License for the specific provisions governing your rights and + * obligations concerning the Software. + * + * The Initial Developer of the Original Code is: Sun Microsystems, Inc. + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * All Rights Reserved. + * + * Contributor(s): _______________________________________ + * + * + ************************************************************************/ + +#ifndef _COM_SUN_STAR_UNO_REFERENCE_H_ +#include <com/sun/star/uno/Reference.h> +#endif +#ifndef _COM_SUN_STAR_LINGUISTIC2_XSEARCHABLEDICTIONARYLIST_HPP_ +#include <com/sun/star/linguistic2/XSearchableDictionaryList.hpp> +#endif + +#include <com/sun/star/linguistic2/SpellFailure.hpp> +#include <cppuhelper/factory.hxx> // helper for factories +#include <com/sun/star/registry/XRegistryKey.hpp> + +#ifndef _TOOLS_DEBUG_HXX //autogen wg. DBG_ASSERT +#include <tools/debug.hxx> +#endif +#ifndef _UNOTOOLS_PROCESSFACTORY_HXX_ +#include <unotools/processfactory.hxx> +#endif + +#ifndef _SPELLIMP_HXX +#include <spelldsp.hxx> +#endif +#ifndef _LNGPROPS_HXX +#include <lngprops.hxx> +#endif + +#include "spelldsp.hxx" +#include "spelldta.hxx" +#include "lngsvcmgr.hxx" + +#ifndef _OSL_MUTEX_HXX_ +#include <osl/mutex.hxx> +#endif + + +using namespace utl; +using namespace osl; +using namespace rtl; +using namespace com::sun::star; +using namespace com::sun::star::beans; +using namespace com::sun::star::lang; +using namespace com::sun::star::uno; +using namespace com::sun::star::linguistic2; +using namespace linguistic; + + +/////////////////////////////////////////////////////////////////////////// + + +SeqLangSvcEntry_Spell::~SeqLangSvcEntry_Spell() +{ +} + + +SeqLangSvcEntry_Spell::SeqLangSvcEntry_Spell( + const Sequence< OUString > &rSvcImplNames ) : + aSvcImplNames ( rSvcImplNames ), + aSvcRefs ( rSvcImplNames.getLength() ), + aSvc1Refs ( rSvcImplNames.getLength() ) +{ +} + +/////////////////////////////////////////////////////////////////////////// + +SpellCheckerDispatcher::SpellCheckerDispatcher( LngSvcMgr &rLngSvcMgr ) : + rMgr (rLngSvcMgr) +{ + pExtCache = NULL; +} + + +SpellCheckerDispatcher::~SpellCheckerDispatcher() +{ + ClearSvcList(); + delete pExtCache; +} + + +void SpellCheckerDispatcher::ClearSvcList() +{ + // release memory for each table entry + SeqLangSvcEntry_Spell *pItem = aSvcList.First(); + while (pItem) + { + SeqLangSvcEntry_Spell *pTmp = pItem; + pItem = aSvcList.Next(); + delete pTmp; + } +} + + +Sequence< Locale > SAL_CALL SpellCheckerDispatcher::getLocales() + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + return LangSeqToLocaleSeq( getLanguages() ); +} + + +sal_Bool SAL_CALL SpellCheckerDispatcher::hasLocale( const Locale& rLocale ) + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + return hasLanguage( LocaleToLanguage(rLocale) ); +} + + +sal_Bool SAL_CALL + SpellCheckerDispatcher::isValid( const OUString& rWord, const Locale& rLocale, + const PropertyValues& rProperties ) + throw(IllegalArgumentException, RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + return isValid( rWord, LocaleToLanguage(rLocale), rProperties ); +} + + +Reference< XSpellAlternatives > SAL_CALL + SpellCheckerDispatcher::spell( const OUString& rWord, const Locale& rLocale, + const PropertyValues& rProperties ) + throw(IllegalArgumentException, RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + return spell( rWord, LocaleToLanguage(rLocale), rProperties ); +} + + +Sequence< sal_Int16 > SAL_CALL SpellCheckerDispatcher::getLanguages() + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + ULONG nCnt = aSvcList.Count(); + Sequence< INT16 > aLanguages( nCnt ); + INT16 *pLang = aLanguages.getArray(); + SeqLangSvcEntry_Spell *pEntry = aSvcList.First(); + for (ULONG i = 0; i < nCnt; i++) + { + DBG_ASSERT( pEntry, "lng : pEntry is NULL pointer" ); + pLang[i] = aSvcList.GetKey( pEntry ); + pEntry = aSvcList.Next(); + } + return aLanguages; +} + + +sal_Bool SAL_CALL SpellCheckerDispatcher::hasLanguage( sal_Int16 nLanguage ) + throw(RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + return 0 != aSvcList.Seek( nLanguage ); +} + + +sal_Bool SAL_CALL + SpellCheckerDispatcher::isValid( const OUString& rWord, sal_Int16 nLanguage, + const PropertyValues& rProperties ) + throw(IllegalArgumentException, RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + BOOL bRes = TRUE; + if (LANGUAGE_NONE != nLanguage && rWord.getLength()) + { + if (aOpt.IsSpellInAllLanguages()) + bRes = isValidInAny( rWord, getLanguages(), rProperties ); + else + bRes = isValid_Impl( rWord, nLanguage, rProperties, TRUE ); + } + return bRes; +} + + +BOOL SpellCheckerDispatcher::isValidInAny( + const OUString& rWord, + const Sequence< INT16 >& aLanguages, + const PropertyValues& rProperties ) + throw( RuntimeException, IllegalArgumentException ) +{ + MutexGuard aGuard( GetLinguMutex() ); + + BOOL bRes = TRUE; + + INT32 nNumLang = aLanguages.getLength(); + const INT16 *pLang = aLanguages.getConstArray(); + BOOL bCheckDics = TRUE; + for (int i = 0; i < nNumLang; i++) + { + // Bug 71632 + if( LANGUAGE_NONE != pLang[i] ) + { + if ((bRes = isValid_Impl( rWord, pLang[i], rProperties, bCheckDics ))) + break; + bCheckDics = FALSE; + } + } + + return bRes; +} + + +BOOL SpellCheckerDispatcher::isValid_Impl( + const OUString& rWord, + INT16 nLanguage, + const PropertyValues& rProperties, + BOOL bCheckDics) + throw( RuntimeException, IllegalArgumentException ) +{ + MutexGuard aGuard( GetLinguMutex() ); + + BOOL bRes = TRUE; + + if (nLanguage == LANGUAGE_NONE || !rWord.getLength()) + return bRes; + + // search for entry with that language + SeqLangSvcEntry_Spell *pEntry = aSvcList.Seek( nLanguage ); + + if (!pEntry) + { +#ifdef LINGU_EXCEPTIONS + throw IllegalArgumentException(); +#endif + } + else + { + INT32 nLen = pEntry->aSvcRefs.getLength(); + DBG_ASSERT( nLen = pEntry->aSvcImplNames.getLength(), + "lng : sequence length mismatch"); + DBG_ASSERT( pEntry->aFlags.nLastTriedSvcIndex < nLen, + "lng : index out of range"); + + INT32 i = 0; + BOOL bTmpRes = TRUE; + BOOL bTmpResValid = FALSE; + + // try already instantiated services first + { + const Reference< XSpellChecker1 > *pRef1 = + pEntry->aSvc1Refs.getConstArray(); + const Reference< XSpellChecker > *pRef = + pEntry->aSvcRefs.getConstArray(); + while (i <= pEntry->aFlags.nLastTriedSvcIndex + && (!bTmpResValid || FALSE == bTmpRes)) + { + bTmpResValid = TRUE; + if (pRef1[i].is()) + bTmpRes = pRef1[i]->isValid( rWord, nLanguage, rProperties ); + else if (pRef[i].is()) + { + bTmpRes = GetExtCache().CheckWord( rWord, nLanguage, FALSE ); + if (!bTmpRes) + { + bTmpRes = pRef[i]->isValid( rWord, + CreateLocale( nLanguage ), rProperties ); + + // Add correct words to the cache. + // But not those that are correct only because of + // the temporary supplied settings. + if (bTmpRes && 0 == rProperties.getLength()) + GetExtCache().AddWord( rWord, nLanguage ); + } + } + else + bTmpResValid = FALSE; + + if (bTmpResValid) + bRes = bTmpRes; + + ++i; + } + } + + // if still no result instantiate new services and try those + if ((!bTmpResValid || FALSE == bTmpRes) + && pEntry->aFlags.nLastTriedSvcIndex < nLen - 1) + { + const OUString *pImplNames = pEntry->aSvcImplNames.getConstArray(); + Reference< XSpellChecker1 > *pRef1 = pEntry->aSvc1Refs.getArray(); + Reference< XSpellChecker > *pRef = pEntry->aSvcRefs .getArray(); + + Reference< XMultiServiceFactory > xMgr( getProcessServiceFactory() ); + if (xMgr.is()) + { + // build service initialization argument + Sequence< Any > aArgs(2); + aArgs.getArray()[0] <<= GetPropSet(); + //! The dispatcher searches the dictionary-list + //! thus the service needs not to now about it + //aArgs.getArray()[1] <<= GetDicList(); + + while (i < nLen && (!bTmpResValid || FALSE == bTmpRes)) + { + // create specific service via it's implementation name + Reference< XSpellChecker > xSpell( + xMgr->createInstanceWithArguments( + pImplNames[i], aArgs ), + UNO_QUERY ); + Reference< XSpellChecker1 > xSpell1( xSpell, UNO_QUERY ); + pRef [i] = xSpell; + pRef1[i] = xSpell1; + + Reference< XLinguServiceEventBroadcaster > + xBroadcaster( xSpell, UNO_QUERY ); + if (xBroadcaster.is()) + rMgr.AddLngSvcEvtBroadcaster( xBroadcaster ); + + bTmpResValid = TRUE; + if (xSpell1.is()) + bTmpRes = xSpell1->isValid( rWord, nLanguage, rProperties ); + else if (xSpell.is()) + { + bTmpRes = GetExtCache().CheckWord( rWord, nLanguage, FALSE ); + if (!bTmpRes) + { + bTmpRes = xSpell->isValid( rWord, + CreateLocale( nLanguage ), rProperties ); + + // Add correct words to the cache. + // But not those that are correct only because of + // the temporary supplied settings. + if (bTmpRes && 0 == rProperties.getLength()) + GetExtCache().AddWord( rWord, nLanguage ); + } + } + else + bTmpResValid = FALSE; + + if (bTmpResValid) + bRes = bTmpRes; + + pEntry->aFlags.nLastTriedSvcIndex = i; + ++i; + } + } + } + } + + // countercheck against results from dictionary which have precedence! + if (bCheckDics && + GetDicList().is() && IsUseDicList( rProperties, GetPropSet() )) + { + BOOL bIsWordOk = bRes; + + Reference< XDictionaryList > xDicList( GetDicList(), UNO_QUERY ); + Reference< XDictionaryEntry > xEntry( SearchDicList( xDicList, + rWord, nLanguage, !bIsWordOk, TRUE ) ); + if (xEntry.is()) + bRes = !bIsWordOk; + } + + return bRes; +} + + +Reference< XSpellAlternatives > SAL_CALL + SpellCheckerDispatcher::spell( const OUString& rWord, sal_Int16 nLanguage, + const PropertyValues& rProperties ) + throw(IllegalArgumentException, RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + Reference< XSpellAlternatives > xAlt; + if (LANGUAGE_NONE != nLanguage && rWord.getLength()) + { + if (aOpt.IsSpellInAllLanguages()) + xAlt = spellInAny( rWord, getLanguages(), rProperties, nLanguage ); + else + xAlt = spell_Impl( rWord, nLanguage, rProperties, TRUE ); + } + return xAlt; +} + + +Reference< XSpellAlternatives > SpellCheckerDispatcher::spellInAny( + const OUString& aWord, + const Sequence< INT16 >& aLanguages, + const PropertyValues& rProperties, + INT16 nPreferredResultLang) + throw( RuntimeException, IllegalArgumentException ) +{ + MutexGuard aGuard( GetLinguMutex() ); + + Reference< XSpellAlternatives > xAlt; + + // check preferred language first + BOOL bPrefLangChecked = FALSE; + if ( LANGUAGE_NONE != nPreferredResultLang + && hasLanguage( nPreferredResultLang ) ) + { + xAlt = spell_Impl( aWord, nPreferredResultLang, rProperties, TRUE ); + bPrefLangChecked = TRUE; + } + + // if word is incorrect (or not checked) try the other languages + if (xAlt.is() || !bPrefLangChecked) + { + INT32 nNumLang = aLanguages.getLength(); + const INT16 *pLang = aLanguages.getConstArray(); + for (int i = 0; i < nNumLang; ++i, ++pLang ) + { + if (pLang[i] == nPreferredResultLang) // already checked! + continue; + + // Bug 71632 + if( LANGUAGE_NONE != pLang[i] && hasLanguage( pLang[i] ) ) + { + Reference< XSpellAlternatives > + xLast( spell_Impl( aWord, pLang[i], rProperties, TRUE )); + + // remember first spelling alternatives found + if (xLast.is() && !xAlt.is()) + xAlt = xLast; + + // did we finally find a language in which the word is correct? + if (!xLast.is()) + { + xAlt = NULL; // don't return any spelling alternatives + break; + } + } + } + } + + return xAlt; +} + + +Reference< XSpellAlternatives > SpellCheckerDispatcher::spell_Impl( + const OUString& rWord, + sal_Int16 nLanguage, + const PropertyValues& rProperties, + BOOL bCheckDics ) + throw(IllegalArgumentException, RuntimeException) +{ + MutexGuard aGuard( GetLinguMutex() ); + + Reference< XSpellAlternatives > xRes; + + if (nLanguage == LANGUAGE_NONE || !rWord.getLength()) + return xRes; + + // search for entry with that language + SeqLangSvcEntry_Spell *pEntry = aSvcList.Seek( nLanguage ); + + if (!pEntry) + { +#ifdef LINGU_EXCEPTIONS + throw IllegalArgumentException(); +#endif + } + else + { + INT32 nLen = pEntry->aSvcRefs.getLength(); + DBG_ASSERT( nLen = pEntry->aSvcImplNames.getLength(), + "lng : sequence length mismatch"); + DBG_ASSERT( pEntry->aFlags.nLastTriedSvcIndex < nLen, + "lng : index out of range"); + + INT32 i = 0; + Reference< XSpellAlternatives > xTmpRes; + BOOL bTmpResValid = FALSE; + + // try already instantiated services first + { + const Reference< XSpellChecker1 > *pRef1 = + pEntry->aSvc1Refs.getConstArray(); + const Reference< XSpellChecker > *pRef = + pEntry->aSvcRefs.getConstArray(); + while (i <= pEntry->aFlags.nLastTriedSvcIndex + && (!bTmpResValid || xTmpRes.is()) ) + { + bTmpResValid = TRUE; + if (pRef1[i].is()) + xTmpRes = pRef1[i]->spell( rWord, nLanguage, rProperties ); + else if (pRef[i].is()) + { + BOOL bOK = GetExtCache().CheckWord( rWord, nLanguage, FALSE ); + if (bOK) + xTmpRes = NULL; + else + { + xTmpRes = pRef[i]->spell( rWord, + CreateLocale( nLanguage ), rProperties ); + + // Add correct words to the cache. + // But not those that are correct only because of + // the temporary supplied settings. + if (!xTmpRes.is() && 0 == rProperties.getLength()) + GetExtCache().AddWord( rWord, nLanguage ); + } + } + else + bTmpResValid = FALSE; + + // remember first found alternatives only + if (!xRes.is() && bTmpResValid) + xRes = xTmpRes; + + ++i; + } + } + + // if still no result instantiate new services and try those + if ((!bTmpResValid || xTmpRes.is()) + && pEntry->aFlags.nLastTriedSvcIndex < nLen - 1) + { + const OUString *pImplNames = pEntry->aSvcImplNames.getConstArray(); + Reference< XSpellChecker1 > *pRef1 = pEntry->aSvc1Refs.getArray(); + Reference< XSpellChecker > *pRef = pEntry->aSvcRefs .getArray(); + + Reference< XMultiServiceFactory > xMgr( getProcessServiceFactory() ); + if (xMgr.is()) + { + // build service initialization argument + Sequence< Any > aArgs(2); + aArgs.getArray()[0] <<= GetPropSet(); + //! The dispatcher searches the dictionary-list + //! thus the service needs not to now about it + //aArgs.getArray()[1] <<= GetDicList(); + + while (i < nLen && (!bTmpResValid || xTmpRes.is())) + { + // create specific service via it's implementation name + Reference< XSpellChecker > xSpell( + xMgr->createInstanceWithArguments( + pImplNames[i], aArgs ), + UNO_QUERY ); + Reference< XSpellChecker1 > xSpell1( xSpell, UNO_QUERY ); + pRef [i] = xSpell; + pRef1[i] = xSpell1; + + Reference< XLinguServiceEventBroadcaster > + xBroadcaster( xSpell, UNO_QUERY ); + if (xBroadcaster.is()) + rMgr.AddLngSvcEvtBroadcaster( xBroadcaster ); + + bTmpResValid = TRUE; + if (xSpell1.is()) + xTmpRes = xSpell1->spell( rWord, nLanguage, rProperties ); + else if (xSpell.is()) + { + BOOL bOK = GetExtCache().CheckWord( rWord, nLanguage, FALSE ); + if (bOK) + xTmpRes = NULL; + else + { + xTmpRes = xSpell->spell( rWord, + CreateLocale( nLanguage ), rProperties ); + + // Add correct words to the cache. + // But not those that are correct only because of + // the temporary supplied settings. + if (!xTmpRes.is() && 0 == rProperties.getLength()) + GetExtCache().AddWord( rWord, nLanguage ); + } + } + else + bTmpResValid = FALSE; + + // remember first found alternatives only + if (!xRes.is() && bTmpResValid) + xRes = xTmpRes; + + pEntry->aFlags.nLastTriedSvcIndex = i; + ++i; + } + } + } + + // if word is finally found to be correct + // clear previously remembered alternatives + if (bTmpResValid && !xTmpRes.is()) + xRes = NULL; + } + + // countercheck against results from dictionary which have precedence! + if (bCheckDics && + GetDicList().is() && IsUseDicList( rProperties, GetPropSet() )) + { + BOOL bIsWordOk = !xRes.is(); + + Reference< XDictionaryList > xDicList( GetDicList(), UNO_QUERY ); + Reference< XDictionaryEntry > xEntry( SearchDicList( xDicList, + rWord, nLanguage, !bIsWordOk, TRUE ) ); + + OUString aRplcTxt; + BOOL bAddAlternative = FALSE; + + if (bIsWordOk && xEntry.is()) // negative entry found + { + aRplcTxt = xEntry->getReplacementText(); + bAddAlternative = TRUE; + } + else if (!bIsWordOk) + { + if (xEntry.is()) // positive entry found + xRes = NULL; + else + { + // search for negative entry to provide additional alternative + xEntry = SearchDicList( xDicList, + rWord, nLanguage, FALSE, TRUE ); + if (xEntry.is()) + { + aRplcTxt = xEntry->getReplacementText(); + bAddAlternative = TRUE; + } + } + } + + // (additional) alternative found? + if (bAddAlternative) + { + Reference< XSpellAlternatives > xAlt( + new SpellAlternatives( rWord, nLanguage, + SpellFailure::IS_NEGATIVE_WORD, aRplcTxt ) ); + xRes = MergeProposals( xAlt, xRes ); + } + } + + return xRes; +} + + +void SpellCheckerDispatcher::SetServiceList( const Locale &rLocale, + const Sequence< OUString > &rSvcImplNames ) +{ + MutexGuard aGuard( GetLinguMutex() ); + + if (pExtCache) + pExtCache->Flush(); // new services may spell differently... + + // search for entry with that language + INT16 nLanguage = LocaleToLanguage( rLocale ); + SeqLangSvcEntry_Spell *pEntry = aSvcList.Seek( nLanguage ); + + if (pEntry) + { + INT32 nLen = rSvcImplNames.getLength(); + pEntry->aSvcImplNames = rSvcImplNames; + pEntry->aSvcRefs = Sequence< Reference < XSpellChecker > > ( nLen ); + pEntry->aSvc1Refs = Sequence< Reference < XSpellChecker1 > >( nLen ); + pEntry->aFlags = SvcFlags(); + } + else + { + pEntry = new SeqLangSvcEntry_Spell( rSvcImplNames ); + aSvcList.Insert( nLanguage, pEntry ); + DBG_ASSERT( aSvcList.Seek( nLanguage ), "lng : Insert failed" ); + } +} + + +Sequence< OUString > + SpellCheckerDispatcher::GetServiceList( const Locale &rLocale ) +{ + MutexGuard aGuard( GetLinguMutex() ); + + Sequence< OUString > aRes; + + // search for entry with that language and use data from that + INT16 nLanguage = LocaleToLanguage( rLocale ); + SeqLangSvcEntry_Spell *pEntry = aSvcList.Seek( nLanguage ); + if (pEntry) + aRes = pEntry->aSvcImplNames; + + return aRes; +} + + +/////////////////////////////////////////////////////////////////////////// + |