xref: /AOO41X/main/linguistic/workben/sspellimp.cxx (revision 3b8558fd12b4776aa3c54a9fb2322537ca2a5e4c)
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