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