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 {
97 MutexGuard aGuard( GetLinguMutex() );
98
99 if (!aSuppLocales.getLength())
100 {
101 aSuppLocales.realloc( 3 );
102 Locale *pLocale = aSuppLocales.getArray();
103 pLocale[0] = Locale( A2OU("en"), A2OU("US"), OUString() );
104 pLocale[1] = Locale( A2OU("de"), A2OU("DE"), OUString() );
105 pLocale[2] = Locale( A2OU("de"), A2OU("CH"), OUString() );
106 }
107
108 return aSuppLocales;
109 }
110
111
hasLocale(const Locale & rLocale)112 sal_Bool SAL_CALL SpellChecker::hasLocale(const Locale& rLocale)
113 {
114 MutexGuard aGuard( GetLinguMutex() );
115
116 BOOL bRes = FALSE;
117 if (!aSuppLocales.getLength())
118 getLocales();
119 INT32 nLen = aSuppLocales.getLength();
120 for (INT32 i = 0; i < nLen; ++i)
121 {
122 const Locale *pLocale = aSuppLocales.getConstArray();
123 if (rLocale == pLocale[i])
124 {
125 bRes = TRUE;
126 break;
127 }
128 }
129 return bRes;
130 }
131
132
GetSpellFailure(const OUString & rWord,const Locale & rLocale)133 INT16 SpellChecker::GetSpellFailure( const OUString &rWord, const Locale &rLocale )
134 {
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.
141
142 INT16 nRes = -1;
143
144 String aTmp( rWord );
145 if (aTmp.Len())
146 {
147 if (STRING_NOTFOUND != aTmp.SearchAscii( "liss" ))
148 {
149 nRes = SpellFailure::IS_NEGATIVE_WORD;
150 }
151 else if (STRING_NOTFOUND != aTmp.Search( (sal_Unicode) 'x' ) ||
152 STRING_NOTFOUND != aTmp.Search( (sal_Unicode) 'X' ))
153 {
154 nRes = SpellFailure::SPELLING_ERROR;
155 }
156 else
157 {
158 sal_Unicode cChar = aTmp.GetChar( 0 );
159 if (cChar == (sal_Unicode) 's' || cChar == (sal_Unicode) 'S')
160 nRes = SpellFailure::CAPTION_ERROR;
161 }
162 }
163
164 return nRes;
165 }
166
167
168 sal_Bool SAL_CALL
isValid(const OUString & rWord,const Locale & rLocale,const PropertyValues & rProperties)169 SpellChecker::isValid( const OUString& rWord, const Locale& rLocale,
170 const PropertyValues& rProperties )
171 {
172 MutexGuard aGuard( GetLinguMutex() );
173
174 if (rLocale == Locale() || !rWord.getLength())
175 return TRUE;
176
177 if (!hasLocale( rLocale ))
178 #ifdef LINGU_EXCEPTIONS
179 throw( IllegalArgumentException() );
180 #else
181 return TRUE;
182 #endif
183
184 // Get property values to be used.
185 // These are be the default values set in the SN_LINGU_PROPERTIES
186 // PropertySet which are overridden by the supplied ones from the
187 // last argument.
188 // You'll probably like to use a simpler solution than the provided
189 // one using the PropertyHelper_Spell.
190 PropertyHelper_Spell &rHelper = GetPropHelper();
191 rHelper.SetTmpPropVals( rProperties );
192
193 INT16 nFailure = GetSpellFailure( rWord, rLocale );
194 if (nFailure != -1)
195 {
196 INT16 nLang = LocaleToLanguage( rLocale );
197 // postprocess result for errors that should be ignored
198 if ( (!rHelper.IsSpellUpperCase() && IsUpper( rWord, nLang ))
199 || (!rHelper.IsSpellWithDigits() && HasDigits( rWord ))
200 || (!rHelper.IsSpellCapitalization()
201 && nFailure == SpellFailure::CAPTION_ERROR)
202 )
203 nFailure = -1;
204 }
205 return nFailure == -1;
206 }
207
208
209 Reference< XSpellAlternatives >
GetProposals(const OUString & rWord,const Locale & rLocale)210 SpellChecker::GetProposals( const OUString &rWord, const Locale &rLocale )
211 {
212 // Retrieves the return values for the 'spell' function call in case
213 // of a misspelled word.
214 // Especially it may give a list of suggested (correct) words:
215 // - a "liss" substring will be replaced by "liz".
216 // - 'x' or 'X' will be replaced by 'u' or 'U' for the first proposal
217 // and they will be removed from the word for the second proposal.
218 // - 's' or 'S' as first letter will be changed to the other caption.
219
220 Reference< XSpellAlternatives > xRes;
221
222 String aTmp( rWord );
223 if (aTmp.Len())
224 {
225 INT16 nLang = LocaleToLanguage( rLocale );
226
227 if (STRING_NOTFOUND != aTmp.SearchAscii( "liss" ))
228 {
229 aTmp.SearchAndReplaceAllAscii( "liss", A2OU("liz") );
230 xRes = new SpellAlternatives( aTmp, nLang,
231 SpellFailure::IS_NEGATIVE_WORD, aTmp );
232 }
233 else if (STRING_NOTFOUND != aTmp.Search( (sal_Unicode) 'x' ) ||
234 STRING_NOTFOUND != aTmp.Search( (sal_Unicode) 'X' ))
235 {
236 Sequence< OUString > aStr( 2 );
237 OUString *pStr = aStr.getArray();
238 String aAlt1( aTmp ),
239 aAlt2( aTmp );
240 aAlt1.SearchAndReplaceAll( (sal_Unicode) 'x', (sal_Unicode) 'u');
241 aAlt1.SearchAndReplaceAll( (sal_Unicode) 'X', (sal_Unicode) 'U');
242 aAlt2.EraseAllChars( (sal_Unicode) 'x' );
243 aAlt2.EraseAllChars( (sal_Unicode) 'X' );
244 pStr[0] = aAlt1;
245 pStr[1] = aAlt2;
246
247 SpellAlternatives *pAlt = new SpellAlternatives;
248 pAlt->SetWordLanguage( aTmp, nLang );
249 pAlt->SetFailureType( SpellFailure::SPELLING_ERROR );
250 pAlt->SetAlternatives( aStr );
251
252 xRes = pAlt;
253 }
254 else
255 {
256 sal_Unicode cChar = aTmp.GetChar( 0 );
257 if (cChar == (sal_Unicode) 's' || cChar == (sal_Unicode) 'S')
258 {
259 sal_Unicode cNewChar = cChar == (sal_Unicode) 's' ?
260 (sal_Unicode) 'S': (sal_Unicode) 's';
261 aTmp.GetBufferAccess()[0] = cNewChar;
262 xRes = new SpellAlternatives( aTmp, nLang,
263 SpellFailure::CAPTION_ERROR, aTmp );
264 }
265 }
266 }
267
268 return xRes;
269 }
270
271
272 Reference< XSpellAlternatives > SAL_CALL
spell(const OUString & rWord,const Locale & rLocale,const PropertyValues & rProperties)273 SpellChecker::spell( const OUString& rWord, const Locale& rLocale,
274 const PropertyValues& rProperties )
275 {
276 MutexGuard aGuard( GetLinguMutex() );
277
278 if (rLocale == Locale() || !rWord.getLength())
279 return NULL;
280
281 if (!hasLocale( rLocale ))
282 #ifdef LINGU_EXCEPTIONS
283 throw( IllegalArgumentException() );
284 #else
285 return NULL;
286 #endif
287
288 Reference< XSpellAlternatives > xAlt;
289 if (!isValid( rWord, rLocale, rProperties ))
290 {
291 xAlt = GetProposals( rWord, rLocale );
292 }
293 return xAlt;
294 }
295
296
SpellChecker_CreateInstance(const Reference<XMultiServiceFactory> & rSMgr)297 Reference< XInterface > SAL_CALL SpellChecker_CreateInstance(
298 const Reference< XMultiServiceFactory > & rSMgr )
299 {
300 Reference< XInterface > xService = (cppu::OWeakObject*) new SpellChecker;
301 return xService;
302 }
303
304
305 sal_Bool SAL_CALL
addLinguServiceEventListener(const Reference<XLinguServiceEventListener> & rxLstnr)306 SpellChecker::addLinguServiceEventListener(
307 const Reference< XLinguServiceEventListener >& rxLstnr )
308 {
309 MutexGuard aGuard( GetLinguMutex() );
310
311 BOOL bRes = FALSE;
312 if (!bDisposing && rxLstnr.is())
313 {
314 bRes = GetPropHelper().addLinguServiceEventListener( rxLstnr );
315 }
316 return bRes;
317 }
318
319
320 sal_Bool SAL_CALL
removeLinguServiceEventListener(const Reference<XLinguServiceEventListener> & rxLstnr)321 SpellChecker::removeLinguServiceEventListener(
322 const Reference< XLinguServiceEventListener >& rxLstnr )
323 {
324 MutexGuard aGuard( GetLinguMutex() );
325
326 BOOL bRes = FALSE;
327 if (!bDisposing && rxLstnr.is())
328 {
329 DBG_ASSERT( xPropHelper.is(), "xPropHelper non existent" );
330 bRes = GetPropHelper().removeLinguServiceEventListener( rxLstnr );
331 }
332 return bRes;
333 }
334
335
336 OUString SAL_CALL
getServiceDisplayName(const Locale & rLocale)337 SpellChecker::getServiceDisplayName( const Locale& rLocale )
338 {
339 MutexGuard aGuard( GetLinguMutex() );
340 return A2OU( "OpenOffice example spellchecker" );
341 }
342
343
344 void SAL_CALL
initialize(const Sequence<Any> & rArguments)345 SpellChecker::initialize( const Sequence< Any >& rArguments )
346 {
347 MutexGuard aGuard( GetLinguMutex() );
348
349 if (!pPropHelper)
350 {
351 INT32 nLen = rArguments.getLength();
352 if (2 == nLen)
353 {
354 Reference< XPropertySet > xPropSet;
355 rArguments.getConstArray()[0] >>= xPropSet;
356 //rArguments.getConstArray()[1] >>= xDicList;
357
358 //! Pointer allows for access of the non-UNO functions.
359 //! And the reference to the UNO-functions while increasing
360 //! the ref-count and will implicitly free the memory
361 //! when the object is not longer used.
362 pPropHelper = new PropertyHelper_Spell( (XSpellChecker *) this, xPropSet );
363 xPropHelper = pPropHelper;
364 pPropHelper->AddAsPropListener(); //! after a reference is established
365 }
366 else
367 DBG_ERROR( "wrong number of arguments in sequence" );
368 }
369 }
370
371
372 void SAL_CALL
dispose()373 SpellChecker::dispose()
374 {
375 MutexGuard aGuard( GetLinguMutex() );
376
377 if (!bDisposing)
378 {
379 bDisposing = TRUE;
380 EventObject aEvtObj( (XSpellChecker *) this );
381 aEvtListeners.disposeAndClear( aEvtObj );
382 }
383 }
384
385
386 void SAL_CALL
addEventListener(const Reference<XEventListener> & rxListener)387 SpellChecker::addEventListener( const Reference< XEventListener >& rxListener )
388 {
389 MutexGuard aGuard( GetLinguMutex() );
390
391 if (!bDisposing && rxListener.is())
392 aEvtListeners.addInterface( rxListener );
393 }
394
395
396 void SAL_CALL
removeEventListener(const Reference<XEventListener> & rxListener)397 SpellChecker::removeEventListener( const Reference< XEventListener >& rxListener )
398 {
399 MutexGuard aGuard( GetLinguMutex() );
400
401 if (!bDisposing && rxListener.is())
402 aEvtListeners.removeInterface( rxListener );
403 }
404
405
406 ///////////////////////////////////////////////////////////////////////////
407 // Service specific part
408 //
409
getImplementationName()410 OUString SAL_CALL SpellChecker::getImplementationName()
411 {
412 MutexGuard aGuard( GetLinguMutex() );
413 return getImplementationName_Static();
414 }
415
416
supportsService(const OUString & ServiceName)417 sal_Bool SAL_CALL SpellChecker::supportsService( const OUString& ServiceName )
418 {
419 MutexGuard aGuard( GetLinguMutex() );
420
421 Sequence< OUString > aSNL = getSupportedServiceNames();
422 const OUString * pArray = aSNL.getConstArray();
423 for( INT32 i = 0; i < aSNL.getLength(); i++ )
424 if( pArray[i] == ServiceName )
425 return TRUE;
426 return FALSE;
427 }
428
429
getSupportedServiceNames()430 Sequence< OUString > SAL_CALL SpellChecker::getSupportedServiceNames()
431 {
432 MutexGuard aGuard( GetLinguMutex() );
433 return getSupportedServiceNames_Static();
434 }
435
436
getSupportedServiceNames_Static()437 Sequence< OUString > SpellChecker::getSupportedServiceNames_Static()
438 throw()
439 {
440 MutexGuard aGuard( GetLinguMutex() );
441
442 Sequence< OUString > aSNS( 1 ); // auch mehr als 1 Service moeglich
443 aSNS.getArray()[0] = A2OU( SN_SPELLCHECKER );
444 return aSNS;
445 }
446
447
SpellChecker_writeInfo(void *,registry::XRegistryKey * pRegistryKey)448 sal_Bool SAL_CALL SpellChecker_writeInfo(
449 void * /*pServiceManager*/, registry::XRegistryKey * pRegistryKey )
450 {
451 try
452 {
453 String aImpl( '/' );
454 aImpl += SpellChecker::getImplementationName_Static().getStr();
455 aImpl.AppendAscii( "/UNO/SERVICES" );
456 Reference< registry::XRegistryKey > xNewKey =
457 pRegistryKey->createKey( aImpl );
458 Sequence< OUString > aServices =
459 SpellChecker::getSupportedServiceNames_Static();
460 for( INT32 i = 0; i < aServices.getLength(); i++ )
461 xNewKey->createKey( aServices.getConstArray()[i] );
462
463 return sal_True;
464 }
465 catch(Exception &)
466 {
467 return sal_False;
468 }
469 }
470
471
SpellChecker_getFactory(const sal_Char * pImplName,XMultiServiceFactory * pServiceManager,void *)472 void * SAL_CALL SpellChecker_getFactory( const sal_Char * pImplName,
473 XMultiServiceFactory * pServiceManager, void * )
474 {
475 void * pRet = 0;
476 if ( !SpellChecker::getImplementationName_Static().compareToAscii( pImplName ) )
477 {
478 Reference< XSingleServiceFactory > xFactory =
479 cppu::createOneInstanceFactory(
480 pServiceManager,
481 SpellChecker::getImplementationName_Static(),
482 SpellChecker_CreateInstance,
483 SpellChecker::getSupportedServiceNames_Static());
484 // acquire, because we return an interface pointer instead of a reference
485 xFactory->acquire();
486 pRet = xFactory.get();
487 }
488 return pRet;
489 }
490
491
492 ///////////////////////////////////////////////////////////////////////////
493