1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*************************************************************************
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * Copyright 2000, 2010 Oracle and/or its affiliates.
8 * OpenOffice.org - a multi-platform office productivity suite
10 * This file is part of OpenOffice.org.
12 * OpenOffice.org is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU Lesser General Public License version 3
14 * only, as published by the Free Software Foundation.
16 * OpenOffice.org is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License version 3 for more details
20 * (a copy is included in the LICENSE file that accompanied this code).
22 * You should have received a copy of the GNU Lesser General Public License
23 * version 3 along with OpenOffice.org. If not, see
24 * <http://www.openoffice.org/license.html>
25 * for a copy of the LGPLv3 License.
27 ************************************************************************/
29 #include <com/sun/star/uno/Reference.h>
30 #include <com/sun/star/linguistic2/XSearchableDictionaryList.hpp>
31 #include <com/sun/star/linguistic2/SpellFailure.hpp>
32 #include <com/sun/star/registry/XRegistryKey.hpp>
33 #include <comphelper/string.hxx>
34 #include <cppuhelper/factory.hxx> // helper for factories
35 #include <tools/debug.hxx>
36 #include <osl/mutex.hxx>
38 #include <sspellimp.hxx>
40 #include "linguistic/lngprops.hxx"
41 #include "linguistic/spelldta.hxx"
45 using namespace com::sun::star
;
46 using namespace com::sun::star::beans
;
47 using namespace com::sun::star::lang
;
48 using namespace com::sun::star::uno
;
49 using namespace com::sun::star::linguistic2
;
50 using namespace linguistic
;
52 using ::rtl::OUString
;
55 sal_Bool
operator == ( const Locale
&rL1
, const Locale
&rL2
)
57 return rL1
.Language
== rL2
.Language
&&
58 rL1
.Country
== rL2
.Country
&&
59 rL1
.Variant
== rL2
.Variant
;
64 SpellChecker::SpellChecker() :
65 aEvtListeners ( GetLinguMutex() )
67 bDisposing
= sal_False
;
72 SpellChecker::~SpellChecker()
75 pPropHelper
->RemoveAsPropListener();
79 PropertyHelper_Spell
& SpellChecker::GetPropHelper_Impl()
83 Reference
< XPropertySet
> xPropSet( GetLinguProperties(), UNO_QUERY
);
85 pPropHelper
= new PropertyHelper_Spell( (XSpellChecker
*) this, xPropSet
);
86 xPropHelper
= pPropHelper
;
87 pPropHelper
->AddAsPropListener(); //! after a reference is established
93 Sequence
< Locale
> SAL_CALL
SpellChecker::getLocales()
94 throw(RuntimeException
)
96 MutexGuard
aGuard( GetLinguMutex() );
98 if (!aSuppLocales
.getLength())
100 aSuppLocales
.realloc( 3 );
101 Locale
*pLocale
= aSuppLocales
.getArray();
102 pLocale
[0] = Locale( A2OU("en"), A2OU("US"), OUString() );
103 pLocale
[1] = Locale( A2OU("de"), A2OU("DE"), OUString() );
104 pLocale
[2] = Locale( A2OU("de"), A2OU("CH"), OUString() );
111 sal_Bool SAL_CALL
SpellChecker::hasLocale(const Locale
& rLocale
)
112 throw(RuntimeException
)
114 MutexGuard
aGuard( GetLinguMutex() );
116 sal_Bool bRes
= sal_False
;
117 if (!aSuppLocales
.getLength())
119 sal_Int32 nLen
= aSuppLocales
.getLength();
120 for (sal_Int32 i
= 0; i
< nLen
; ++i
)
122 const Locale
*pLocale
= aSuppLocales
.getConstArray();
123 if (rLocale
== pLocale
[i
])
133 sal_Int16
SpellChecker::GetSpellFailure( const OUString
&rWord
, const Locale
& )
135 // Checks whether a word is OK in a given language (Locale) or not, and
136 // provides a failure type for the incorrect ones.
137 // - words with "liss" (case sensitiv) as substring will be negative.
138 // - words with 'x' or 'X' will have incorrect spelling.
139 // - words with 's' or 'S' as first letter will have the wrong caption.
140 // - all other words will be OK.
144 String
aTmp( rWord
);
147 if (STRING_NOTFOUND
!= aTmp
.SearchAscii( "liss" ))
149 nRes
= SpellFailure::IS_NEGATIVE_WORD
;
151 else if (STRING_NOTFOUND
!= aTmp
.Search( (sal_Unicode
) 'x' ) ||
152 STRING_NOTFOUND
!= aTmp
.Search( (sal_Unicode
) 'X' ))
154 nRes
= SpellFailure::SPELLING_ERROR
;
158 sal_Unicode cChar
= aTmp
.GetChar( 0 );
159 if (cChar
== (sal_Unicode
) 's' || cChar
== (sal_Unicode
) 'S')
160 nRes
= SpellFailure::CAPTION_ERROR
;
169 SpellChecker::isValid( const OUString
& rWord
, const Locale
& rLocale
,
170 const PropertyValues
& rProperties
)
171 throw(IllegalArgumentException
, RuntimeException
)
173 MutexGuard
aGuard( GetLinguMutex() );
175 if (rLocale
== Locale() || !rWord
.getLength())
178 if (!hasLocale( rLocale
))
179 #ifdef LINGU_EXCEPTIONS
180 throw( IllegalArgumentException() );
185 // Get property values to be used.
186 // These are be the default values set in the SN_LINGU_PROPERTIES
187 // PropertySet which are overridden by the supplied ones from the
189 // You'll probably like to use a simplier solution than the provided
190 // one using the PropertyHelper_Spell.
191 PropertyHelper_Spell
&rHelper
= GetPropHelper();
192 rHelper
.SetTmpPropVals( rProperties
);
194 sal_Int16 nFailure
= GetSpellFailure( rWord
, rLocale
);
197 sal_Int16 nLang
= LocaleToLanguage( rLocale
);
198 // postprocess result for errors that should be ignored
199 if ( (!rHelper
.IsSpellUpperCase() && IsUpper( rWord
, nLang
))
200 || (!rHelper
.IsSpellWithDigits() && HasDigits( rWord
))
201 || (!rHelper
.IsSpellCapitalization()
202 && nFailure
== SpellFailure::CAPTION_ERROR
)
206 return nFailure
== -1;
210 Reference
< XSpellAlternatives
>
211 SpellChecker::GetProposals( const OUString
&rWord
, const Locale
&rLocale
)
213 // Retrieves the return values for the 'spell' function call in case
214 // of a misspelled word.
215 // Especially it may give a list of suggested (correct) words:
216 // - a "liss" substring will be replaced by "liz".
217 // - 'x' or 'X' will be replaced by 'u' or 'U' for the first proposal
218 // and they will be removed from the word for the second proposal.
219 // - 's' or 'S' as first letter will be changed to the other caption.
221 Reference
< XSpellAlternatives
> xRes
;
223 String
aTmp( rWord
);
226 sal_Int16 nLang
= LocaleToLanguage( rLocale
);
228 if (STRING_NOTFOUND
!= aTmp
.SearchAscii( "liss" ))
230 aTmp
.SearchAndReplaceAllAscii( "liss", A2OU("liz") );
231 xRes
= new SpellAlternatives( aTmp
, nLang
,
232 SpellFailure::IS_NEGATIVE_WORD
, ::com::sun::star::uno::Sequence
< ::rtl::OUString
>() );
234 else if (STRING_NOTFOUND
!= aTmp
.Search( (sal_Unicode
) 'x' ) ||
235 STRING_NOTFOUND
!= aTmp
.Search( (sal_Unicode
) 'X' ))
237 Sequence
< OUString
> aStr( 2 );
238 OUString
*pStr
= aStr
.getArray();
239 String
aAlt1( aTmp
),
241 aAlt1
.SearchAndReplaceAll( (sal_Unicode
) 'x', (sal_Unicode
) 'u');
242 aAlt1
.SearchAndReplaceAll( (sal_Unicode
) 'X', (sal_Unicode
) 'U');
243 aAlt2
= comphelper::string::remove(aAlt2
, 'x');
244 aAlt2
= comphelper::string::remove(aAlt2
, 'X');
248 SpellAlternatives
*pAlt
= new SpellAlternatives
;
249 pAlt
->SetWordLanguage( aTmp
, nLang
);
250 pAlt
->SetFailureType( SpellFailure::SPELLING_ERROR
);
251 pAlt
->SetAlternatives( aStr
);
257 sal_Unicode cChar
= aTmp
.GetChar( 0 );
258 if (cChar
== (sal_Unicode
) 's' || cChar
== (sal_Unicode
) 'S')
260 sal_Unicode cNewChar
= cChar
== (sal_Unicode
) 's' ?
261 (sal_Unicode
) 'S': (sal_Unicode
) 's';
262 aTmp
.GetBufferAccess()[0] = cNewChar
;
263 xRes
= new SpellAlternatives( aTmp
, nLang
,
264 SpellFailure::CAPTION_ERROR
, ::com::sun::star::uno::Sequence
< ::rtl::OUString
>() );
273 Reference
< XSpellAlternatives
> SAL_CALL
274 SpellChecker::spell( const OUString
& rWord
, const Locale
& rLocale
,
275 const PropertyValues
& rProperties
)
276 throw(IllegalArgumentException
, RuntimeException
)
278 MutexGuard
aGuard( GetLinguMutex() );
280 if (rLocale
== Locale() || !rWord
.getLength())
283 if (!hasLocale( rLocale
))
284 #ifdef LINGU_EXCEPTIONS
285 throw( IllegalArgumentException() );
290 Reference
< XSpellAlternatives
> xAlt
;
291 if (!isValid( rWord
, rLocale
, rProperties
))
293 xAlt
= GetProposals( rWord
, rLocale
);
299 Reference
< XInterface
> SAL_CALL
SpellChecker_CreateInstance(
300 const Reference
< XMultiServiceFactory
> & )
303 Reference
< XInterface
> xService
= (cppu::OWeakObject
*) new SpellChecker
;
309 SpellChecker::addLinguServiceEventListener(
310 const Reference
< XLinguServiceEventListener
>& rxLstnr
)
311 throw(RuntimeException
)
313 MutexGuard
aGuard( GetLinguMutex() );
315 sal_Bool bRes
= sal_False
;
316 if (!bDisposing
&& rxLstnr
.is())
318 bRes
= GetPropHelper().addLinguServiceEventListener( rxLstnr
);
325 SpellChecker::removeLinguServiceEventListener(
326 const Reference
< XLinguServiceEventListener
>& rxLstnr
)
327 throw(RuntimeException
)
329 MutexGuard
aGuard( GetLinguMutex() );
331 sal_Bool bRes
= sal_False
;
332 if (!bDisposing
&& rxLstnr
.is())
334 DBG_ASSERT( xPropHelper
.is(), "xPropHelper non existent" );
335 bRes
= GetPropHelper().removeLinguServiceEventListener( rxLstnr
);
342 SpellChecker::getServiceDisplayName( const Locale
& )
343 throw(RuntimeException
)
345 MutexGuard
aGuard( GetLinguMutex() );
346 return A2OU( "OpenOffice example spellchecker" );
351 SpellChecker::initialize( const Sequence
< Any
>& rArguments
)
352 throw(Exception
, RuntimeException
)
354 MutexGuard
aGuard( GetLinguMutex() );
358 sal_Int32 nLen
= rArguments
.getLength();
361 Reference
< XPropertySet
> xPropSet
;
362 rArguments
.getConstArray()[0] >>= xPropSet
;
364 //! Pointer allows for access of the non-UNO functions.
365 //! And the reference to the UNO-functions while increasing
366 //! the ref-count and will implicitly free the memory
367 //! when the object is not longer used.
368 pPropHelper
= new PropertyHelper_Spell( (XSpellChecker
*) this, xPropSet
);
369 xPropHelper
= pPropHelper
;
370 pPropHelper
->AddAsPropListener(); //! after a reference is established
373 OSL_FAIL( "wrong number of arguments in sequence" );
379 SpellChecker::dispose()
380 throw(RuntimeException
)
382 MutexGuard
aGuard( GetLinguMutex() );
386 bDisposing
= sal_True
;
387 EventObject
aEvtObj( (XSpellChecker
*) this );
388 aEvtListeners
.disposeAndClear( aEvtObj
);
394 SpellChecker::addEventListener( const Reference
< XEventListener
>& rxListener
)
395 throw(RuntimeException
)
397 MutexGuard
aGuard( GetLinguMutex() );
399 if (!bDisposing
&& rxListener
.is())
400 aEvtListeners
.addInterface( rxListener
);
405 SpellChecker::removeEventListener( const Reference
< XEventListener
>& rxListener
)
406 throw(RuntimeException
)
408 MutexGuard
aGuard( GetLinguMutex() );
410 if (!bDisposing
&& rxListener
.is())
411 aEvtListeners
.removeInterface( rxListener
);
415 // Service specific part
417 OUString SAL_CALL
SpellChecker::getImplementationName()
418 throw(RuntimeException
)
420 MutexGuard
aGuard( GetLinguMutex() );
421 return getImplementationName_Static();
425 sal_Bool SAL_CALL
SpellChecker::supportsService( const OUString
& ServiceName
)
426 throw(RuntimeException
)
428 MutexGuard
aGuard( GetLinguMutex() );
430 Sequence
< OUString
> aSNL
= getSupportedServiceNames();
431 const OUString
* pArray
= aSNL
.getConstArray();
432 for( sal_Int32 i
= 0; i
< aSNL
.getLength(); i
++ )
433 if( pArray
[i
] == ServiceName
)
439 Sequence
< OUString
> SAL_CALL
SpellChecker::getSupportedServiceNames()
440 throw(RuntimeException
)
442 MutexGuard
aGuard( GetLinguMutex() );
443 return getSupportedServiceNames_Static();
447 Sequence
< OUString
> SpellChecker::getSupportedServiceNames_Static()
450 MutexGuard
aGuard( GetLinguMutex() );
452 Sequence
< OUString
> aSNS( 1 ); // more than 1 service possible
453 aSNS
.getArray()[0] = A2OU( SN_SPELLCHECKER
);
458 sal_Bool SAL_CALL
SpellChecker_writeInfo(
459 void * /*pServiceManager*/, registry::XRegistryKey
* pRegistryKey
)
464 aImpl
+= SpellChecker::getImplementationName_Static().getStr();
465 aImpl
.AppendAscii( "/UNO/SERVICES" );
466 Reference
< registry::XRegistryKey
> xNewKey
=
467 pRegistryKey
->createKey( aImpl
);
468 Sequence
< OUString
> aServices
=
469 SpellChecker::getSupportedServiceNames_Static();
470 for( sal_Int32 i
= 0; i
< aServices
.getLength(); i
++ )
471 xNewKey
->createKey( aServices
.getConstArray()[i
] );
482 void * SAL_CALL
SpellChecker_getFactory( const sal_Char
* pImplName
,
483 XMultiServiceFactory
* pServiceManager
, void * )
486 if ( !SpellChecker::getImplementationName_Static().compareToAscii( pImplName
) )
488 Reference
< XSingleServiceFactory
> xFactory
=
489 cppu::createOneInstanceFactory(
491 SpellChecker::getImplementationName_Static(),
492 SpellChecker_CreateInstance
,
493 SpellChecker::getSupportedServiceNames_Static());
494 // acquire, because we return an interface pointer instead of a reference
496 pRet
= xFactory
.get();
503 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */