/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 .
*/
// ProposalList: list of proposals for misspelled words // The order of strings in the array should be left unchanged because the // spellchecker should have put the more likely suggestions at the top. // New entries will be added to the end but duplicates are to be avoided. // Removing entries is done by assigning the empty string. // The sequence is constructed from all non empty strings in the original // while maintaining the order. class ProposalList
{
std::vector< OUString > aVec;
sal_Bool SAL_CALL
SpellCheckerDispatcher::isValid( const OUString& rWord, const Locale& rLocale, const css::uno::Sequence< ::css::beans::PropertyValue >& rProperties )
{
MutexGuard aGuard( GetLinguMutex() ); // for historical reasons, the word can be only with ASCII apostrophe in the dictionaries, // so as a fallback, convert typographical apostrophes to avoid annoying users, if they // have old (user) dictionaries only with the obsolete ASCII apostrophe. bool bConvert = false; bool bRet = isValid_Impl( rWord, LinguLocaleToLanguage( rLocale ), rProperties, bConvert ); if (!bRet && bConvert)
{ // fallback: convert the apostrophes
bRet = isValid_Impl( rWord, LinguLocaleToLanguage( rLocale ), rProperties, bConvert );
} return bRet;
}
// returns the overall result of cross-checking with all user-dictionaries // including the IgnoreAll list static Reference< XDictionaryEntry > lcl_GetRulingDictionaryEntry( const OUString &rWord,
LanguageType nLanguage )
{
Reference< XDictionaryEntry > xRes;
// the order of winning from top to bottom is: // 1) IgnoreAll list will always win // 2) Negative dictionaries will win over positive dictionaries
Reference< XDictionary > xIgnoreAll( GetIgnoreAllList() ); if (xIgnoreAll.is())
xRes = xIgnoreAll->getEntry( rWord ); if (!xRes.is())
{
Reference< XSearchableDictionaryList > xDList( GetDictionaryList() );
Reference< XDictionaryEntry > xNegEntry( SearchDicList( xDList,
rWord, nLanguage, false, true ) ); if (xNegEntry.is())
xRes = std::move(xNegEntry); else
{
Reference< XDictionaryEntry > xPosEntry( SearchDicList( xDList,
rWord, nLanguage, true, true ) ); if (xPosEntry.is())
xRes = std::move(xPosEntry);
}
}
if (LinguIsUnspecified( nLanguage) || rWord.isEmpty()) return bRes;
// search for entry with that language
SpellSvcByLangMap_t::iterator aIt( m_aSvcMap.find( nLanguage ) );
LangSvcEntries_Spell *pEntry = aIt != m_aSvcMap.end() ? aIt->second.get() : nullptr;
// replace typographical apostrophe by ASCII apostrophe only as a fallback // for old user dictionaries before the time of the default typographical apostrophe // (Note: otherwise also no problem with non-Unicode Hunspell dictionaries, because // the character conversion converts also the typographical apostrophe to the ASCII one)
OUString aSingleQuote( GetLocaleDataWrapper( nLanguage ).getQuotationMarkEnd() );
DBG_ASSERT( 1 == aSingleQuote.getLength(), "unexpected length of quotation mark" ); if (!aSingleQuote.isEmpty() && aChkWord.indexOf(aSingleQuote[0]) > -1)
{ // tdf#150582 first check with the original typographical apostrophe, // and convert it only on the second try if (rConvertApostrophe)
aChkWord = aChkWord.replace( aSingleQuote[0], '\'' ); else
rConvertApostrophe = true;
}
// try already instantiated services first
{ const Reference< XSpellChecker > *pRef =
pEntry->aSvcRefs.getConstArray(); while (i <= pEntry->nLastTriedSvcIndex
&& (!bTmpResValid || !bTmpRes))
{
bTmpResValid = true; if (pRef[i].is() && pRef[i]->hasLocale( aLocale ))
{
bTmpRes = GetCache().CheckWord( aChkWord, nLanguage ); if (!bTmpRes)
{
bTmpRes = pRef[i]->isValid( aChkWord, aLocale, rProperties );
// Add correct words to the cache. // But not those that are correct only because of // the temporary supplied settings. if (bTmpRes && !rProperties.hasElements())
GetCache().AddWord( aChkWord, nLanguage );
}
} else
bTmpResValid = false;
if (bTmpResValid)
bRes = bTmpRes;
++i;
}
}
// if still no result instantiate new services and try those if ((!bTmpResValid || !bTmpRes)
&& pEntry->nLastTriedSvcIndex < nLen - 1)
{ const OUString *pImplNames = pEntry->aSvcImplNames.getConstArray();
Reference< XSpellChecker > *pRef = pEntry->aSvcRefs .getArray();
bTmpResValid = true; if (xSpell.is() && xSpell->hasLocale( aLocale ))
{
bTmpRes = GetCache().CheckWord( aChkWord, nLanguage ); if (!bTmpRes)
{
bTmpRes = xSpell->isValid( aChkWord, aLocale, rProperties ); // Add correct words to the cache. // But not those that are correct only because of // the temporary supplied settings. if (bTmpRes && !rProperties.hasElements())
GetCache().AddWord( aChkWord, nLanguage );
}
} else
bTmpResValid = false; if (bTmpResValid)
bRes = bTmpRes;
// if language is not supported by any of the services // remove it from the list. if (i == nLen)
{ if (!SvcListHasLanguage( *pEntry, nLanguage ))
m_aSvcMap.erase( nLanguage );
}
}
// cross-check against results from dictionaries which have precedence! if (GetDicList().is() && IsUseDicList( rProperties, GetPropSet() ))
{
Reference< XDictionaryEntry > xTmp( lcl_GetRulingDictionaryEntry( aChkWord, nLanguage ) ); if (xTmp.is()) {
bRes = !xTmp->isNegative();
} else {
setCharClass(LanguageTag(nLanguage));
CapType ct = capitalType(aChkWord, m_oCharClass ? &*m_oCharClass : nullptr); if (ct == CapType::INITCAP || ct == CapType::ALLCAP) {
Reference< XDictionaryEntry > xTmp2( lcl_GetRulingDictionaryEntry( makeLowerCase(aChkWord, m_oCharClass), nLanguage ) ); if (xTmp2.is()) {
bRes = !xTmp2->isNegative();
}
}
}
}
}
if (LinguIsUnspecified( nLanguage) || rWord.isEmpty()) return nullptr;
// search for entry with that language
SpellSvcByLangMap_t::iterator aIt( m_aSvcMap.find( nLanguage ) );
LangSvcEntries_Spell *pEntry = aIt != m_aSvcMap.end() ? aIt->second.get() : nullptr;
// try already instantiated services first
{ const Reference< XSpellChecker > *pRef = pEntry->aSvcRefs.getConstArray();
sal_Int32 nNumSuggestions = -1; while (i <= pEntry->nLastTriedSvcIndex
&& (!bTmpResValid || xTmpRes.is()) )
{
bTmpResValid = true; if (pRef[i].is() && pRef[i]->hasLocale( aLocale ))
{ bool bOK = GetCache().CheckWord( aChkWord, nLanguage ); if (bOK)
xTmpRes = nullptr; else
{
xTmpRes = pRef[i]->spell( aChkWord, aLocale, rProperties );
// Add correct words to the cache. // But not those that are correct only because of // the temporary supplied settings. if (!xTmpRes.is() && !rProperties.hasElements())
GetCache().AddWord( aChkWord, nLanguage );
}
} else
bTmpResValid = false;
// return first found result if the word is not known by any checker. // But if that result has no suggestions use the first one that does // provide suggestions for the misspelled word. if (!xRes.is() && bTmpResValid)
{
xRes = xTmpRes;
nNumSuggestions = 0; if (xRes.is())
nNumSuggestions = xRes->getAlternatives().getLength();
}
sal_Int32 nTmpNumSuggestions = 0; if (xTmpRes.is() && bTmpResValid)
nTmpNumSuggestions = xTmpRes->getAlternatives().getLength(); if (xRes.is() && nNumSuggestions == 0 && nTmpNumSuggestions > 0)
{
xRes = xTmpRes;
nNumSuggestions = nTmpNumSuggestions;
}
++i;
}
}
// if still no result instantiate new services and try those if ((!bTmpResValid || xTmpRes.is())
&& pEntry->nLastTriedSvcIndex < nLen - 1)
{ const OUString *pImplNames = pEntry->aSvcImplNames.getConstArray();
Reference< XSpellChecker > *pRef = pEntry->aSvcRefs .getArray();
// Add correct words to the cache. // But not those that are correct only because of // the temporary supplied settings. if (!xTmpRes.is() && !rProperties.hasElements())
GetCache().AddWord( aChkWord, nLanguage );
}
} else
bTmpResValid = false;
// return first found result if the word is not known by any checker. // But if that result has no suggestions use the first one that does // provide suggestions for the misspelled word. if (!xRes.is() && bTmpResValid)
{
xRes = xTmpRes;
nNumSuggestions = 0; if (xRes.is())
nNumSuggestions = xRes->getAlternatives().getLength();
}
sal_Int32 nTmpNumSuggestions = 0; if (xTmpRes.is() && bTmpResValid)
nTmpNumSuggestions = xTmpRes->getAlternatives().getLength(); if (xRes.is() && nNumSuggestions == 0 && nTmpNumSuggestions > 0)
{
xRes = xTmpRes;
nNumSuggestions = nTmpNumSuggestions;
}
// if language is not supported by any of the services // remove it from the list. if (i == nLen)
{ if (!SvcListHasLanguage( *pEntry, nLanguage ))
m_aSvcMap.erase( nLanguage );
}
}
// if word is finally found to be correct // clear previously remembered alternatives if (bTmpResValid && !xTmpRes.is())
xRes = nullptr;
// list of proposals found (to be checked against entries of // negative dictionaries)
ProposalList aProposalList;
sal_Int16 eFailureType = -1; // no failure if (xRes.is())
{
aProposalList.Append( xRes->getAlternatives() );
eFailureType = xRes->getFailureType();
}
Reference< XSearchableDictionaryList > xDList; if (GetDicList().is() && IsUseDicList( rProperties, GetPropSet() ))
xDList = GetDicList();
// cross-check against results from user-dictionaries which have precedence! if (xDList.is())
{
Reference< XDictionaryEntry > xTmp( lcl_GetRulingDictionaryEntry( aChkWord, nLanguage ) ); if (xTmp.is())
{ if (xTmp->isNegative()) // negative entry found
{
eFailureType = SpellFailure::IS_NEGATIVE_WORD;
// replacement text to be added to suggestions, if not empty
OUString aAddRplcTxt( xTmp->getReplacementText() );
// replacement text must not be in negative dictionary itself if (!aAddRplcTxt.isEmpty() &&
!SearchDicList( xDList, aAddRplcTxt, nLanguage, false, true ).is())
{
aProposalList.Prepend( aAddRplcTxt );
}
} else// positive entry found
{
xRes = nullptr;
eFailureType = -1; // no failure
}
} else
{
setCharClass(LanguageTag(nLanguage));
CapType ct = capitalType(aChkWord, m_oCharClass ? &*m_oCharClass : nullptr); if (ct == CapType::INITCAP || ct == CapType::ALLCAP)
{
Reference< XDictionaryEntry > xTmp2( lcl_GetRulingDictionaryEntry( makeLowerCase(aChkWord, m_oCharClass), nLanguage ) ); if (xTmp2.is())
{ if (xTmp2->isNegative()) // negative entry found
{
eFailureType = SpellFailure::IS_NEGATIVE_WORD;
// replacement text to be added to suggestions, if not empty
OUString aAddRplcTxt( xTmp2->getReplacementText() );
// replacement text must not be in negative dictionary itself if (!aAddRplcTxt.isEmpty() &&
!SearchDicList( xDList, aAddRplcTxt, nLanguage, false, true ).is())
{ switch ( ct )
{ case CapType::INITCAP:
aProposalList.Prepend( m_oCharClass->titlecase(aAddRplcTxt) ); break; case CapType::ALLCAP:
aProposalList.Prepend( m_oCharClass->uppercase(aAddRplcTxt) ); break; default: /* can't happen because of if ct == above */ break;
}
}
} else// positive entry found
{
xRes = nullptr;
eFailureType = -1; // no failure
}
}
}
}
}
if (eFailureType != -1) // word misspelled or found in negative user-dictionary
{ // search suitable user-dictionaries for suggestions that are // similar to the misspelled word
std::vector< OUString > aDicListProps; // list of proposals from user-dictionaries
SearchSimilarText( aChkWord, nLanguage, xDList, aDicListProps );
aProposalList.Append( aDicListProps );
std::vector< OUString > aProposals = aProposalList.GetVector();
// remove entries listed in negative dictionaries // (we don't want to display suggestions that will be regarded as misspelled later on) if (xDList.is())
SeqRemoveNegEntries( aProposals, xDList, nLanguage );
uno::Reference< linguistic2::XSetSpellAlternatives > xSetAlt( xRes, uno::UNO_QUERY ); if (xSetAlt.is())
{
xSetAlt->setAlternatives( comphelper::containerToSequence(aProposals) );
xSetAlt->setFailureType( eFailureType );
} else
{ if (xRes.is())
{
SAL_WARN( "linguistic", "XSetSpellAlternatives not implemented!" );
} elseif (!aProposals.empty())
{ // no xRes but Proposals found from the user-dictionaries. // Thus we need to create an xRes...
xRes = new linguistic::SpellAlternatives( rWord, nLanguage,
comphelper::containerToSequence(aProposals) );
}
}
}
// search for entry with that language and use data from that
LanguageType nLanguage = LinguLocaleToLanguage( rLocale ); const SpellSvcByLangMap_t::const_iterator aIt( m_aSvcMap.find( nLanguage ) ); const LangSvcEntries_Spell *pEntry = aIt != m_aSvcMap.end() ? aIt->second.get() : nullptr; if (pEntry)
aRes = pEntry->aSvcImplNames;
return aRes;
}
void SpellCheckerDispatcher::FlushSpellCache()
{ if (m_pCache)
m_pCache->Flush();
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.