1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_linguistic.hxx"
26 #include <com/sun/star/uno/Reference.h>
27 #include <com/sun/star/linguistic2/XSearchableDictionaryList.hpp>
28
29 #include <com/sun/star/linguistic2/SpellFailure.hpp>
30 #include <cppuhelper/factory.hxx> // helper for factories
31 #include <com/sun/star/registry/XRegistryKey.hpp>
32 #include <tools/debug.hxx>
33 #include <unotools/processfactory.hxx>
34 #include <osl/mutex.hxx>
35
36 #ifndef _SPELLIMP_HXX
37 #include <sspellimp.hxx>
38 #endif
39
40 #include "linguistic/lngprops.hxx"
41 #include "linguistic/spelldta.hxx"
42
43 using namespace utl;
44 using namespace osl;
45 using namespace rtl;
46 using namespace com::sun::star;
47 using namespace com::sun::star::beans;
48 using namespace com::sun::star::lang;
49 using namespace com::sun::star::uno;
50 using namespace com::sun::star::linguistic2;
51 using namespace linguistic;
52
53
54 ///////////////////////////////////////////////////////////////////////////
55
operator ==(const Locale & rL1,const Locale & rL2)56 BOOL operator == ( const Locale &rL1, const Locale &rL2 )
57 {
58 return rL1.Language == rL2.Language &&
59 rL1.Country == rL2.Country &&
60 rL1.Variant == rL2.Variant;
61 }
62
63 ///////////////////////////////////////////////////////////////////////////
64
65
SpellChecker()66 SpellChecker::SpellChecker() :
67 aEvtListeners ( GetLinguMutex() )
68 {
69 bDisposing = FALSE;
70 pPropHelper = NULL;
71 }
72
73
~SpellChecker()74 SpellChecker::~SpellChecker()
75 {
76 if (pPropHelper)
77 pPropHelper->RemoveAsPropListener();
78 }
79
80
GetPropHelper_Impl()81 PropertyHelper_Spell & SpellChecker::GetPropHelper_Impl()
82 {
83 if (!pPropHelper)
84 {
85 Reference< XPropertySet > xPropSet( GetLinguProperties(), UNO_QUERY );
86
87 pPropHelper = new PropertyHelper_Spell( (XSpellChecker *) this, xPropSet );
88 xPropHelper = pPropHelper;
89 pPropHelper->AddAsPropListener(); //! after a reference is established
90 }
91 return *pPropHelper;
92 }
93
94
getLocales()95 Sequence< Locale > SAL_CALL SpellChecker::getLocales()
96 throw(RuntimeException)
97 {
98 MutexGuard aGuard( GetLinguMutex() );
99
100 if (!aSuppLocales.getLength())
101 {
102 aSuppLocales.realloc( 3 );
103 Locale *pLocale = aSuppLocales.getArray();
104 pLocale[0] = Locale( A2OU("en"), A2OU("US"), OUString() );
105 pLocale[1] = Locale( A2OU("de"), A2OU("DE"), OUString() );
106 pLocale[2] = Locale( A2OU("de"), A2OU("CH"), OUString() );
107 }
108
109 return aSuppLocales;
110 }
111
112
hasLocale(const Locale & rLocale)113 sal_Bool SAL_CALL SpellChecker::hasLocale(const Locale& rLocale)
114 throw(RuntimeException)
115 {
116 MutexGuard aGuard( GetLinguMutex() );
117
118 BOOL bRes = FALSE;
119 if (!aSuppLocales.getLength())
120 getLocales();
121 INT32 nLen = aSuppLocales.getLength();
122 for (INT32 i = 0; i < nLen; ++i)
123 {
124 const Locale *pLocale = aSuppLocales.getConstArray();
125 if (rLocale == pLocale[i])
126 {
127 bRes = TRUE;
128 break;
129 }
130 }
131 return bRes;
132 }
133
134
GetSpellFailure(const OUString & rWord,const Locale & rLocale)135 INT16 SpellChecker::GetSpellFailure( const OUString &rWord, const Locale &rLocale )
136 {
137 // Checks wether a word is OK in a given language (Locale) or not, and
138 // provides a failure type for the incorrect ones.
139 // - words with "liss" (case sensitiv) as substring will be negative.
140 // - words with 'x' or 'X' will have incorrect spelling.
141 // - words with 's' or 'S' as first letter will have the wrong caption.
142 // - all other words will be OK.
143
144 INT16 nRes = -1;
145
146 String aTmp( rWord );
147 if (aTmp.Len())
148 {
149 if (STRING_NOTFOUND != aTmp.SearchAscii( "liss" ))
150 {
151 nRes = SpellFailure::IS_NEGATIVE_WORD;
152 }
153 else if (STRING_NOTFOUND != aTmp.Search( (sal_Unicode) 'x' ) ||
154 STRING_NOTFOUND != aTmp.Search( (sal_Unicode) 'X' ))
155 {
156 nRes = SpellFailure::SPELLING_ERROR;
157 }
158 else
159 {
160 sal_Unicode cChar = aTmp.GetChar( 0 );
161 if (cChar == (sal_Unicode) 's' || cChar == (sal_Unicode) 'S')
162 nRes = SpellFailure::CAPTION_ERROR;
163 }
164 }
165
166 return nRes;
167 }
168
169
170 sal_Bool SAL_CALL
isValid(const OUString & rWord,const Locale & rLocale,const PropertyValues & rProperties)171 SpellChecker::isValid( const OUString& rWord, const Locale& rLocale,
172 const PropertyValues& rProperties )
173 throw(IllegalArgumentException, RuntimeException)
174 {
175 MutexGuard aGuard( GetLinguMutex() );
176
177 if (rLocale == Locale() || !rWord.getLength())
178 return TRUE;
179
180 if (!hasLocale( rLocale ))
181 #ifdef LINGU_EXCEPTIONS
182 throw( IllegalArgumentException() );
183 #else
184 return TRUE;
185 #endif
186
187 // Get property values to be used.
188 // These are be the default values set in the SN_LINGU_PROPERTIES
189 // PropertySet which are overridden by the supplied ones from the
190 // last argument.
191 // You'll probably like to use a simplier solution than the provided
192 // one using the PropertyHelper_Spell.
193 PropertyHelper_Spell &rHelper = GetPropHelper();
194 rHelper.SetTmpPropVals( rProperties );
195
196 INT16 nFailure = GetSpellFailure( rWord, rLocale );
197 if (nFailure != -1)
198 {
199 INT16 nLang = LocaleToLanguage( rLocale );
200 // postprocess result for errors that should be ignored
201 if ( (!rHelper.IsSpellUpperCase() && IsUpper( rWord, nLang ))
202 || (!rHelper.IsSpellWithDigits() && HasDigits( rWord ))
203 || (!rHelper.IsSpellCapitalization()
204 && nFailure == SpellFailure::CAPTION_ERROR)
205 )
206 nFailure = -1;
207 }
208 return nFailure == -1;
209 }
210
211
212 Reference< XSpellAlternatives >
GetProposals(const OUString & rWord,const Locale & rLocale)213 SpellChecker::GetProposals( const OUString &rWord, const Locale &rLocale )
214 {
215 // Retrieves the return values for the 'spell' function call in case
216 // of a misspelled word.
217 // Especially it may give a list of suggested (correct) words:
218 // - a "liss" substring will be replaced by "liz".
219 // - 'x' or 'X' will be replaced by 'u' or 'U' for the first proposal
220 // and they will be removed from the word for the second proposal.
221 // - 's' or 'S' as first letter will be changed to the other caption.
222
223 Reference< XSpellAlternatives > xRes;
224
225 String aTmp( rWord );
226 if (aTmp.Len())
227 {
228 INT16 nLang = LocaleToLanguage( rLocale );
229
230 if (STRING_NOTFOUND != aTmp.SearchAscii( "liss" ))
231 {
232 aTmp.SearchAndReplaceAllAscii( "liss", A2OU("liz") );
233 xRes = new SpellAlternatives( aTmp, nLang,
234 SpellFailure::IS_NEGATIVE_WORD, aTmp );
235 }
236 else if (STRING_NOTFOUND != aTmp.Search( (sal_Unicode) 'x' ) ||
237 STRING_NOTFOUND != aTmp.Search( (sal_Unicode) 'X' ))
238 {
239 Sequence< OUString > aStr( 2 );
240 OUString *pStr = aStr.getArray();
241 String aAlt1( aTmp ),
242 aAlt2( aTmp );
243 aAlt1.SearchAndReplaceAll( (sal_Unicode) 'x', (sal_Unicode) 'u');
244 aAlt1.SearchAndReplaceAll( (sal_Unicode) 'X', (sal_Unicode) 'U');
245 aAlt2.EraseAllChars( (sal_Unicode) 'x' );
246 aAlt2.EraseAllChars( (sal_Unicode) 'X' );
247 pStr[0] = aAlt1;
248 pStr[1] = aAlt2;
249
250 SpellAlternatives *pAlt = new SpellAlternatives;
251 pAlt->SetWordLanguage( aTmp, nLang );
252 pAlt->SetFailureType( SpellFailure::SPELLING_ERROR );
253 pAlt->SetAlternatives( aStr );
254
255 xRes = pAlt;
256 }
257 else
258 {
259 sal_Unicode cChar = aTmp.GetChar( 0 );
260 if (cChar == (sal_Unicode) 's' || cChar == (sal_Unicode) 'S')
261 {
262 sal_Unicode cNewChar = cChar == (sal_Unicode) 's' ?
263 (sal_Unicode) 'S': (sal_Unicode) 's';
264 aTmp.GetBufferAccess()[0] = cNewChar;
265 xRes = new SpellAlternatives( aTmp, nLang,
266 SpellFailure::CAPTION_ERROR, aTmp );
267 }
268 }
269 }
270
271 return xRes;
272 }
273
274
275 Reference< XSpellAlternatives > SAL_CALL
spell(const OUString & rWord,const Locale & rLocale,const PropertyValues & rProperties)276 SpellChecker::spell( const OUString& rWord, const Locale& rLocale,
277 const PropertyValues& rProperties )
278 throw(IllegalArgumentException, RuntimeException)
279 {
280 MutexGuard aGuard( GetLinguMutex() );
281
282 if (rLocale == Locale() || !rWord.getLength())
283 return NULL;
284
285 if (!hasLocale( rLocale ))
286 #ifdef LINGU_EXCEPTIONS
287 throw( IllegalArgumentException() );
288 #else
289 return NULL;
290 #endif
291
292 Reference< XSpellAlternatives > xAlt;
293 if (!isValid( rWord, rLocale, rProperties ))
294 {
295 xAlt = GetProposals( rWord, rLocale );
296 }
297 return xAlt;
298 }
299
300
SpellChecker_CreateInstance(const Reference<XMultiServiceFactory> & rSMgr)301 Reference< XInterface > SAL_CALL SpellChecker_CreateInstance(
302 const Reference< XMultiServiceFactory > & rSMgr )
303 throw(Exception)
304 {
305 Reference< XInterface > xService = (cppu::OWeakObject*) new SpellChecker;
306 return xService;
307 }
308
309
310 sal_Bool SAL_CALL
addLinguServiceEventListener(const Reference<XLinguServiceEventListener> & rxLstnr)311 SpellChecker::addLinguServiceEventListener(
312 const Reference< XLinguServiceEventListener >& rxLstnr )
313 throw(RuntimeException)
314 {
315 MutexGuard aGuard( GetLinguMutex() );
316
317 BOOL bRes = FALSE;
318 if (!bDisposing && rxLstnr.is())
319 {
320 bRes = GetPropHelper().addLinguServiceEventListener( rxLstnr );
321 }
322 return bRes;
323 }
324
325
326 sal_Bool SAL_CALL
removeLinguServiceEventListener(const Reference<XLinguServiceEventListener> & rxLstnr)327 SpellChecker::removeLinguServiceEventListener(
328 const Reference< XLinguServiceEventListener >& rxLstnr )
329 throw(RuntimeException)
330 {
331 MutexGuard aGuard( GetLinguMutex() );
332
333 BOOL bRes = FALSE;
334 if (!bDisposing && rxLstnr.is())
335 {
336 DBG_ASSERT( xPropHelper.is(), "xPropHelper non existent" );
337 bRes = GetPropHelper().removeLinguServiceEventListener( rxLstnr );
338 }
339 return bRes;
340 }
341
342
343 OUString SAL_CALL
getServiceDisplayName(const Locale & rLocale)344 SpellChecker::getServiceDisplayName( const Locale& rLocale )
345 throw(RuntimeException)
346 {
347 MutexGuard aGuard( GetLinguMutex() );
348 return A2OU( "OpenOffice example spellchecker" );
349 }
350
351
352 void SAL_CALL
initialize(const Sequence<Any> & rArguments)353 SpellChecker::initialize( const Sequence< Any >& rArguments )
354 throw(Exception, RuntimeException)
355 {
356 MutexGuard aGuard( GetLinguMutex() );
357
358 if (!pPropHelper)
359 {
360 INT32 nLen = rArguments.getLength();
361 if (2 == nLen)
362 {
363 Reference< XPropertySet > xPropSet;
364 rArguments.getConstArray()[0] >>= xPropSet;
365 //rArguments.getConstArray()[1] >>= xDicList;
366
367 //! Pointer allows for access of the non-UNO functions.
368 //! And the reference to the UNO-functions while increasing
369 //! the ref-count and will implicitly free the memory
370 //! when the object is not longer used.
371 pPropHelper = new PropertyHelper_Spell( (XSpellChecker *) this, xPropSet );
372 xPropHelper = pPropHelper;
373 pPropHelper->AddAsPropListener(); //! after a reference is established
374 }
375 else
376 DBG_ERROR( "wrong number of arguments in sequence" );
377 }
378 }
379
380
381 void SAL_CALL
dispose()382 SpellChecker::dispose()
383 throw(RuntimeException)
384 {
385 MutexGuard aGuard( GetLinguMutex() );
386
387 if (!bDisposing)
388 {
389 bDisposing = TRUE;
390 EventObject aEvtObj( (XSpellChecker *) this );
391 aEvtListeners.disposeAndClear( aEvtObj );
392 }
393 }
394
395
396 void SAL_CALL
addEventListener(const Reference<XEventListener> & rxListener)397 SpellChecker::addEventListener( const Reference< XEventListener >& rxListener )
398 throw(RuntimeException)
399 {
400 MutexGuard aGuard( GetLinguMutex() );
401
402 if (!bDisposing && rxListener.is())
403 aEvtListeners.addInterface( rxListener );
404 }
405
406
407 void SAL_CALL
removeEventListener(const Reference<XEventListener> & rxListener)408 SpellChecker::removeEventListener( const Reference< XEventListener >& rxListener )
409 throw(RuntimeException)
410 {
411 MutexGuard aGuard( GetLinguMutex() );
412
413 if (!bDisposing && rxListener.is())
414 aEvtListeners.removeInterface( rxListener );
415 }
416
417
418 ///////////////////////////////////////////////////////////////////////////
419 // Service specific part
420 //
421
getImplementationName()422 OUString SAL_CALL SpellChecker::getImplementationName()
423 throw(RuntimeException)
424 {
425 MutexGuard aGuard( GetLinguMutex() );
426 return getImplementationName_Static();
427 }
428
429
supportsService(const OUString & ServiceName)430 sal_Bool SAL_CALL SpellChecker::supportsService( const OUString& ServiceName )
431 throw(RuntimeException)
432 {
433 MutexGuard aGuard( GetLinguMutex() );
434
435 Sequence< OUString > aSNL = getSupportedServiceNames();
436 const OUString * pArray = aSNL.getConstArray();
437 for( INT32 i = 0; i < aSNL.getLength(); i++ )
438 if( pArray[i] == ServiceName )
439 return TRUE;
440 return FALSE;
441 }
442
443
getSupportedServiceNames()444 Sequence< OUString > SAL_CALL SpellChecker::getSupportedServiceNames()
445 throw(RuntimeException)
446 {
447 MutexGuard aGuard( GetLinguMutex() );
448 return getSupportedServiceNames_Static();
449 }
450
451
getSupportedServiceNames_Static()452 Sequence< OUString > SpellChecker::getSupportedServiceNames_Static()
453 throw()
454 {
455 MutexGuard aGuard( GetLinguMutex() );
456
457 Sequence< OUString > aSNS( 1 ); // auch mehr als 1 Service moeglich
458 aSNS.getArray()[0] = A2OU( SN_SPELLCHECKER );
459 return aSNS;
460 }
461
462
SpellChecker_writeInfo(void *,registry::XRegistryKey * pRegistryKey)463 sal_Bool SAL_CALL SpellChecker_writeInfo(
464 void * /*pServiceManager*/, registry::XRegistryKey * pRegistryKey )
465 {
466 try
467 {
468 String aImpl( '/' );
469 aImpl += SpellChecker::getImplementationName_Static().getStr();
470 aImpl.AppendAscii( "/UNO/SERVICES" );
471 Reference< registry::XRegistryKey > xNewKey =
472 pRegistryKey->createKey( aImpl );
473 Sequence< OUString > aServices =
474 SpellChecker::getSupportedServiceNames_Static();
475 for( INT32 i = 0; i < aServices.getLength(); i++ )
476 xNewKey->createKey( aServices.getConstArray()[i] );
477
478 return sal_True;
479 }
480 catch(Exception &)
481 {
482 return sal_False;
483 }
484 }
485
486
SpellChecker_getFactory(const sal_Char * pImplName,XMultiServiceFactory * pServiceManager,void *)487 void * SAL_CALL SpellChecker_getFactory( const sal_Char * pImplName,
488 XMultiServiceFactory * pServiceManager, void * )
489 {
490 void * pRet = 0;
491 if ( !SpellChecker::getImplementationName_Static().compareToAscii( pImplName ) )
492 {
493 Reference< XSingleServiceFactory > xFactory =
494 cppu::createOneInstanceFactory(
495 pServiceManager,
496 SpellChecker::getImplementationName_Static(),
497 SpellChecker_CreateInstance,
498 SpellChecker::getSupportedServiceNames_Static());
499 // acquire, because we return an interface pointer instead of a reference
500 xFactory->acquire();
501 pRet = xFactory.get();
502 }
503 return pRet;
504 }
505
506
507 ///////////////////////////////////////////////////////////////////////////
508
509