use more concrete UNO classes in linguistic

Change-Id: Iae9dc3c7dadc21e3d92192a197f42c55e590fab1
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/182434
Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
Tested-by: Jenkins
This commit is contained in:
Noel Grandin 2025-03-03 15:50:11 +02:00
parent 835fcbca7a
commit 9865922ff1
5 changed files with 448 additions and 453 deletions

View File

@ -29,6 +29,7 @@
#include <cppuhelper/implbase.hxx>
#include <i18nlangtag/lang.h>
#include <rtl/ustring.hxx>
#include <rtl/ref.hxx>
#include <linguistic/lngdllapi.h>
#include <vector>
@ -45,7 +46,6 @@ namespace osl { class Mutex; }
class CharClass;
class LocaleDataWrapper;
inline constexpr OUString SN_GRAMMARCHECKER = u"com.sun.star.linguistic2.Proofreader"_ustr;
inline constexpr OUString SN_SPELLCHECKER = u"com.sun.star.linguistic2.SpellChecker"_ustr;
inline constexpr OUString SN_HYPHENATOR = u"com.sun.star.linguistic2.Hyphenator"_ustr;
@ -54,6 +54,7 @@ inline constexpr OUString SN_THESAURUS = u"com.sun.star.linguistic2.Thesaurus"_u
namespace linguistic
{
class HyphenatedWord;
// AddEntryToDic return values
@ -122,7 +123,7 @@ LNG_DLLPUBLIC OUString GetWritableDictionaryURL( std::u16string_view rDicName )
LNG_DLLPUBLIC sal_Int32 GetPosInWordToCheck( std::u16string_view rTxt, sal_Int32 nPos );
css::uno::Reference< css::linguistic2::XHyphenatedWord >
rtl::Reference< HyphenatedWord >
RebuildHyphensAndControlChars(
const OUString &rOrigWord,
css::uno::Reference< css::linguistic2::XHyphenatedWord > const &rxHyphWord );

View File

@ -72,172 +72,169 @@ void HyphenatorDispatcher::ClearSvcList()
}
Reference<XHyphenatedWord> HyphenatorDispatcher::buildHyphWord(
rtl::Reference< HyphenatedWord > HyphenatorDispatcher::buildHyphWord(
const OUString& rOrigWord,
const Reference<XDictionaryEntry> &xEntry,
LanguageType nLang, sal_Int16 nMaxLeading )
{
MutexGuard aGuard( GetLinguMutex() );
Reference< XHyphenatedWord > xRes;
if (!xEntry.is())
return nullptr;
if (xEntry.is())
OUString aText( xEntry->getDictionaryWord() );
sal_Int32 nTextLen = aText.getLength();
// trailing '=' means "hyphenation should not be possible"
if (nTextLen <= 0 || aText[ nTextLen - 1 ] == '=' || aText[ nTextLen - 1 ] == '[')
return nullptr;
sal_Int16 nHyphenationPos = -1;
sal_Int16 nOrigHyphPos = -1;
OUStringBuffer aTmp( nTextLen );
bool bSkip = false;
bool bSkip2 = false;
sal_Int32 nHyphIdx = -1;
sal_Int32 nLeading = 0;
for (sal_Int32 i = 0; i < nTextLen; i++)
{
OUString aText( xEntry->getDictionaryWord() );
sal_Int32 nTextLen = aText.getLength();
// trailing '=' means "hyphenation should not be possible"
if (nTextLen > 0 && aText[ nTextLen - 1 ] != '=' && aText[ nTextLen - 1 ] != '[')
sal_Unicode cTmp = aText[i];
if (cTmp == '[' || cTmp == ']')
bSkip2 = !bSkip2;
if (cTmp != '=' && !bSkip2 && cTmp != ']')
{
sal_Int16 nHyphenationPos = -1;
sal_Int16 nOrigHyphPos = -1;
OUStringBuffer aTmp( nTextLen );
bool bSkip = false;
bool bSkip2 = false;
sal_Int32 nHyphIdx = -1;
sal_Int32 nLeading = 0;
for (sal_Int32 i = 0; i < nTextLen; i++)
aTmp.append( cTmp );
nLeading++;
bSkip = false;
nHyphIdx++;
}
else
{
if (!bSkip && nHyphIdx >= 0)
{
sal_Unicode cTmp = aText[i];
if (cTmp == '[' || cTmp == ']')
bSkip2 = !bSkip2;
if (cTmp != '=' && !bSkip2 && cTmp != ']')
{
aTmp.append( cTmp );
nLeading++;
bSkip = false;
nHyphIdx++;
}
else
{
if (!bSkip && nHyphIdx >= 0)
{
if (nLeading <= nMaxLeading) {
nHyphenationPos = static_cast<sal_Int16>(nHyphIdx);
nOrigHyphPos = i;
}
}
bSkip = true; //! multiple '=' should count as one only
if (nLeading <= nMaxLeading) {
nHyphenationPos = static_cast<sal_Int16>(nHyphIdx);
nOrigHyphPos = i;
}
}
bSkip = true; //! multiple '=' should count as one only
}
}
if (nHyphenationPos <= 0)
return nullptr;
if (nHyphenationPos > 0)
{
#if OSL_DEBUG_LEVEL > 0
{
if (std::u16string_view(aTmp) != rOrigWord)
{
// both words should only differ by a having a trailing '.'
// character or not...
std::u16string_view aShorter(aTmp), aLonger(rOrigWord);
if (aTmp.getLength() > rOrigWord.getLength())
std::swap(aShorter, aLonger);
sal_Int32 nS = aShorter.size();
sal_Int32 nL = aLonger.size();
if (nS > 0 && nL > 0)
{
assert( ((nS + 1 == nL) && aLonger[nL-1] == '.') && "HyphenatorDispatcher::buildHyphWord: unexpected difference between words!" );
}
}
}
#endif
sal_Int32 nHyphenPos = -1;
if (aText[ nOrigHyphPos ] == '[') // alternative hyphenation
{
sal_Int16 split = 0;
sal_Unicode c = aText [ nOrigHyphPos + 1 ];
sal_Int32 endhyphpat = aText.indexOf( ']', nOrigHyphPos );
if ('0' <= c && c <= '9')
{
split = c - '0';
nOrigHyphPos++;
}
if (endhyphpat > -1)
{
OUStringBuffer aTmp2 ( aTmp.copy(0, std::max (nHyphenationPos + 1 - split, 0) ) );
aTmp2.append( aText.subView( nOrigHyphPos + 1, endhyphpat - nOrigHyphPos - 1) );
nHyphenPos = aTmp2.getLength();
aTmp2.append( aTmp.subView( nHyphenationPos + 1 ) );
//! take care of #i22591#
if (rOrigWord[ rOrigWord.getLength() - 1 ] == '.')
aTmp2.append( '.' );
aText = aTmp2.makeStringAndClear();
}
}
if (nHyphenPos == -1)
aText = rOrigWord;
xRes = new HyphenatedWord( rOrigWord, nLang, nHyphenationPos,
aText, (nHyphenPos > -1) ? nHyphenPos - 1 : nHyphenationPos);
{
if (std::u16string_view(aTmp) != rOrigWord)
{
// both words should only differ by a having a trailing '.'
// character or not...
std::u16string_view aShorter(aTmp), aLonger(rOrigWord);
if (aTmp.getLength() > rOrigWord.getLength())
std::swap(aShorter, aLonger);
sal_Int32 nS = aShorter.size();
sal_Int32 nL = aLonger.size();
if (nS > 0 && nL > 0)
{
assert( ((nS + 1 == nL) && aLonger[nL-1] == '.') && "HyphenatorDispatcher::buildHyphWord: unexpected difference between words!" );
}
}
}
#endif
sal_Int32 nHyphenPos = -1;
if (aText[ nOrigHyphPos ] == '[') // alternative hyphenation
{
sal_Int16 split = 0;
sal_Unicode c = aText [ nOrigHyphPos + 1 ];
sal_Int32 endhyphpat = aText.indexOf( ']', nOrigHyphPos );
if ('0' <= c && c <= '9')
{
split = c - '0';
nOrigHyphPos++;
}
if (endhyphpat > -1)
{
OUStringBuffer aTmp2 ( aTmp.copy(0, std::max (nHyphenationPos + 1 - split, 0) ) );
aTmp2.append( aText.subView( nOrigHyphPos + 1, endhyphpat - nOrigHyphPos - 1) );
nHyphenPos = aTmp2.getLength();
aTmp2.append( aTmp.subView( nHyphenationPos + 1 ) );
//! take care of #i22591#
if (rOrigWord[ rOrigWord.getLength() - 1 ] == '.')
aTmp2.append( '.' );
aText = aTmp2.makeStringAndClear();
}
}
if (nHyphenPos == -1)
aText = rOrigWord;
rtl::Reference< HyphenatedWord > xRes = new HyphenatedWord( rOrigWord, nLang, nHyphenationPos,
aText, (nHyphenPos > -1) ? nHyphenPos - 1 : nHyphenationPos);
return xRes;
}
Reference< XPossibleHyphens > HyphenatorDispatcher::buildPossHyphens(
rtl::Reference<PossibleHyphens> HyphenatorDispatcher::buildPossHyphens(
const Reference< XDictionaryEntry > &xEntry, LanguageType nLanguage )
{
MutexGuard aGuard( GetLinguMutex() );
Reference<XPossibleHyphens> xRes;
if (!xEntry.is())
return nullptr;
if (xEntry.is())
// text with hyphenation info
OUString aText( xEntry->getDictionaryWord() );
sal_Int32 nTextLen = aText.getLength();
// trailing '=' means "hyphenation should not be possible"
if (nTextLen <= 0 || aText[ nTextLen - 1 ] == '=' || aText[ nTextLen - 1 ] == '[')
return nullptr;
// sequence to hold hyphenation positions
Sequence< sal_Int16 > aHyphPos( nTextLen );
sal_Int16 *pPos = aHyphPos.getArray();
sal_Int32 nHyphCount = 0;
OUStringBuffer aTmp( nTextLen );
bool bSkip = false;
bool bSkip2 = false;
sal_Int32 nHyphIdx = -1;
for (sal_Int32 i = 0; i < nTextLen; i++)
{
// text with hyphenation info
OUString aText( xEntry->getDictionaryWord() );
sal_Int32 nTextLen = aText.getLength();
// trailing '=' means "hyphenation should not be possible"
if (nTextLen > 0 && aText[ nTextLen - 1 ] != '=' && aText[ nTextLen - 1 ] != '[')
sal_Unicode cTmp = aText[i];
if (cTmp == '[' || cTmp == ']')
bSkip2 = !bSkip2;
if (cTmp != '=' && !bSkip2 && cTmp != ']')
{
// sequence to hold hyphenation positions
Sequence< sal_Int16 > aHyphPos( nTextLen );
sal_Int16 *pPos = aHyphPos.getArray();
sal_Int32 nHyphCount = 0;
OUStringBuffer aTmp( nTextLen );
bool bSkip = false;
bool bSkip2 = false;
sal_Int32 nHyphIdx = -1;
for (sal_Int32 i = 0; i < nTextLen; i++)
{
sal_Unicode cTmp = aText[i];
if (cTmp == '[' || cTmp == ']')
bSkip2 = !bSkip2;
if (cTmp != '=' && !bSkip2 && cTmp != ']')
{
aTmp.append( cTmp );
bSkip = false;
nHyphIdx++;
}
else
{
if (!bSkip && nHyphIdx >= 0)
pPos[ nHyphCount++ ] = static_cast<sal_Int16>(nHyphIdx);
bSkip = true; //! multiple '=' should count as one only
}
}
// ignore (multiple) trailing '='
if (bSkip && nHyphIdx >= 0)
{
nHyphCount--;
}
DBG_ASSERT( nHyphCount >= 0, "lng : invalid hyphenation count");
if (nHyphCount > 0)
{
aHyphPos.realloc( nHyphCount );
xRes = new PossibleHyphens( aTmp.makeStringAndClear(), nLanguage,
aText, aHyphPos );
}
aTmp.append( cTmp );
bSkip = false;
nHyphIdx++;
}
else
{
if (!bSkip && nHyphIdx >= 0)
pPos[ nHyphCount++ ] = static_cast<sal_Int16>(nHyphIdx);
bSkip = true; //! multiple '=' should count as one only
}
}
// ignore (multiple) trailing '='
if (bSkip && nHyphIdx >= 0)
{
nHyphCount--;
}
DBG_ASSERT( nHyphCount >= 0, "lng : invalid hyphenation count");
if (nHyphCount <= 0)
return nullptr;
aHyphPos.realloc( nHyphCount );
rtl::Reference<PossibleHyphens> xRes = new PossibleHyphens( aTmp.makeStringAndClear(), nLanguage,
aText, aHyphPos );
return xRes;
}

View File

@ -32,7 +32,7 @@
#include "defs.hxx"
class LngSvcMgr;
namespace linguistic { class HyphenatedWord; class PossibleHyphens; }
class HyphenatorDispatcher :
public cppu::WeakImplHelper
@ -60,12 +60,12 @@ class HyphenatorDispatcher :
void ClearSvcList();
static css::uno::Reference< css::linguistic2::XHyphenatedWord>
static rtl::Reference< linguistic::HyphenatedWord >
buildHyphWord( const OUString& rOrigWord,
const css::uno::Reference< css::linguistic2::XDictionaryEntry> &xEntry,
LanguageType nLang, sal_Int16 nMaxLeading );
static css::uno::Reference< css::linguistic2::XPossibleHyphens >
static rtl::Reference< linguistic::PossibleHyphens >
buildPossHyphens( const css::uno::Reference< css::linguistic2::XDictionaryEntry > &xEntry,
LanguageType nLanguage );

View File

@ -498,63 +498,60 @@ sal_Int32 GetPosInWordToCheck( std::u16string_view rTxt, sal_Int32 nPos )
return nRes;
}
uno::Reference< XHyphenatedWord > RebuildHyphensAndControlChars(
rtl::Reference< HyphenatedWord > RebuildHyphensAndControlChars(
const OUString &rOrigWord,
uno::Reference< XHyphenatedWord > const &rxHyphWord )
{
uno::Reference< XHyphenatedWord > xRes;
if (!rOrigWord.isEmpty() && rxHyphWord.is())
if (rOrigWord.isEmpty() || !rxHyphWord.is())
return nullptr;
sal_Int16 nChgPos = 0,
nChgLen = 0;
OUString aRplc;
bool bAltSpelling = GetAltSpelling( nChgPos, nChgLen, aRplc, rxHyphWord );
OUString aOrigHyphenatedWord;
sal_Int16 nOrigHyphenPos = -1;
sal_Int16 nOrigHyphenationPos = -1;
if (!bAltSpelling)
{
sal_Int16 nChgPos = 0,
nChgLen = 0;
OUString aRplc;
bool bAltSpelling = GetAltSpelling( nChgPos, nChgLen, aRplc, rxHyphWord );
OUString aOrigHyphenatedWord;
sal_Int16 nOrigHyphenPos = -1;
sal_Int16 nOrigHyphenationPos = -1;
if (!bAltSpelling)
{
aOrigHyphenatedWord = rOrigWord;
nOrigHyphenPos = GetOrigWordPos( rOrigWord, rxHyphWord->getHyphenPos() );
nOrigHyphenationPos = GetOrigWordPos( rOrigWord, rxHyphWord->getHyphenationPos() );
}
else
{
//! should at least work with the German words
//! B-"u-c-k-er and Sc-hif-fah-rt
sal_Int16 nPos = GetOrigWordPos( rOrigWord, nChgPos );
// get words like Sc-hif-fah-rt to work correct
sal_Int16 nHyphenationPos = rxHyphWord->getHyphenationPos();
if (nChgPos > nHyphenationPos)
--nPos;
std::u16string_view aLeft = rOrigWord.subView( 0, nPos );
std::u16string_view aRight = rOrigWord.subView( nPos ); // FIXME: changes at the right side
aOrigHyphenatedWord = aLeft + aRplc + aRight;
nOrigHyphenPos = sal::static_int_cast< sal_Int16 >(aLeft.size() +
rxHyphWord->getHyphenPos() - nChgPos);
nOrigHyphenationPos = GetOrigWordPos( rOrigWord, nHyphenationPos );
}
if (nOrigHyphenPos == -1 || nOrigHyphenationPos == -1)
{
SAL_WARN( "linguistic", "failed to get nOrigHyphenPos or nOrigHyphenationPos" );
}
else
{
LanguageType nLang = LinguLocaleToLanguage( rxHyphWord->getLocale() );
xRes = new HyphenatedWord(
rOrigWord, nLang, nOrigHyphenationPos,
aOrigHyphenatedWord, nOrigHyphenPos );
}
aOrigHyphenatedWord = rOrigWord;
nOrigHyphenPos = GetOrigWordPos( rOrigWord, rxHyphWord->getHyphenPos() );
nOrigHyphenationPos = GetOrigWordPos( rOrigWord, rxHyphWord->getHyphenationPos() );
}
return xRes;
else
{
//! should at least work with the German words
//! B-"u-c-k-er and Sc-hif-fah-rt
sal_Int16 nPos = GetOrigWordPos( rOrigWord, nChgPos );
// get words like Sc-hif-fah-rt to work correct
sal_Int16 nHyphenationPos = rxHyphWord->getHyphenationPos();
if (nChgPos > nHyphenationPos)
--nPos;
std::u16string_view aLeft = rOrigWord.subView( 0, nPos );
std::u16string_view aRight = rOrigWord.subView( nPos ); // FIXME: changes at the right side
aOrigHyphenatedWord = aLeft + aRplc + aRight;
nOrigHyphenPos = sal::static_int_cast< sal_Int16 >(aLeft.size() +
rxHyphWord->getHyphenPos() - nChgPos);
nOrigHyphenationPos = GetOrigWordPos( rOrigWord, nHyphenationPos );
}
if (nOrigHyphenPos != -1 && nOrigHyphenationPos != -1)
{
SAL_WARN( "linguistic", "failed to get nOrigHyphenPos or nOrigHyphenationPos" );
return nullptr;
}
LanguageType nLang = LinguLocaleToLanguage( rxHyphWord->getLocale() );
return new HyphenatedWord(
rOrigWord, nLang, nOrigHyphenationPos,
aOrigHyphenatedWord, nOrigHyphenPos );
}
bool IsUpper( const OUString &rText, sal_Int32 nPos, sal_Int32 nLen, LanguageType nLanguage )

View File

@ -424,299 +424,299 @@ Reference< XSpellAlternatives > SpellCheckerDispatcher::spell_Impl(
{
MutexGuard aGuard( GetLinguMutex() );
Reference< XSpellAlternatives > xRes;
if (LinguIsUnspecified( nLanguage) || rWord.isEmpty())
return xRes;
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;
if (pEntry)
if (!pEntry)
return nullptr;
Reference< XSpellAlternatives > xRes;
OUString aChkWord( rWord );
Locale aLocale( LanguageTag::convertToLocale( nLanguage ) );
// replace typographical apostroph by ascii apostroph
OUString aSingleQuote( GetLocaleDataWrapper( nLanguage ).getQuotationMarkEnd() );
DBG_ASSERT( 1 == aSingleQuote.getLength(), "unexpected length of quotation mark" );
if (!aSingleQuote.isEmpty())
aChkWord = aChkWord.replace( aSingleQuote[0], '\'' );
RemoveHyphens( aChkWord );
if (IsIgnoreControlChars( rProperties, GetPropSet() ))
RemoveControlChars( aChkWord );
sal_Int32 nLen = pEntry->aSvcRefs.getLength();
DBG_ASSERT( nLen == pEntry->aSvcImplNames.getLength(),
"lng : sequence length mismatch");
DBG_ASSERT( pEntry->nLastTriedSvcIndex < nLen,
"lng : index out of range");
sal_Int32 i = 0;
Reference< XSpellAlternatives > xTmpRes;
bool bTmpResValid = false;
// try already instantiated services first
{
OUString aChkWord( rWord );
Locale aLocale( LanguageTag::convertToLocale( nLanguage ) );
// replace typographical apostroph by ascii apostroph
OUString aSingleQuote( GetLocaleDataWrapper( nLanguage ).getQuotationMarkEnd() );
DBG_ASSERT( 1 == aSingleQuote.getLength(), "unexpected length of quotation mark" );
if (!aSingleQuote.isEmpty())
aChkWord = aChkWord.replace( aSingleQuote[0], '\'' );
RemoveHyphens( aChkWord );
if (IsIgnoreControlChars( rProperties, GetPropSet() ))
RemoveControlChars( aChkWord );
sal_Int32 nLen = pEntry->aSvcRefs.getLength();
DBG_ASSERT( nLen == pEntry->aSvcImplNames.getLength(),
"lng : sequence length mismatch");
DBG_ASSERT( pEntry->nLastTriedSvcIndex < nLen,
"lng : index out of range");
sal_Int32 i = 0;
Reference< XSpellAlternatives > xTmpRes;
bool bTmpResValid = false;
// try already instantiated services first
const Reference< XSpellChecker > *pRef = pEntry->aSvcRefs.getConstArray();
sal_Int32 nNumSuggestions = -1;
while (i <= pEntry->nLastTriedSvcIndex
&& (!bTmpResValid || xTmpRes.is()) )
{
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 ))
{
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 );
}
}
bool bOK = GetCache().CheckWord( aChkWord, nLanguage );
if (bOK)
xTmpRes = nullptr;
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;
}
xTmpRes = pRef[i]->spell( aChkWord, aLocale, rProperties );
++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();
const Reference< XComponentContext >& xContext(
comphelper::getProcessComponentContext() );
// build service initialization argument
Sequence< Any > aArgs(2);
aArgs.getArray()[0] <<= GetPropSet();
sal_Int32 nNumSuggestions = -1;
while (i < nLen && (!bTmpResValid || xTmpRes.is()))
{
// create specific service via it's implementation name
Reference< XSpellChecker > xSpell;
try
{
xSpell.set( xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
pImplNames[i], aArgs, xContext ),
UNO_QUERY );
}
catch (uno::Exception &)
{
SAL_WARN( "linguistic", "createInstanceWithArguments failed" );
}
pRef [i] = xSpell;
Reference< XLinguServiceEventBroadcaster >
xBroadcaster( xSpell, UNO_QUERY );
if (xBroadcaster.is())
m_rMgr.AddLngSvcEvtBroadcaster( xBroadcaster );
bTmpResValid = true;
if (xSpell.is() && xSpell->hasLocale( aLocale ))
{
bool bOK = GetCache().CheckWord( aChkWord, nLanguage );
if (bOK)
xTmpRes = nullptr;
else
{
xTmpRes = xSpell->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;
}
pEntry->nLastTriedSvcIndex = static_cast<sal_Int16>(i);
++i;
}
// 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
// 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)
{
setCharClass(LanguageTag(nLanguage));
CapType ct = capitalType(aChkWord, m_oCharClass ? &*m_oCharClass : nullptr);
if (ct == CapType::INITCAP || ct == CapType::ALLCAP)
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();
const Reference< XComponentContext >& xContext(
comphelper::getProcessComponentContext() );
// build service initialization argument
Sequence< Any > aArgs(2);
aArgs.getArray()[0] <<= GetPropSet();
sal_Int32 nNumSuggestions = -1;
while (i < nLen && (!bTmpResValid || xTmpRes.is()))
{
// create specific service via it's implementation name
Reference< XSpellChecker > xSpell;
try
{
xSpell.set( xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
pImplNames[i], aArgs, xContext ),
UNO_QUERY );
}
catch (uno::Exception &)
{
SAL_WARN( "linguistic", "createInstanceWithArguments failed" );
}
pRef [i] = xSpell;
Reference< XLinguServiceEventBroadcaster >
xBroadcaster( xSpell, UNO_QUERY );
if (xBroadcaster.is())
m_rMgr.AddLngSvcEvtBroadcaster( xBroadcaster );
bTmpResValid = true;
if (xSpell.is() && xSpell->hasLocale( aLocale ))
{
bool bOK = GetCache().CheckWord( aChkWord, nLanguage );
if (bOK)
xTmpRes = nullptr;
else
{
Reference< XDictionaryEntry > xTmp2( lcl_GetRulingDictionaryEntry( makeLowerCase(aChkWord, m_oCharClass), nLanguage ) );
if (xTmp2.is())
xTmpRes = xSpell->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;
}
pEntry->nLastTriedSvcIndex = static_cast<sal_Int16>(i);
++i;
}
// 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
{
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())
{
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 )
{
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;
}
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
}
}
else // positive entry found
{
xRes = nullptr;
eFailureType = -1; // no failure
}
}
}
}
}
if (eFailureType != -1) // word misspelled or found in negative user-dictionary
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())
{
// 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())
{
xSetAlt->setAlternatives( comphelper::containerToSequence(aProposals) );
xSetAlt->setFailureType( eFailureType );
SAL_WARN( "linguistic", "XSetSpellAlternatives not implemented!" );
}
else
else if (!aProposals.empty())
{
if (xRes.is())
{
SAL_WARN( "linguistic", "XSetSpellAlternatives not implemented!" );
}
else if (!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) );
}
// 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) );
}
}
}