blob: 143a32a43a4e27bdd2929c9c71320854be1d65b5 [file] [log] [blame]
/**************************************************************
*
* 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
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_linguistic.hxx"
#include <com/sun/star/uno/Reference.h>
#include <com/sun/star/linguistic2/XSearchableDictionaryList.hpp>
#include <com/sun/star/linguistic2/SpellFailure.hpp>
#include <cppuhelper/factory.hxx> // helper for factories
#include <com/sun/star/registry/XRegistryKey.hpp>
#include <tools/debug.hxx>
#include <unotools/processfactory.hxx>
#include <osl/mutex.hxx>
#ifndef _SPELLIMP_HXX
#include <sspellimp.hxx>
#endif
#include "linguistic/lngprops.hxx"
#include "linguistic/spelldta.hxx"
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;
///////////////////////////////////////////////////////////////////////////
BOOL operator == ( const Locale &rL1, const Locale &rL2 )
{
return rL1.Language == rL2.Language &&
rL1.Country == rL2.Country &&
rL1.Variant == rL2.Variant;
}
///////////////////////////////////////////////////////////////////////////
SpellChecker::SpellChecker() :
aEvtListeners ( GetLinguMutex() )
{
bDisposing = FALSE;
pPropHelper = NULL;
}
SpellChecker::~SpellChecker()
{
if (pPropHelper)
pPropHelper->RemoveAsPropListener();
}
PropertyHelper_Spell & SpellChecker::GetPropHelper_Impl()
{
if (!pPropHelper)
{
Reference< XPropertySet > xPropSet( GetLinguProperties(), UNO_QUERY );
pPropHelper = new PropertyHelper_Spell( (XSpellChecker *) this, xPropSet );
xPropHelper = pPropHelper;
pPropHelper->AddAsPropListener(); //! after a reference is established
}
return *pPropHelper;
}
Sequence< Locale > SAL_CALL SpellChecker::getLocales()
throw(RuntimeException)
{
MutexGuard aGuard( GetLinguMutex() );
if (!aSuppLocales.getLength())
{
aSuppLocales.realloc( 3 );
Locale *pLocale = aSuppLocales.getArray();
pLocale[0] = Locale( A2OU("en"), A2OU("US"), OUString() );
pLocale[1] = Locale( A2OU("de"), A2OU("DE"), OUString() );
pLocale[2] = Locale( A2OU("de"), A2OU("CH"), OUString() );
}
return aSuppLocales;
}
sal_Bool SAL_CALL SpellChecker::hasLocale(const Locale& rLocale)
throw(RuntimeException)
{
MutexGuard aGuard( GetLinguMutex() );
BOOL bRes = FALSE;
if (!aSuppLocales.getLength())
getLocales();
INT32 nLen = aSuppLocales.getLength();
for (INT32 i = 0; i < nLen; ++i)
{
const Locale *pLocale = aSuppLocales.getConstArray();
if (rLocale == pLocale[i])
{
bRes = TRUE;
break;
}
}
return bRes;
}
INT16 SpellChecker::GetSpellFailure( const OUString &rWord, const Locale &rLocale )
{
// Checks wether a word is OK in a given language (Locale) or not, and
// provides a failure type for the incorrect ones.
// - words with "liss" (case sensitiv) as substring will be negative.
// - words with 'x' or 'X' will have incorrect spelling.
// - words with 's' or 'S' as first letter will have the wrong caption.
// - all other words will be OK.
INT16 nRes = -1;
String aTmp( rWord );
if (aTmp.Len())
{
if (STRING_NOTFOUND != aTmp.SearchAscii( "liss" ))
{
nRes = SpellFailure::IS_NEGATIVE_WORD;
}
else if (STRING_NOTFOUND != aTmp.Search( (sal_Unicode) 'x' ) ||
STRING_NOTFOUND != aTmp.Search( (sal_Unicode) 'X' ))
{
nRes = SpellFailure::SPELLING_ERROR;
}
else
{
sal_Unicode cChar = aTmp.GetChar( 0 );
if (cChar == (sal_Unicode) 's' || cChar == (sal_Unicode) 'S')
nRes = SpellFailure::CAPTION_ERROR;
}
}
return nRes;
}
sal_Bool SAL_CALL
SpellChecker::isValid( const OUString& rWord, const Locale& rLocale,
const PropertyValues& rProperties )
throw(IllegalArgumentException, RuntimeException)
{
MutexGuard aGuard( GetLinguMutex() );
if (rLocale == Locale() || !rWord.getLength())
return TRUE;
if (!hasLocale( rLocale ))
#ifdef LINGU_EXCEPTIONS
throw( IllegalArgumentException() );
#else
return TRUE;
#endif
// Get property values to be used.
// These are be the default values set in the SN_LINGU_PROPERTIES
// PropertySet which are overridden by the supplied ones from the
// last argument.
// You'll probably like to use a simplier solution than the provided
// one using the PropertyHelper_Spell.
PropertyHelper_Spell &rHelper = GetPropHelper();
rHelper.SetTmpPropVals( rProperties );
INT16 nFailure = GetSpellFailure( rWord, rLocale );
if (nFailure != -1)
{
INT16 nLang = LocaleToLanguage( rLocale );
// postprocess result for errors that should be ignored
if ( (!rHelper.IsSpellUpperCase() && IsUpper( rWord, nLang ))
|| (!rHelper.IsSpellWithDigits() && HasDigits( rWord ))
|| (!rHelper.IsSpellCapitalization()
&& nFailure == SpellFailure::CAPTION_ERROR)
)
nFailure = -1;
}
return nFailure == -1;
}
Reference< XSpellAlternatives >
SpellChecker::GetProposals( const OUString &rWord, const Locale &rLocale )
{
// Retrieves the return values for the 'spell' function call in case
// of a misspelled word.
// Especially it may give a list of suggested (correct) words:
// - a "liss" substring will be replaced by "liz".
// - 'x' or 'X' will be replaced by 'u' or 'U' for the first proposal
// and they will be removed from the word for the second proposal.
// - 's' or 'S' as first letter will be changed to the other caption.
Reference< XSpellAlternatives > xRes;
String aTmp( rWord );
if (aTmp.Len())
{
INT16 nLang = LocaleToLanguage( rLocale );
if (STRING_NOTFOUND != aTmp.SearchAscii( "liss" ))
{
aTmp.SearchAndReplaceAllAscii( "liss", A2OU("liz") );
xRes = new SpellAlternatives( aTmp, nLang,
SpellFailure::IS_NEGATIVE_WORD, aTmp );
}
else if (STRING_NOTFOUND != aTmp.Search( (sal_Unicode) 'x' ) ||
STRING_NOTFOUND != aTmp.Search( (sal_Unicode) 'X' ))
{
Sequence< OUString > aStr( 2 );
OUString *pStr = aStr.getArray();
String aAlt1( aTmp ),
aAlt2( aTmp );
aAlt1.SearchAndReplaceAll( (sal_Unicode) 'x', (sal_Unicode) 'u');
aAlt1.SearchAndReplaceAll( (sal_Unicode) 'X', (sal_Unicode) 'U');
aAlt2.EraseAllChars( (sal_Unicode) 'x' );
aAlt2.EraseAllChars( (sal_Unicode) 'X' );
pStr[0] = aAlt1;
pStr[1] = aAlt2;
SpellAlternatives *pAlt = new SpellAlternatives;
pAlt->SetWordLanguage( aTmp, nLang );
pAlt->SetFailureType( SpellFailure::SPELLING_ERROR );
pAlt->SetAlternatives( aStr );
xRes = pAlt;
}
else
{
sal_Unicode cChar = aTmp.GetChar( 0 );
if (cChar == (sal_Unicode) 's' || cChar == (sal_Unicode) 'S')
{
sal_Unicode cNewChar = cChar == (sal_Unicode) 's' ?
(sal_Unicode) 'S': (sal_Unicode) 's';
aTmp.GetBufferAccess()[0] = cNewChar;
xRes = new SpellAlternatives( aTmp, nLang,
SpellFailure::CAPTION_ERROR, aTmp );
}
}
}
return xRes;
}
Reference< XSpellAlternatives > SAL_CALL
SpellChecker::spell( const OUString& rWord, const Locale& rLocale,
const PropertyValues& rProperties )
throw(IllegalArgumentException, RuntimeException)
{
MutexGuard aGuard( GetLinguMutex() );
if (rLocale == Locale() || !rWord.getLength())
return NULL;
if (!hasLocale( rLocale ))
#ifdef LINGU_EXCEPTIONS
throw( IllegalArgumentException() );
#else
return NULL;
#endif
Reference< XSpellAlternatives > xAlt;
if (!isValid( rWord, rLocale, rProperties ))
{
xAlt = GetProposals( rWord, rLocale );
}
return xAlt;
}
Reference< XInterface > SAL_CALL SpellChecker_CreateInstance(
const Reference< XMultiServiceFactory > & rSMgr )
throw(Exception)
{
Reference< XInterface > xService = (cppu::OWeakObject*) new SpellChecker;
return xService;
}
sal_Bool SAL_CALL
SpellChecker::addLinguServiceEventListener(
const Reference< XLinguServiceEventListener >& rxLstnr )
throw(RuntimeException)
{
MutexGuard aGuard( GetLinguMutex() );
BOOL bRes = FALSE;
if (!bDisposing && rxLstnr.is())
{
bRes = GetPropHelper().addLinguServiceEventListener( rxLstnr );
}
return bRes;
}
sal_Bool SAL_CALL
SpellChecker::removeLinguServiceEventListener(
const Reference< XLinguServiceEventListener >& rxLstnr )
throw(RuntimeException)
{
MutexGuard aGuard( GetLinguMutex() );
BOOL bRes = FALSE;
if (!bDisposing && rxLstnr.is())
{
DBG_ASSERT( xPropHelper.is(), "xPropHelper non existent" );
bRes = GetPropHelper().removeLinguServiceEventListener( rxLstnr );
}
return bRes;
}
OUString SAL_CALL
SpellChecker::getServiceDisplayName( const Locale& rLocale )
throw(RuntimeException)
{
MutexGuard aGuard( GetLinguMutex() );
return A2OU( "OpenOffice example spellchecker" );
}
void SAL_CALL
SpellChecker::initialize( const Sequence< Any >& rArguments )
throw(Exception, RuntimeException)
{
MutexGuard aGuard( GetLinguMutex() );
if (!pPropHelper)
{
INT32 nLen = rArguments.getLength();
if (2 == nLen)
{
Reference< XPropertySet > xPropSet;
rArguments.getConstArray()[0] >>= xPropSet;
//rArguments.getConstArray()[1] >>= xDicList;
//! Pointer allows for access of the non-UNO functions.
//! And the reference to the UNO-functions while increasing
//! the ref-count and will implicitly free the memory
//! when the object is not longer used.
pPropHelper = new PropertyHelper_Spell( (XSpellChecker *) this, xPropSet );
xPropHelper = pPropHelper;
pPropHelper->AddAsPropListener(); //! after a reference is established
}
else
DBG_ERROR( "wrong number of arguments in sequence" );
}
}
void SAL_CALL
SpellChecker::dispose()
throw(RuntimeException)
{
MutexGuard aGuard( GetLinguMutex() );
if (!bDisposing)
{
bDisposing = TRUE;
EventObject aEvtObj( (XSpellChecker *) this );
aEvtListeners.disposeAndClear( aEvtObj );
}
}
void SAL_CALL
SpellChecker::addEventListener( const Reference< XEventListener >& rxListener )
throw(RuntimeException)
{
MutexGuard aGuard( GetLinguMutex() );
if (!bDisposing && rxListener.is())
aEvtListeners.addInterface( rxListener );
}
void SAL_CALL
SpellChecker::removeEventListener( const Reference< XEventListener >& rxListener )
throw(RuntimeException)
{
MutexGuard aGuard( GetLinguMutex() );
if (!bDisposing && rxListener.is())
aEvtListeners.removeInterface( rxListener );
}
///////////////////////////////////////////////////////////////////////////
// Service specific part
//
OUString SAL_CALL SpellChecker::getImplementationName()
throw(RuntimeException)
{
MutexGuard aGuard( GetLinguMutex() );
return getImplementationName_Static();
}
sal_Bool SAL_CALL SpellChecker::supportsService( const OUString& ServiceName )
throw(RuntimeException)
{
MutexGuard aGuard( GetLinguMutex() );
Sequence< OUString > aSNL = getSupportedServiceNames();
const OUString * pArray = aSNL.getConstArray();
for( INT32 i = 0; i < aSNL.getLength(); i++ )
if( pArray[i] == ServiceName )
return TRUE;
return FALSE;
}
Sequence< OUString > SAL_CALL SpellChecker::getSupportedServiceNames()
throw(RuntimeException)
{
MutexGuard aGuard( GetLinguMutex() );
return getSupportedServiceNames_Static();
}
Sequence< OUString > SpellChecker::getSupportedServiceNames_Static()
throw()
{
MutexGuard aGuard( GetLinguMutex() );
Sequence< OUString > aSNS( 1 ); // auch mehr als 1 Service moeglich
aSNS.getArray()[0] = A2OU( SN_SPELLCHECKER );
return aSNS;
}
sal_Bool SAL_CALL SpellChecker_writeInfo(
void * /*pServiceManager*/, registry::XRegistryKey * pRegistryKey )
{
try
{
String aImpl( '/' );
aImpl += SpellChecker::getImplementationName_Static().getStr();
aImpl.AppendAscii( "/UNO/SERVICES" );
Reference< registry::XRegistryKey > xNewKey =
pRegistryKey->createKey( aImpl );
Sequence< OUString > aServices =
SpellChecker::getSupportedServiceNames_Static();
for( INT32 i = 0; i < aServices.getLength(); i++ )
xNewKey->createKey( aServices.getConstArray()[i] );
return sal_True;
}
catch(Exception &)
{
return sal_False;
}
}
void * SAL_CALL SpellChecker_getFactory( const sal_Char * pImplName,
XMultiServiceFactory * pServiceManager, void * )
{
void * pRet = 0;
if ( !SpellChecker::getImplementationName_Static().compareToAscii( pImplName ) )
{
Reference< XSingleServiceFactory > xFactory =
cppu::createOneInstanceFactory(
pServiceManager,
SpellChecker::getImplementationName_Static(),
SpellChecker_CreateInstance,
SpellChecker::getSupportedServiceNames_Static());
// acquire, because we return an interface pointer instead of a reference
xFactory->acquire();
pRet = xFactory.get();
}
return pRet;
}
///////////////////////////////////////////////////////////////////////////