1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <com/sun/star/uno/Reference.h>
21 #include <com/sun/star/linguistic2/XSearchableDictionaryList.hpp>
22 #include <com/sun/star/linguistic2/SpellFailure.hpp>
23 #include <com/sun/star/registry/XRegistryKey.hpp>
24 #include <comphelper/string.hxx>
25 #include <cppuhelper/factory.hxx>
26 #include <cppuhelper/supportsservice.hxx>
27 #include <tools/debug.hxx>
28 #include <osl/mutex.hxx>
30 #include <sspellimp.hxx>
32 #include "linguistic/lngprops.hxx"
33 #include "linguistic/spelldta.hxx"
37 using namespace com::sun::star
;
38 using namespace com::sun::star::beans
;
39 using namespace com::sun::star::lang
;
40 using namespace com::sun::star::uno
;
41 using namespace com::sun::star::linguistic2
;
42 using namespace linguistic
;
46 sal_Bool
operator == ( const Locale
&rL1
, const Locale
&rL2
)
48 return rL1
.Language
== rL2
.Language
&&
49 rL1
.Country
== rL2
.Country
&&
50 rL1
.Variant
== rL2
.Variant
;
55 SpellChecker::SpellChecker() :
56 aEvtListeners ( GetLinguMutex() )
58 bDisposing
= sal_False
;
63 SpellChecker::~SpellChecker()
66 pPropHelper
->RemoveAsPropListener();
70 PropertyHelper_Spell
& SpellChecker::GetPropHelper_Impl()
74 Reference
< XLinguProperties
> xPropSet
= GetLinguProperties();
76 pPropHelper
= new PropertyHelper_Spell( (XSpellChecker
*) this, xPropSet
);
77 xPropHelper
= pPropHelper
;
78 pPropHelper
->AddAsPropListener(); //! after a reference is established
84 Sequence
< Locale
> SAL_CALL
SpellChecker::getLocales()
85 throw(RuntimeException
)
87 MutexGuard
aGuard( GetLinguMutex() );
89 if (!aSuppLocales
.getLength())
91 aSuppLocales
.realloc( 3 );
92 Locale
*pLocale
= aSuppLocales
.getArray();
93 pLocale
[0] = Locale( "en", "US", OUString() );
94 pLocale
[1] = Locale( "de", "DE", OUString() );
95 pLocale
[2] = Locale( "de", "CH", OUString() );
102 sal_Bool SAL_CALL
SpellChecker::hasLocale(const Locale
& rLocale
)
103 throw(RuntimeException
)
105 MutexGuard
aGuard( GetLinguMutex() );
107 sal_Bool bRes
= sal_False
;
108 if (!aSuppLocales
.getLength())
110 sal_Int32 nLen
= aSuppLocales
.getLength();
111 for (sal_Int32 i
= 0; i
< nLen
; ++i
)
113 const Locale
*pLocale
= aSuppLocales
.getConstArray();
114 if (rLocale
== pLocale
[i
])
124 sal_Int16
SpellChecker::GetSpellFailure( const OUString
&rWord
, const Locale
& )
126 // Checks whether a word is OK in a given language (Locale) or not, and
127 // provides a failure type for the incorrect ones.
128 // - words with "liss" (case sensitiv) as substring will be negative.
129 // - words with 'x' or 'X' will have incorrect spelling.
130 // - words with 's' or 'S' as first letter will have the wrong caption.
131 // - all other words will be OK.
135 String
aTmp( rWord
);
138 if (-1 != aTmp
.indexOf( "liss" ))
140 nRes
= SpellFailure::IS_NEGATIVE_WORD
;
142 else if (-1 != aTmp
.indexOf( (sal_Unicode
) 'x' ) ||
143 -1 != aTmp
.indexOf( (sal_Unicode
) 'X' ))
145 nRes
= SpellFailure::SPELLING_ERROR
;
149 sal_Unicode cChar
= aTmp
.GetChar( 0 );
150 if (cChar
== (sal_Unicode
) 's' || cChar
== (sal_Unicode
) 'S')
151 nRes
= SpellFailure::CAPTION_ERROR
;
160 SpellChecker::isValid( const OUString
& rWord
, const Locale
& rLocale
,
161 const PropertyValues
& rProperties
)
162 throw(IllegalArgumentException
, RuntimeException
)
164 MutexGuard
aGuard( GetLinguMutex() );
166 if (rLocale
== Locale() || !rWord
.getLength())
169 if (!hasLocale( rLocale
))
170 #ifdef LINGU_EXCEPTIONS
171 throw( IllegalArgumentException() );
176 // Get property values to be used.
177 // These are be the default values set in the SN_LINGU_PROPERTIES
178 // PropertySet which are overridden by the supplied ones from the
180 // You'll probably like to use a simpler solution than the provided
181 // one using the PropertyHelper_Spell.
182 PropertyHelper_Spell
&rHelper
= GetPropHelper();
183 rHelper
.SetTmpPropVals( rProperties
);
185 sal_Int16 nFailure
= GetSpellFailure( rWord
, rLocale
);
188 sal_Int16 nLang
= LinguLocaleToLanguage( rLocale
);
189 // postprocess result for errors that should be ignored
190 if ( (!rHelper
.IsSpellUpperCase() && IsUpper( rWord
, nLang
))
191 || (!rHelper
.IsSpellWithDigits() && HasDigits( rWord
))
192 || (!rHelper
.IsSpellCapitalization()
193 && nFailure
== SpellFailure::CAPTION_ERROR
)
197 return nFailure
== -1;
201 Reference
< XSpellAlternatives
>
202 SpellChecker::GetProposals( const OUString
&rWord
, const Locale
&rLocale
)
204 // Retrieves the return values for the 'spell' function call in case
205 // of a misspelled word.
206 // Especially it may give a list of suggested (correct) words:
207 // - a "liss" substring will be replaced by "liz".
208 // - 'x' or 'X' will be replaced by 'u' or 'U' for the first proposal
209 // and they will be removed from the word for the second proposal.
210 // - 's' or 'S' as first letter will be changed to the other caption.
212 Reference
< XSpellAlternatives
> xRes
;
214 String
aTmp( rWord
);
217 sal_Int16 nLang
= LinguLocaleToLanguage( rLocale
);
219 if (-1 != aTmp
.indexOf( "liss" ))
221 aTmp
.SearchAndReplaceAllAscii( "liss", "liz" );
222 xRes
= new SpellAlternatives( aTmp
, nLang
,
223 SpellFailure::IS_NEGATIVE_WORD
, ::com::sun::star::uno::Sequence
< OUString
>() );
225 else if (-1 != aTmp
.indexOf( (sal_Unicode
) 'x' ) ||
226 -1 != aTmp
.indexOf( (sal_Unicode
) 'X' ))
228 Sequence
< OUString
> aStr( 2 );
229 OUString
*pStr
= aStr
.getArray();
230 String
aAlt1( aTmp
),
232 aAlt1
.SearchAndReplaceAll( (sal_Unicode
) 'x', (sal_Unicode
) 'u');
233 aAlt1
.SearchAndReplaceAll( (sal_Unicode
) 'X', (sal_Unicode
) 'U');
234 aAlt2
= comphelper::string::remove(aAlt2
, 'x');
235 aAlt2
= comphelper::string::remove(aAlt2
, 'X');
239 SpellAlternatives
*pAlt
= new SpellAlternatives
;
240 pAlt
->SetWordLanguage( aTmp
, nLang
);
241 pAlt
->SetFailureType( SpellFailure::SPELLING_ERROR
);
242 pAlt
->SetAlternatives( aStr
);
248 sal_Unicode cChar
= aTmp
.GetChar( 0 );
249 if (cChar
== (sal_Unicode
) 's' || cChar
== (sal_Unicode
) 'S')
251 sal_Unicode cNewChar
= cChar
== (sal_Unicode
) 's' ?
252 (sal_Unicode
) 'S': (sal_Unicode
) 's';
253 aTmp
.GetBufferAccess()[0] = cNewChar
;
254 xRes
= new SpellAlternatives( aTmp
, nLang
,
255 SpellFailure::CAPTION_ERROR
, ::com::sun::star::uno::Sequence
< OUString
>() );
264 Reference
< XSpellAlternatives
> SAL_CALL
265 SpellChecker::spell( const OUString
& rWord
, const Locale
& rLocale
,
266 const PropertyValues
& rProperties
)
267 throw(IllegalArgumentException
, RuntimeException
)
269 MutexGuard
aGuard( GetLinguMutex() );
271 if (rLocale
== Locale() || !rWord
.getLength())
274 if (!hasLocale( rLocale
))
275 #ifdef LINGU_EXCEPTIONS
276 throw( IllegalArgumentException() );
281 Reference
< XSpellAlternatives
> xAlt
;
282 if (!isValid( rWord
, rLocale
, rProperties
))
284 xAlt
= GetProposals( rWord
, rLocale
);
290 Reference
< XInterface
> SAL_CALL
SpellChecker_CreateInstance(
291 const Reference
< XMultiServiceFactory
> & )
294 Reference
< XInterface
> xService
= (cppu::OWeakObject
*) new SpellChecker
;
300 SpellChecker::addLinguServiceEventListener(
301 const Reference
< XLinguServiceEventListener
>& rxLstnr
)
302 throw(RuntimeException
)
304 MutexGuard
aGuard( GetLinguMutex() );
306 sal_Bool bRes
= sal_False
;
307 if (!bDisposing
&& rxLstnr
.is())
309 bRes
= GetPropHelper().addLinguServiceEventListener( rxLstnr
);
316 SpellChecker::removeLinguServiceEventListener(
317 const Reference
< XLinguServiceEventListener
>& rxLstnr
)
318 throw(RuntimeException
)
320 MutexGuard
aGuard( GetLinguMutex() );
322 sal_Bool bRes
= sal_False
;
323 if (!bDisposing
&& rxLstnr
.is())
325 DBG_ASSERT( xPropHelper
.is(), "xPropHelper non existent" );
326 bRes
= GetPropHelper().removeLinguServiceEventListener( rxLstnr
);
333 SpellChecker::getServiceDisplayName( const Locale
& )
334 throw(RuntimeException
)
336 MutexGuard
aGuard( GetLinguMutex() );
337 return OUString( "OpenOffice example spellchecker" );
342 SpellChecker::initialize( const Sequence
< Any
>& rArguments
)
343 throw(Exception
, RuntimeException
)
345 MutexGuard
aGuard( GetLinguMutex() );
349 sal_Int32 nLen
= rArguments
.getLength();
352 Reference
< XPropertySet
> xPropSet
;
353 rArguments
.getConstArray()[0] >>= xPropSet
;
355 //! Pointer allows for access of the non-UNO functions.
356 //! And the reference to the UNO-functions while increasing
357 //! the ref-count and will implicitly free the memory
358 //! when the object is not longer used.
359 pPropHelper
= new PropertyHelper_Spell( (XSpellChecker
*) this, xPropSet
);
360 xPropHelper
= pPropHelper
;
361 pPropHelper
->AddAsPropListener(); //! after a reference is established
364 OSL_FAIL( "wrong number of arguments in sequence" );
370 SpellChecker::dispose()
371 throw(RuntimeException
)
373 MutexGuard
aGuard( GetLinguMutex() );
377 bDisposing
= sal_True
;
378 EventObject
aEvtObj( (XSpellChecker
*) this );
379 aEvtListeners
.disposeAndClear( aEvtObj
);
385 SpellChecker::addEventListener( const Reference
< XEventListener
>& rxListener
)
386 throw(RuntimeException
)
388 MutexGuard
aGuard( GetLinguMutex() );
390 if (!bDisposing
&& rxListener
.is())
391 aEvtListeners
.addInterface( rxListener
);
396 SpellChecker::removeEventListener( const Reference
< XEventListener
>& rxListener
)
397 throw(RuntimeException
)
399 MutexGuard
aGuard( GetLinguMutex() );
401 if (!bDisposing
&& rxListener
.is())
402 aEvtListeners
.removeInterface( rxListener
);
406 // Service specific part
408 OUString SAL_CALL
SpellChecker::getImplementationName()
409 throw(RuntimeException
)
411 MutexGuard
aGuard( GetLinguMutex() );
412 return getImplementationName_Static();
416 sal_Bool SAL_CALL
SpellChecker::supportsService( const OUString
& ServiceName
)
417 throw(RuntimeException
)
419 return cppu::supportsService(this, ServiceName
);
422 Sequence
< OUString
> SAL_CALL
SpellChecker::getSupportedServiceNames()
423 throw(RuntimeException
)
425 MutexGuard
aGuard( GetLinguMutex() );
426 return getSupportedServiceNames_Static();
430 Sequence
< OUString
> SpellChecker::getSupportedServiceNames_Static()
433 MutexGuard
aGuard( GetLinguMutex() );
435 Sequence
< OUString
> aSNS( 1 ); // more than 1 service possible
436 aSNS
.getArray()[0] = SN_SPELLCHECKER
;
441 sal_Bool SAL_CALL
SpellChecker_writeInfo(
442 void * /*pServiceManager*/, registry::XRegistryKey
* pRegistryKey
)
446 OUString
aImpl( "/" + SpellChecker::getImplementationName_Static().getStr() +
449 Reference
< registry::XRegistryKey
> xNewKey
=
450 pRegistryKey
->createKey( aImpl
);
451 Sequence
< OUString
> aServices
=
452 SpellChecker::getSupportedServiceNames_Static();
453 for( sal_Int32 i
= 0; i
< aServices
.getLength(); i
++ )
454 xNewKey
->createKey( aServices
.getConstArray()[i
] );
465 void * SAL_CALL
SpellChecker_getFactory( const sal_Char
* pImplName
,
466 XMultiServiceFactory
* pServiceManager
, void * )
469 if ( SpellChecker::getImplementationName_Static().equalsAscii( pImplName
) )
471 Reference
< XSingleServiceFactory
> xFactory
=
472 cppu::createOneInstanceFactory(
474 SpellChecker::getImplementationName_Static(),
475 SpellChecker_CreateInstance
,
476 SpellChecker::getSupportedServiceNames_Static());
477 // acquire, because we return an interface pointer instead of a reference
479 pRet
= xFactory
.get();
486 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */