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> // helper for factories
26 #include <tools/debug.hxx>
27 #include <osl/mutex.hxx>
29 #include <sspellimp.hxx>
31 #include "linguistic/lngprops.hxx"
32 #include "linguistic/spelldta.hxx"
36 using namespace com::sun::star
;
37 using namespace com::sun::star::beans
;
38 using namespace com::sun::star::lang
;
39 using namespace com::sun::star::uno
;
40 using namespace com::sun::star::linguistic2
;
41 using namespace linguistic
;
45 sal_Bool
operator == ( const Locale
&rL1
, const Locale
&rL2
)
47 return rL1
.Language
== rL2
.Language
&&
48 rL1
.Country
== rL2
.Country
&&
49 rL1
.Variant
== rL2
.Variant
;
54 SpellChecker::SpellChecker() :
55 aEvtListeners ( GetLinguMutex() )
57 bDisposing
= sal_False
;
62 SpellChecker::~SpellChecker()
65 pPropHelper
->RemoveAsPropListener();
69 PropertyHelper_Spell
& SpellChecker::GetPropHelper_Impl()
73 Reference
< XLinguProperties
> xPropSet
= GetLinguProperties();
75 pPropHelper
= new PropertyHelper_Spell( (XSpellChecker
*) this, xPropSet
);
76 xPropHelper
= pPropHelper
;
77 pPropHelper
->AddAsPropListener(); //! after a reference is established
83 Sequence
< Locale
> SAL_CALL
SpellChecker::getLocales()
84 throw(RuntimeException
)
86 MutexGuard
aGuard( GetLinguMutex() );
88 if (!aSuppLocales
.getLength())
90 aSuppLocales
.realloc( 3 );
91 Locale
*pLocale
= aSuppLocales
.getArray();
92 pLocale
[0] = Locale( "en", "US", OUString() );
93 pLocale
[1] = Locale( "de", "DE", OUString() );
94 pLocale
[2] = Locale( "de", "CH"), OUString() );
101 sal_Bool SAL_CALL
SpellChecker::hasLocale(const Locale
& rLocale
)
102 throw(RuntimeException
)
104 MutexGuard
aGuard( GetLinguMutex() );
106 sal_Bool bRes
= sal_False
;
107 if (!aSuppLocales
.getLength())
109 sal_Int32 nLen
= aSuppLocales
.getLength();
110 for (sal_Int32 i
= 0; i
< nLen
; ++i
)
112 const Locale
*pLocale
= aSuppLocales
.getConstArray();
113 if (rLocale
== pLocale
[i
])
123 sal_Int16
SpellChecker::GetSpellFailure( const OUString
&rWord
, const Locale
& )
125 // Checks whether a word is OK in a given language (Locale) or not, and
126 // provides a failure type for the incorrect ones.
127 // - words with "liss" (case sensitiv) as substring will be negative.
128 // - words with 'x' or 'X' will have incorrect spelling.
129 // - words with 's' or 'S' as first letter will have the wrong caption.
130 // - all other words will be OK.
134 String
aTmp( rWord
);
137 if (STRING_NOTFOUND
!= aTmp
.SearchAscii( "liss" ))
139 nRes
= SpellFailure::IS_NEGATIVE_WORD
;
141 else if (STRING_NOTFOUND
!= aTmp
.Search( (sal_Unicode
) 'x' ) ||
142 STRING_NOTFOUND
!= aTmp
.Search( (sal_Unicode
) 'X' ))
144 nRes
= SpellFailure::SPELLING_ERROR
;
148 sal_Unicode cChar
= aTmp
.GetChar( 0 );
149 if (cChar
== (sal_Unicode
) 's' || cChar
== (sal_Unicode
) 'S')
150 nRes
= SpellFailure::CAPTION_ERROR
;
159 SpellChecker::isValid( const OUString
& rWord
, const Locale
& rLocale
,
160 const PropertyValues
& rProperties
)
161 throw(IllegalArgumentException
, RuntimeException
)
163 MutexGuard
aGuard( GetLinguMutex() );
165 if (rLocale
== Locale() || !rWord
.getLength())
168 if (!hasLocale( rLocale
))
169 #ifdef LINGU_EXCEPTIONS
170 throw( IllegalArgumentException() );
175 // Get property values to be used.
176 // These are be the default values set in the SN_LINGU_PROPERTIES
177 // PropertySet which are overridden by the supplied ones from the
179 // You'll probably like to use a simplier solution than the provided
180 // one using the PropertyHelper_Spell.
181 PropertyHelper_Spell
&rHelper
= GetPropHelper();
182 rHelper
.SetTmpPropVals( rProperties
);
184 sal_Int16 nFailure
= GetSpellFailure( rWord
, rLocale
);
187 sal_Int16 nLang
= LinguLocaleToLanguage( rLocale
);
188 // postprocess result for errors that should be ignored
189 if ( (!rHelper
.IsSpellUpperCase() && IsUpper( rWord
, nLang
))
190 || (!rHelper
.IsSpellWithDigits() && HasDigits( rWord
))
191 || (!rHelper
.IsSpellCapitalization()
192 && nFailure
== SpellFailure::CAPTION_ERROR
)
196 return nFailure
== -1;
200 Reference
< XSpellAlternatives
>
201 SpellChecker::GetProposals( const OUString
&rWord
, const Locale
&rLocale
)
203 // Retrieves the return values for the 'spell' function call in case
204 // of a misspelled word.
205 // Especially it may give a list of suggested (correct) words:
206 // - a "liss" substring will be replaced by "liz".
207 // - 'x' or 'X' will be replaced by 'u' or 'U' for the first proposal
208 // and they will be removed from the word for the second proposal.
209 // - 's' or 'S' as first letter will be changed to the other caption.
211 Reference
< XSpellAlternatives
> xRes
;
213 String
aTmp( rWord
);
216 sal_Int16 nLang
= LinguLocaleToLanguage( rLocale
);
218 if (STRING_NOTFOUND
!= aTmp
.SearchAscii( "liss" ))
220 aTmp
.SearchAndReplaceAllAscii( "liss", "liz" );
221 xRes
= new SpellAlternatives( aTmp
, nLang
,
222 SpellFailure::IS_NEGATIVE_WORD
, ::com::sun::star::uno::Sequence
< OUString
>() );
224 else if (STRING_NOTFOUND
!= aTmp
.Search( (sal_Unicode
) 'x' ) ||
225 STRING_NOTFOUND
!= aTmp
.Search( (sal_Unicode
) 'X' ))
227 Sequence
< OUString
> aStr( 2 );
228 OUString
*pStr
= aStr
.getArray();
229 String
aAlt1( aTmp
),
231 aAlt1
.SearchAndReplaceAll( (sal_Unicode
) 'x', (sal_Unicode
) 'u');
232 aAlt1
.SearchAndReplaceAll( (sal_Unicode
) 'X', (sal_Unicode
) 'U');
233 aAlt2
= comphelper::string::remove(aAlt2
, 'x');
234 aAlt2
= comphelper::string::remove(aAlt2
, 'X');
238 SpellAlternatives
*pAlt
= new SpellAlternatives
;
239 pAlt
->SetWordLanguage( aTmp
, nLang
);
240 pAlt
->SetFailureType( SpellFailure::SPELLING_ERROR
);
241 pAlt
->SetAlternatives( aStr
);
247 sal_Unicode cChar
= aTmp
.GetChar( 0 );
248 if (cChar
== (sal_Unicode
) 's' || cChar
== (sal_Unicode
) 'S')
250 sal_Unicode cNewChar
= cChar
== (sal_Unicode
) 's' ?
251 (sal_Unicode
) 'S': (sal_Unicode
) 's';
252 aTmp
.GetBufferAccess()[0] = cNewChar
;
253 xRes
= new SpellAlternatives( aTmp
, nLang
,
254 SpellFailure::CAPTION_ERROR
, ::com::sun::star::uno::Sequence
< OUString
>() );
263 Reference
< XSpellAlternatives
> SAL_CALL
264 SpellChecker::spell( const OUString
& rWord
, const Locale
& rLocale
,
265 const PropertyValues
& rProperties
)
266 throw(IllegalArgumentException
, RuntimeException
)
268 MutexGuard
aGuard( GetLinguMutex() );
270 if (rLocale
== Locale() || !rWord
.getLength())
273 if (!hasLocale( rLocale
))
274 #ifdef LINGU_EXCEPTIONS
275 throw( IllegalArgumentException() );
280 Reference
< XSpellAlternatives
> xAlt
;
281 if (!isValid( rWord
, rLocale
, rProperties
))
283 xAlt
= GetProposals( rWord
, rLocale
);
289 Reference
< XInterface
> SAL_CALL
SpellChecker_CreateInstance(
290 const Reference
< XMultiServiceFactory
> & )
293 Reference
< XInterface
> xService
= (cppu::OWeakObject
*) new SpellChecker
;
299 SpellChecker::addLinguServiceEventListener(
300 const Reference
< XLinguServiceEventListener
>& rxLstnr
)
301 throw(RuntimeException
)
303 MutexGuard
aGuard( GetLinguMutex() );
305 sal_Bool bRes
= sal_False
;
306 if (!bDisposing
&& rxLstnr
.is())
308 bRes
= GetPropHelper().addLinguServiceEventListener( rxLstnr
);
315 SpellChecker::removeLinguServiceEventListener(
316 const Reference
< XLinguServiceEventListener
>& rxLstnr
)
317 throw(RuntimeException
)
319 MutexGuard
aGuard( GetLinguMutex() );
321 sal_Bool bRes
= sal_False
;
322 if (!bDisposing
&& rxLstnr
.is())
324 DBG_ASSERT( xPropHelper
.is(), "xPropHelper non existent" );
325 bRes
= GetPropHelper().removeLinguServiceEventListener( rxLstnr
);
332 SpellChecker::getServiceDisplayName( const Locale
& )
333 throw(RuntimeException
)
335 MutexGuard
aGuard( GetLinguMutex() );
336 return OUString( "OpenOffice example spellchecker" );
341 SpellChecker::initialize( const Sequence
< Any
>& rArguments
)
342 throw(Exception
, RuntimeException
)
344 MutexGuard
aGuard( GetLinguMutex() );
348 sal_Int32 nLen
= rArguments
.getLength();
351 Reference
< XPropertySet
> xPropSet
;
352 rArguments
.getConstArray()[0] >>= xPropSet
;
354 //! Pointer allows for access of the non-UNO functions.
355 //! And the reference to the UNO-functions while increasing
356 //! the ref-count and will implicitly free the memory
357 //! when the object is not longer used.
358 pPropHelper
= new PropertyHelper_Spell( (XSpellChecker
*) this, xPropSet
);
359 xPropHelper
= pPropHelper
;
360 pPropHelper
->AddAsPropListener(); //! after a reference is established
363 OSL_FAIL( "wrong number of arguments in sequence" );
369 SpellChecker::dispose()
370 throw(RuntimeException
)
372 MutexGuard
aGuard( GetLinguMutex() );
376 bDisposing
= sal_True
;
377 EventObject
aEvtObj( (XSpellChecker
*) this );
378 aEvtListeners
.disposeAndClear( aEvtObj
);
384 SpellChecker::addEventListener( const Reference
< XEventListener
>& rxListener
)
385 throw(RuntimeException
)
387 MutexGuard
aGuard( GetLinguMutex() );
389 if (!bDisposing
&& rxListener
.is())
390 aEvtListeners
.addInterface( rxListener
);
395 SpellChecker::removeEventListener( const Reference
< XEventListener
>& rxListener
)
396 throw(RuntimeException
)
398 MutexGuard
aGuard( GetLinguMutex() );
400 if (!bDisposing
&& rxListener
.is())
401 aEvtListeners
.removeInterface( rxListener
);
405 // Service specific part
407 OUString SAL_CALL
SpellChecker::getImplementationName()
408 throw(RuntimeException
)
410 MutexGuard
aGuard( GetLinguMutex() );
411 return getImplementationName_Static();
415 sal_Bool SAL_CALL
SpellChecker::supportsService( const OUString
& ServiceName
)
416 throw(RuntimeException
)
418 MutexGuard
aGuard( GetLinguMutex() );
420 Sequence
< OUString
> aSNL
= getSupportedServiceNames();
421 const OUString
* pArray
= aSNL
.getConstArray();
422 for( sal_Int32 i
= 0; i
< aSNL
.getLength(); i
++ )
423 if( pArray
[i
] == ServiceName
)
429 Sequence
< OUString
> SAL_CALL
SpellChecker::getSupportedServiceNames()
430 throw(RuntimeException
)
432 MutexGuard
aGuard( GetLinguMutex() );
433 return getSupportedServiceNames_Static();
437 Sequence
< OUString
> SpellChecker::getSupportedServiceNames_Static()
440 MutexGuard
aGuard( GetLinguMutex() );
442 Sequence
< OUString
> aSNS( 1 ); // more than 1 service possible
443 aSNS
.getArray()[0] = SN_SPELLCHECKER
;
448 sal_Bool SAL_CALL
SpellChecker_writeInfo(
449 void * /*pServiceManager*/, registry::XRegistryKey
* pRegistryKey
)
453 OUString
aImpl( "/" + SpellChecker::getImplementationName_Static().getStr() +
456 Reference
< registry::XRegistryKey
> xNewKey
=
457 pRegistryKey
->createKey( aImpl
);
458 Sequence
< OUString
> aServices
=
459 SpellChecker::getSupportedServiceNames_Static();
460 for( sal_Int32 i
= 0; i
< aServices
.getLength(); i
++ )
461 xNewKey
->createKey( aServices
.getConstArray()[i
] );
472 void * SAL_CALL
SpellChecker_getFactory( const sal_Char
* pImplName
,
473 XMultiServiceFactory
* pServiceManager
, void * )
476 if ( !SpellChecker::getImplementationName_Static().compareToAscii( pImplName
) )
478 Reference
< XSingleServiceFactory
> xFactory
=
479 cppu::createOneInstanceFactory(
481 SpellChecker::getImplementationName_Static(),
482 SpellChecker_CreateInstance
,
483 SpellChecker::getSupportedServiceNames_Static());
484 // acquire, because we return an interface pointer instead of a reference
486 pRet
= xFactory
.get();
493 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */