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 <cppuhelper/factory.hxx>
25 #include <cppuhelper/supportsservice.hxx>
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
;
44 sal_Bool
operator == ( const Locale
&rL1
, const Locale
&rL2
)
46 return rL1
.Language
== rL2
.Language
&&
47 rL1
.Country
== rL2
.Country
&&
48 rL1
.Variant
== rL2
.Variant
;
52 SpellChecker::SpellChecker() :
53 aEvtListeners ( GetLinguMutex() ),
60 SpellChecker::~SpellChecker()
63 pPropHelper
->RemoveAsPropListener();
67 PropertyHelper_Spell
& SpellChecker::GetPropHelper_Impl()
71 Reference
< XLinguProperties
> xPropSet
= GetLinguProperties();
73 pPropHelper
= new PropertyHelper_Spell( (XSpellChecker
*) this, xPropSet
);
74 xPropHelper
= pPropHelper
;
75 pPropHelper
->AddAsPropListener(); //! after a reference is established
81 Sequence
< Locale
> SAL_CALL
SpellChecker::getLocales()
82 throw(RuntimeException
)
84 MutexGuard
aGuard( GetLinguMutex() );
86 if (!aSuppLocales
.getLength())
88 aSuppLocales
.realloc( 3 );
89 Locale
*pLocale
= aSuppLocales
.getArray();
90 pLocale
[0] = Locale( "en", "US", OUString() );
91 pLocale
[1] = Locale( "de", "DE", OUString() );
92 pLocale
[2] = Locale( "de", "CH", OUString() );
99 sal_Bool SAL_CALL
SpellChecker::hasLocale(const Locale
& rLocale
)
100 throw(RuntimeException
)
102 MutexGuard
aGuard( GetLinguMutex() );
104 sal_Bool bRes
= sal_False
;
105 if (!aSuppLocales
.getLength())
107 sal_Int32 nLen
= aSuppLocales
.getLength();
108 for (sal_Int32 i
= 0; i
< nLen
; ++i
)
110 const Locale
*pLocale
= aSuppLocales
.getConstArray();
111 if (rLocale
== pLocale
[i
])
121 sal_Int16
SpellChecker::GetSpellFailure( const OUString
&rWord
, const Locale
& )
123 // Checks whether a word is OK in a given language (Locale) or not, and
124 // provides a failure type for the incorrect ones.
125 // - words with "liss" (case sensitive) as substring will be negative.
126 // - words with 'x' or 'X' will have incorrect spelling.
127 // - words with 's' or 'S' as first letter will have the wrong caption.
128 // - all other words will be OK.
132 String
aTmp( rWord
);
135 if (-1 != aTmp
.indexOf( "liss" ))
137 nRes
= SpellFailure::IS_NEGATIVE_WORD
;
139 else if (-1 != aTmp
.indexOf( 'x' ) ||
140 -1 != aTmp
.indexOf( 'X' ))
142 nRes
= SpellFailure::SPELLING_ERROR
;
146 sal_Unicode cChar
= aTmp
.GetChar( 0 );
147 if (cChar
== 's' || cChar
== 'S')
148 nRes
= SpellFailure::CAPTION_ERROR
;
157 SpellChecker::isValid( const OUString
& rWord
, const Locale
& rLocale
,
158 const PropertyValues
& rProperties
)
159 throw(IllegalArgumentException
, RuntimeException
)
161 MutexGuard
aGuard( GetLinguMutex() );
163 if (rLocale
== Locale() || !rWord
.getLength())
166 if (!hasLocale( rLocale
))
167 #ifdef LINGU_EXCEPTIONS
168 throw( IllegalArgumentException() );
173 // Get property values to be used.
174 // These are be the default values set in the SN_LINGU_PROPERTIES
175 // PropertySet which are overridden by the supplied ones from the
177 // You'll probably like to use a simpler solution than the provided
178 // one using the PropertyHelper_Spell.
179 PropertyHelper_Spell
&rHelper
= GetPropHelper();
180 rHelper
.SetTmpPropVals( rProperties
);
182 sal_Int16 nFailure
= GetSpellFailure( rWord
, rLocale
);
185 sal_Int16 nLang
= LinguLocaleToLanguage( rLocale
);
186 // postprocess result for errors that should be ignored
187 if ( (!rHelper
.IsSpellUpperCase() && IsUpper( rWord
, nLang
))
188 || (!rHelper
.IsSpellWithDigits() && HasDigits( rWord
))
189 || (!rHelper
.IsSpellCapitalization()
190 && nFailure
== SpellFailure::CAPTION_ERROR
)
194 return nFailure
== -1;
198 Reference
< XSpellAlternatives
>
199 SpellChecker::GetProposals( const OUString
&rWord
, const Locale
&rLocale
)
201 // Retrieves the return values for the 'spell' function call in case
202 // of a misspelled word.
203 // Especially it may give a list of suggested (correct) words:
204 // - a "liss" substring will be replaced by "liz".
205 // - 'x' or 'X' will be replaced by 'u' or 'U' for the first proposal
206 // and they will be removed from the word for the second proposal.
207 // - 's' or 'S' as first letter will be changed to the other caption.
209 Reference
< XSpellAlternatives
> xRes
;
211 String
aTmp( rWord
);
214 sal_Int16 nLang
= LinguLocaleToLanguage( rLocale
);
216 if (-1 != aTmp
.indexOf( "liss" ))
218 aTmp
.SearchAndReplaceAllAscii( "liss", "liz" );
219 xRes
= new SpellAlternatives( aTmp
, nLang
,
220 SpellFailure::IS_NEGATIVE_WORD
, css::uno::Sequence
< OUString
>() );
222 else if (-1 != aTmp
.indexOf( 'x' ) ||
223 -1 != aTmp
.indexOf( 'X' ))
225 Sequence
< OUString
> aStr( 2 );
226 OUString
*pStr
= aStr
.getArray();
227 String
aAlt1( aTmp
),
229 aAlt1
.SearchAndReplaceAll( 'x', 'u');
230 aAlt1
.SearchAndReplaceAll( 'X', 'U');
231 aAlt2
= aAlt2
.replaceAll("x", "").replaceAll("X", "");
235 SpellAlternatives
*pAlt
= new SpellAlternatives
;
236 pAlt
->SetWordLanguage( aTmp
, nLang
);
237 pAlt
->SetFailureType( SpellFailure::SPELLING_ERROR
);
238 pAlt
->SetAlternatives( aStr
);
244 sal_Unicode cChar
= aTmp
.GetChar( 0 );
245 if (cChar
== 's' || cChar
== 'S')
247 sal_Unicode cNewChar
= cChar
== 's' ?
249 aTmp
.GetBufferAccess()[0] = cNewChar
;
250 xRes
= new SpellAlternatives( aTmp
, nLang
,
251 SpellFailure::CAPTION_ERROR
, css::uno::Sequence
< OUString
>() );
260 Reference
< XSpellAlternatives
> SAL_CALL
261 SpellChecker::spell( const OUString
& rWord
, const Locale
& rLocale
,
262 const PropertyValues
& rProperties
)
263 throw(IllegalArgumentException
, RuntimeException
)
265 MutexGuard
aGuard( GetLinguMutex() );
267 if (rLocale
== Locale() || !rWord
.getLength())
270 if (!hasLocale( rLocale
))
271 #ifdef LINGU_EXCEPTIONS
272 throw( IllegalArgumentException() );
277 Reference
< XSpellAlternatives
> xAlt
;
278 if (!isValid( rWord
, rLocale
, rProperties
))
280 xAlt
= GetProposals( rWord
, rLocale
);
286 Reference
< XInterface
> SAL_CALL
SpellChecker_CreateInstance(
287 const Reference
< XMultiServiceFactory
> & )
290 Reference
< XInterface
> xService
= (cppu::OWeakObject
*) new SpellChecker
;
296 SpellChecker::addLinguServiceEventListener(
297 const Reference
< XLinguServiceEventListener
>& rxLstnr
)
298 throw(RuntimeException
)
300 MutexGuard
aGuard( GetLinguMutex() );
302 sal_Bool bRes
= sal_False
;
303 if (!bDisposing
&& rxLstnr
.is())
305 bRes
= GetPropHelper().addLinguServiceEventListener( rxLstnr
);
312 SpellChecker::removeLinguServiceEventListener(
313 const Reference
< XLinguServiceEventListener
>& rxLstnr
)
314 throw(RuntimeException
)
316 MutexGuard
aGuard( GetLinguMutex() );
318 sal_Bool bRes
= sal_False
;
319 if (!bDisposing
&& rxLstnr
.is())
321 DBG_ASSERT( xPropHelper
.is(), "xPropHelper non existent" );
322 bRes
= GetPropHelper().removeLinguServiceEventListener( rxLstnr
);
329 SpellChecker::getServiceDisplayName( const Locale
& )
330 throw(RuntimeException
)
332 MutexGuard
aGuard( GetLinguMutex() );
333 return OUString( "OpenOffice example spellchecker" );
338 SpellChecker::initialize( const Sequence
< Any
>& rArguments
)
339 throw(Exception
, RuntimeException
)
341 MutexGuard
aGuard( GetLinguMutex() );
345 sal_Int32 nLen
= rArguments
.getLength();
348 Reference
< XPropertySet
> xPropSet
;
349 rArguments
.getConstArray()[0] >>= xPropSet
;
351 //! Pointer allows for access of the non-UNO functions.
352 //! And the reference to the UNO-functions while increasing
353 //! the ref-count and will implicitly free the memory
354 //! when the object is no longer used.
355 pPropHelper
= new PropertyHelper_Spell( (XSpellChecker
*) this, xPropSet
);
356 xPropHelper
= pPropHelper
;
357 pPropHelper
->AddAsPropListener(); //! after a reference is established
360 OSL_FAIL( "wrong number of arguments in sequence" );
366 SpellChecker::dispose()
367 throw(RuntimeException
)
369 MutexGuard
aGuard( GetLinguMutex() );
373 bDisposing
= sal_True
;
374 EventObject
aEvtObj( (XSpellChecker
*) this );
375 aEvtListeners
.disposeAndClear( aEvtObj
);
381 SpellChecker::addEventListener( const Reference
< XEventListener
>& rxListener
)
382 throw(RuntimeException
)
384 MutexGuard
aGuard( GetLinguMutex() );
386 if (!bDisposing
&& rxListener
.is())
387 aEvtListeners
.addInterface( rxListener
);
392 SpellChecker::removeEventListener( const Reference
< XEventListener
>& rxListener
)
393 throw(RuntimeException
)
395 MutexGuard
aGuard( GetLinguMutex() );
397 if (!bDisposing
&& rxListener
.is())
398 aEvtListeners
.removeInterface( rxListener
);
402 // Service specific part
404 OUString SAL_CALL
SpellChecker::getImplementationName()
405 throw(RuntimeException
)
407 return getImplementationName_Static();
411 sal_Bool SAL_CALL
SpellChecker::supportsService( const OUString
& ServiceName
)
412 throw(RuntimeException
)
414 return cppu::supportsService(this, ServiceName
);
417 Sequence
< OUString
> SAL_CALL
SpellChecker::getSupportedServiceNames()
418 throw(RuntimeException
)
420 return getSupportedServiceNames_Static();
424 Sequence
< OUString
> SpellChecker::getSupportedServiceNames_Static()
427 Sequence
< OUString
> aSNS
{ SN_SPELLCHECKER
};
432 sal_Bool SAL_CALL
SpellChecker_writeInfo(
433 void * /*pServiceManager*/, registry::XRegistryKey
* pRegistryKey
)
437 OUString
aImpl( "/" + SpellChecker::getImplementationName_Static().getStr() +
440 Reference
< registry::XRegistryKey
> xNewKey
=
441 pRegistryKey
->createKey( aImpl
);
442 Sequence
< OUString
> aServices
=
443 SpellChecker::getSupportedServiceNames_Static();
444 for( sal_Int32 i
= 0; i
< aServices
.getLength(); i
++ )
445 xNewKey
->createKey( aServices
.getConstArray()[i
] );
456 void * SAL_CALL
SpellChecker_getFactory( const char * pImplName
,
457 XMultiServiceFactory
* pServiceManager
, void * )
460 if ( SpellChecker::getImplementationName_Static().equalsAscii( pImplName
) )
462 Reference
< XSingleServiceFactory
> xFactory
=
463 cppu::createOneInstanceFactory(
465 SpellChecker::getImplementationName_Static(),
466 SpellChecker_CreateInstance
,
467 SpellChecker::getSupportedServiceNames_Static());
468 // acquire, because we return an interface pointer instead of a reference
470 pRet
= xFactory
.get();
476 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */