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 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 66 SpellChecker::SpellChecker() : 67 aEvtListeners ( GetLinguMutex() ) 68 { 69 bDisposing = FALSE; 70 pPropHelper = NULL; 71 } 72 73 74 SpellChecker::~SpellChecker() 75 { 76 if (pPropHelper) 77 pPropHelper->RemoveAsPropListener(); 78 } 79 80 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 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 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 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 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 simpler 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 > 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 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 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 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 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 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 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 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 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 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 422 OUString SAL_CALL SpellChecker::getImplementationName() 423 throw(RuntimeException) 424 { 425 MutexGuard aGuard( GetLinguMutex() ); 426 return getImplementationName_Static(); 427 } 428 429 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 444 Sequence< OUString > SAL_CALL SpellChecker::getSupportedServiceNames() 445 throw(RuntimeException) 446 { 447 MutexGuard aGuard( GetLinguMutex() ); 448 return getSupportedServiceNames_Static(); 449 } 450 451 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 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 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