xref: /trunk/main/linguistic/workben/sspellimp.cxx (revision 91144cd0085a7583d2099b982122deb2184ab956)
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