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_lingucomponent.hxx" 26 27 #include <com/sun/star/uno/Reference.h> 28 #include <com/sun/star/linguistic2/XSearchableDictionaryList.hpp> 29 30 #include <com/sun/star/linguistic2/SpellFailure.hpp> 31 #include <cppuhelper/factory.hxx> // helper for factories 32 #include <com/sun/star/registry/XRegistryKey.hpp> 33 #include <tools/debug.hxx> 34 #include <unotools/processfactory.hxx> 35 #include <osl/mutex.hxx> 36 #include <com/sun/star/ucb/XSimpleFileAccess.hpp> 37 38 #include <lingutil.hxx> 39 #include <hunspell.hxx> 40 #include <dictmgr.hxx> 41 #include <sspellimp.hxx> 42 43 #include <linguistic/lngprops.hxx> 44 #include <linguistic/spelldta.hxx> 45 #include <i18npool/mslangid.hxx> 46 #include <unotools/pathoptions.hxx> 47 #include <unotools/lingucfg.hxx> 48 #include <unotools/useroptions.hxx> 49 #include <osl/file.hxx> 50 #include <rtl/ustrbuf.hxx> 51 #include <rtl/textenc.h> 52 53 #include <list> 54 #include <set> 55 #include <string.h> 56 57 using namespace utl; 58 using namespace osl; 59 using namespace rtl; 60 using namespace com::sun::star; 61 using namespace com::sun::star::beans; 62 using namespace com::sun::star::lang; 63 using namespace com::sun::star::uno; 64 using namespace com::sun::star::linguistic2; 65 using namespace linguistic; 66 67 // XML-header of SPELLML queries 68 #define SPELLML_HEADER "<?xml?>" 69 70 /////////////////////////////////////////////////////////////////////////// 71 72 SpellChecker::SpellChecker() : 73 aEvtListeners ( GetLinguMutex() ) 74 { 75 aDicts = NULL; 76 aDEncs = NULL; 77 aDLocs = NULL; 78 aDNames = NULL; 79 bDisposing = sal_False; 80 pPropHelper = NULL; 81 numdict = 0; 82 } 83 84 85 SpellChecker::~SpellChecker() 86 { 87 if (aDicts) 88 { 89 for (int i = 0; i < numdict; i++) 90 { 91 if (aDicts[i]) delete aDicts[i]; 92 aDicts[i] = NULL; 93 } 94 delete[] aDicts; 95 } 96 aDicts = NULL; 97 numdict = 0; 98 if (aDEncs) delete[] aDEncs; 99 aDEncs = NULL; 100 if (aDLocs) delete[] aDLocs; 101 aDLocs = NULL; 102 if (aDNames) delete[] aDNames; 103 aDNames = NULL; 104 if (pPropHelper) 105 pPropHelper->RemoveAsPropListener(); 106 delete pPropHelper; 107 } 108 109 110 PropertyHelper_Spelling & SpellChecker::GetPropHelper_Impl() 111 { 112 if (!pPropHelper) 113 { 114 Reference< XPropertySet > xPropSet( GetLinguProperties(), UNO_QUERY ); 115 116 pPropHelper = new PropertyHelper_Spelling( (XSpellChecker *) this, xPropSet ); 117 pPropHelper->AddAsPropListener(); //! after a reference is established 118 } 119 return *pPropHelper; 120 } 121 122 123 Sequence< Locale > SAL_CALL SpellChecker::getLocales() 124 throw(RuntimeException) 125 { 126 MutexGuard aGuard( GetLinguMutex() ); 127 128 // this routine should return the locales supported by the installed 129 // dictionaries. 130 131 if (!numdict) 132 { 133 SvtLinguConfig aLinguCfg; 134 135 // get list of extension dictionaries-to-use 136 // (or better speaking: the list of dictionaries using the 137 // new configuration entries). 138 std::list< SvtLinguConfigDictionaryEntry > aDics; 139 uno::Sequence< rtl::OUString > aFormatList; 140 aLinguCfg.GetSupportedDictionaryFormatsFor( A2OU("SpellCheckers"), 141 A2OU("org.openoffice.lingu.MySpellSpellChecker"), aFormatList ); 142 sal_Int32 nLen = aFormatList.getLength(); 143 for (sal_Int32 i = 0; i < nLen; ++i) 144 { 145 std::vector< SvtLinguConfigDictionaryEntry > aTmpDic( 146 aLinguCfg.GetActiveDictionariesByFormat( aFormatList[i] ) ); 147 aDics.insert( aDics.end(), aTmpDic.begin(), aTmpDic.end() ); 148 } 149 150 //!! for compatibility with old dictionaries (the ones not using extensions 151 //!! or new configuration entries, but still using the dictionary.lst file) 152 //!! Get the list of old style spell checking dictionaries to use... 153 std::vector< SvtLinguConfigDictionaryEntry > aOldStyleDics( 154 GetOldStyleDics( "DICT" ) ); 155 156 // to prefer dictionaries with configuration entries we will only 157 // use those old style dictionaries that add a language that 158 // is not yet supported by the list od new style dictionaries 159 MergeNewStyleDicsAndOldStyleDics( aDics, aOldStyleDics ); 160 161 numdict = aDics.size(); 162 if (numdict) 163 { 164 uno::Reference< lang::XMultiServiceFactory > xServiceFactory( utl::getProcessServiceFactory() ); 165 uno::Reference< ucb::XSimpleFileAccess > xAccess; 166 try 167 { 168 xAccess.set( xServiceFactory->createInstance( 169 A2OU( "com.sun.star.ucb.SimpleFileAccess" ) ), uno::UNO_QUERY_THROW ); 170 } 171 catch (uno::Exception & e) 172 { 173 DBG_ASSERT( 0, "failed to get input stream" ); 174 (void) e; 175 } 176 // get supported locales from the dictionaries-to-use... 177 sal_Int32 k = 0; 178 std::set< rtl::OUString, lt_rtl_OUString > aLocaleNamesSet; 179 std::list< SvtLinguConfigDictionaryEntry >::const_iterator aDictIt; 180 for (aDictIt = aDics.begin(); aDictIt != aDics.end(); ++aDictIt) 181 { 182 uno::Sequence< rtl::OUString > aLocations( aDictIt->aLocations ); 183 if ( xAccess.is() && aLocations.getLength() ) 184 { 185 sal_Bool bAllFileExists = sal_False; 186 sal_Int32 nLength = aLocations.getLength(); 187 for (sal_Int32 i = 0; i < nLength; ++i) 188 { 189 bAllFileExists |= xAccess->exists( aLocations[i] ); 190 } 191 if ( bAllFileExists ) 192 { 193 uno::Sequence< rtl::OUString > aLocaleNames( aDictIt->aLocaleNames ); 194 sal_Int32 nLen2 = aLocaleNames.getLength(); 195 for (k = 0; k < nLen2; ++k) 196 { 197 aLocaleNamesSet.insert( aLocaleNames[k] ); 198 } 199 } 200 } 201 } 202 // ... and add them to the resulting sequence 203 aSuppLocales.realloc( aLocaleNamesSet.size() ); 204 std::set< rtl::OUString, lt_rtl_OUString >::const_iterator aItB; 205 k = 0; 206 for (aItB = aLocaleNamesSet.begin(); aItB != aLocaleNamesSet.end(); ++aItB) 207 { 208 Locale aTmp( MsLangId::convertLanguageToLocale( 209 MsLangId::convertIsoStringToLanguage( *aItB ))); 210 aSuppLocales[k++] = aTmp; 211 } 212 213 //! For each dictionary and each locale we need a separate entry. 214 //! If this results in more than one dictionary per locale than (for now) 215 //! it is undefined which dictionary gets used. 216 //! In the future the implementation should support using several dictionaries 217 //! for one locale. 218 numdict = 0; 219 for (aDictIt = aDics.begin(); aDictIt != aDics.end(); ++aDictIt) 220 numdict = numdict + aDictIt->aLocaleNames.getLength(); 221 222 // add dictionary information 223 aDicts = new Hunspell* [numdict]; 224 aDEncs = new rtl_TextEncoding [numdict]; 225 aDLocs = new Locale [numdict]; 226 aDNames = new OUString [numdict]; 227 k = 0; 228 for (aDictIt = aDics.begin(); aDictIt != aDics.end(); ++aDictIt) 229 { 230 if (aDictIt->aLocaleNames.getLength() > 0 && 231 aDictIt->aLocations.getLength() > 0) 232 { 233 uno::Sequence< rtl::OUString > aLocaleNames( aDictIt->aLocaleNames ); 234 sal_Int32 nLocales = aLocaleNames.getLength(); 235 236 // currently only one language per dictionary is supported in the actual implementation... 237 // Thus here we work-around this by adding the same dictionary several times. 238 // Once for each of it's supported locales. 239 for (sal_Int32 i = 0; i < nLocales; ++i) 240 { 241 aDicts[k] = NULL; 242 aDEncs[k] = RTL_TEXTENCODING_DONTKNOW; 243 aDLocs[k] = MsLangId::convertLanguageToLocale( 244 MsLangId::convertIsoStringToLanguage( aLocaleNames[i] )); 245 // also both files have to be in the same directory and the 246 // file names must only differ in the extension (.aff/.dic). 247 // Thus we use the first location only and strip the extension part. 248 rtl::OUString aLocation = aDictIt->aLocations[0]; 249 sal_Int32 nPos = aLocation.lastIndexOf( '.' ); 250 aLocation = aLocation.copy( 0, nPos ); 251 aDNames[k] = aLocation; 252 253 ++k; 254 } 255 } 256 } 257 DBG_ASSERT( k == numdict, "index mismatch?" ); 258 } 259 else 260 { 261 /* no dictionary found so register no dictionaries */ 262 numdict = 0; 263 aDicts = NULL; 264 aDEncs = NULL; 265 aDLocs = NULL; 266 aDNames = NULL; 267 aSuppLocales.realloc(0); 268 } 269 } 270 271 return aSuppLocales; 272 } 273 274 275 sal_Bool SAL_CALL SpellChecker::hasLocale(const Locale& rLocale) 276 throw(RuntimeException) 277 { 278 MutexGuard aGuard( GetLinguMutex() ); 279 280 sal_Bool bRes = sal_False; 281 if (!aSuppLocales.getLength()) 282 getLocales(); 283 284 sal_Int32 nLen = aSuppLocales.getLength(); 285 for (sal_Int32 i = 0; i < nLen; ++i) 286 { 287 const Locale *pLocale = aSuppLocales.getConstArray(); 288 if (rLocale == pLocale[i]) 289 { 290 bRes = sal_True; 291 break; 292 } 293 } 294 return bRes; 295 } 296 297 298 sal_Int16 SpellChecker::GetSpellFailure( const OUString &rWord, const Locale &rLocale ) 299 { 300 Hunspell * pMS = NULL; 301 rtl_TextEncoding eEnc = RTL_TEXTENCODING_DONTKNOW; 302 303 // initialize a myspell object for each dictionary once 304 // (note: mutex is held higher up in isValid) 305 306 sal_Int16 nRes = -1; 307 308 // first handle smart quotes both single and double 309 OUStringBuffer rBuf(rWord); 310 sal_Int32 n = rBuf.getLength(); 311 sal_Unicode c; 312 for (sal_Int32 ix=0; ix < n; ix++) 313 { 314 c = rBuf.charAt(ix); 315 if ((c == 0x201C) || (c == 0x201D)) 316 rBuf.setCharAt(ix,(sal_Unicode)0x0022); 317 if ((c == 0x2018) || (c == 0x2019)) 318 rBuf.setCharAt(ix,(sal_Unicode)0x0027); 319 } 320 OUString nWord(rBuf.makeStringAndClear()); 321 322 if (n) 323 { 324 for (sal_Int32 i = 0; i < numdict; ++i) 325 { 326 pMS = NULL; 327 eEnc = RTL_TEXTENCODING_DONTKNOW; 328 329 if (rLocale == aDLocs[i]) 330 { 331 if (!aDicts[i]) 332 { 333 OUString dicpath = aDNames[i] + A2OU(".dic"); 334 OUString affpath = aDNames[i] + A2OU(".aff"); 335 OUString dict; 336 OUString aff; 337 osl::FileBase::getSystemPathFromFileURL(dicpath,dict); 338 osl::FileBase::getSystemPathFromFileURL(affpath,aff); 339 OString aTmpaff(OU2ENC(aff,osl_getThreadTextEncoding())); 340 OString aTmpdict(OU2ENC(dict,osl_getThreadTextEncoding())); 341 342 #if defined(WNT) 343 // workaround for Windows specifc problem that the 344 // path length in calls to 'fopen' is limted to somewhat 345 // about 120+ characters which will usually be exceed when 346 // using dictionaries as extensions. 347 aTmpaff = Win_GetShortPathName( aff ); 348 aTmpdict = Win_GetShortPathName( dict ); 349 #endif 350 351 aDicts[i] = new Hunspell(aTmpaff.getStr(),aTmpdict.getStr()); 352 aDEncs[i] = RTL_TEXTENCODING_DONTKNOW; 353 if (aDicts[i]) 354 aDEncs[i] = getTextEncodingFromCharset(aDicts[i]->get_dic_encoding()); 355 } 356 pMS = aDicts[i]; 357 eEnc = aDEncs[i]; 358 } 359 360 if (pMS) 361 { 362 // we don't want to work with a default text encoding since following incorrect 363 // results may occur only for specific text and thus may be hard to notice. 364 // Thus better always make a clean exit here if the text encoding is in question. 365 // Hopefully something not working at all will raise proper attention quickly. ;-) 366 DBG_ASSERT( eEnc != RTL_TEXTENCODING_DONTKNOW, "failed to get text encoding! (maybe incorrect encoding string in file)" ); 367 if (eEnc == RTL_TEXTENCODING_DONTKNOW) 368 return -1; 369 370 OString aWrd(OU2ENC(nWord,eEnc)); 371 int rVal = pMS->spell((char*)aWrd.getStr()); 372 if (rVal != 1) 373 nRes = SpellFailure::SPELLING_ERROR; 374 else 375 return -1; 376 pMS = NULL; 377 } 378 } 379 } 380 381 return nRes; 382 } 383 384 385 sal_Bool SAL_CALL SpellChecker::isValid( const OUString& rWord, const Locale& rLocale, 386 const PropertyValues& rProperties ) 387 throw(IllegalArgumentException, RuntimeException) 388 { 389 MutexGuard aGuard( GetLinguMutex() ); 390 391 if (rLocale == Locale() || !rWord.getLength()) 392 return sal_True; 393 394 if (!hasLocale( rLocale )) 395 #ifdef LINGU_EXCEPTIONS 396 throw( IllegalArgumentException() ); 397 #else 398 return sal_True; 399 #endif 400 401 // return sal_False to process SPELLML requests (they are longer than the header) 402 if (rWord.match(A2OU(SPELLML_HEADER), 0) && (rWord.getLength() > 10)) return sal_False; 403 404 // Get property values to be used. 405 // These are be the default values set in the SN_LINGU_PROPERTIES 406 // PropertySet which are overridden by the supplied ones from the 407 // last argument. 408 // You'll probably like to use a simpler solution than the provided 409 // one using the PropertyHelper_Spell. 410 411 PropertyHelper_Spelling& rHelper = GetPropHelper(); 412 rHelper.SetTmpPropVals( rProperties ); 413 414 sal_Int16 nFailure = GetSpellFailure( rWord, rLocale ); 415 if (nFailure != -1 && !rWord.match(A2OU(SPELLML_HEADER), 0)) 416 { 417 sal_Int16 nLang = LocaleToLanguage( rLocale ); 418 // postprocess result for errors that should be ignored 419 const bool bIgnoreError = 420 (!rHelper.IsSpellUpperCase() && IsUpper( rWord, nLang )) || 421 (!rHelper.IsSpellWithDigits() && HasDigits( rWord )) || 422 (!rHelper.IsSpellCapitalization() && nFailure == SpellFailure::CAPTION_ERROR); 423 if (bIgnoreError) 424 nFailure = -1; 425 } 426 427 return (nFailure == -1); 428 } 429 430 431 Reference< XSpellAlternatives > 432 SpellChecker::GetProposals( const OUString &rWord, const Locale &rLocale ) 433 { 434 // Retrieves the return values for the 'spell' function call in case 435 // of a misspelled word. 436 // Especially it may give a list of suggested (correct) words: 437 438 Reference< XSpellAlternatives > xRes; 439 // note: mutex is held by higher up by spell which covers both 440 441 Hunspell* pMS = NULL; 442 rtl_TextEncoding eEnc = RTL_TEXTENCODING_DONTKNOW; 443 int count = 0; 444 int numsug = 0; 445 446 // first handle smart quotes (single and double) 447 OUStringBuffer rBuf(rWord); 448 sal_Int32 n = rBuf.getLength(); 449 sal_Unicode c; 450 for (sal_Int32 ix=0; ix < n; ix++) 451 { 452 c = rBuf.charAt(ix); 453 if ((c == 0x201C) || (c == 0x201D)) 454 rBuf.setCharAt(ix,(sal_Unicode)0x0022); 455 if ((c == 0x2018) || (c == 0x2019)) 456 rBuf.setCharAt(ix,(sal_Unicode)0x0027); 457 } 458 OUString nWord(rBuf.makeStringAndClear()); 459 460 if (n) 461 { 462 sal_Int16 nLang = LocaleToLanguage( rLocale ); 463 464 Sequence< OUString > aStr( 0 ); 465 466 for (int i =0; i < numdict; i++) 467 { 468 pMS = NULL; 469 eEnc = RTL_TEXTENCODING_DONTKNOW; 470 count = 0; 471 472 if (rLocale == aDLocs[i]) 473 { 474 pMS = aDicts[i]; 475 eEnc = aDEncs[i]; 476 } 477 478 if (pMS) 479 { 480 char ** suglst = NULL; 481 OString aWrd(OU2ENC(nWord,eEnc)); 482 count = pMS->suggest(&suglst, (const char *) aWrd.getStr()); 483 484 if (count) 485 { 486 aStr.realloc( numsug + count ); 487 OUString *pStr = aStr.getArray(); 488 for (int ii=0; ii < count; ++ii) 489 { 490 OUString cvtwrd(suglst[ii],strlen(suglst[ii]),eEnc); 491 pStr[numsug + ii] = cvtwrd; 492 } 493 pMS->free_list(&suglst, count); 494 numsug += count; 495 } 496 } 497 } 498 499 // now return an empty alternative for no suggestions or the list of alternatives if some found 500 String aTmp(rWord); 501 xRes = SpellAlternatives::CreateSpellAlternatives( aTmp, nLang, SpellFailure::SPELLING_ERROR, aStr ); 502 return xRes; 503 } 504 return xRes; 505 } 506 507 508 Reference< XSpellAlternatives > SAL_CALL SpellChecker::spell( 509 const OUString& rWord, const Locale& rLocale, 510 const PropertyValues& rProperties ) 511 throw(IllegalArgumentException, RuntimeException) 512 { 513 MutexGuard aGuard( GetLinguMutex() ); 514 515 if (rLocale == Locale() || !rWord.getLength()) 516 return NULL; 517 518 if (!hasLocale( rLocale )) 519 #ifdef LINGU_EXCEPTIONS 520 throw( IllegalArgumentException() ); 521 #else 522 return NULL; 523 #endif 524 525 Reference< XSpellAlternatives > xAlt; 526 if (!isValid( rWord, rLocale, rProperties )) 527 { 528 xAlt = GetProposals( rWord, rLocale ); 529 } 530 return xAlt; 531 } 532 533 534 Reference< XInterface > SAL_CALL SpellChecker_CreateInstance( 535 const Reference< XMultiServiceFactory > & /*rSMgr*/ ) 536 throw(Exception) 537 { 538 539 Reference< XInterface > xService = (cppu::OWeakObject*) new SpellChecker; 540 return xService; 541 } 542 543 544 sal_Bool SAL_CALL SpellChecker::addLinguServiceEventListener( 545 const Reference< XLinguServiceEventListener >& rxLstnr ) 546 throw(RuntimeException) 547 { 548 MutexGuard aGuard( GetLinguMutex() ); 549 550 sal_Bool bRes = sal_False; 551 if (!bDisposing && rxLstnr.is()) 552 { 553 bRes = GetPropHelper().addLinguServiceEventListener( rxLstnr ); 554 } 555 return bRes; 556 } 557 558 559 sal_Bool SAL_CALL SpellChecker::removeLinguServiceEventListener( 560 const Reference< XLinguServiceEventListener >& rxLstnr ) 561 throw(RuntimeException) 562 { 563 MutexGuard aGuard( GetLinguMutex() ); 564 565 sal_Bool bRes = sal_False; 566 if (!bDisposing && rxLstnr.is()) 567 { 568 bRes = GetPropHelper().removeLinguServiceEventListener( rxLstnr ); 569 } 570 return bRes; 571 } 572 573 574 OUString SAL_CALL SpellChecker::getServiceDisplayName( const Locale& /*rLocale*/ ) 575 throw(RuntimeException) 576 { 577 MutexGuard aGuard( GetLinguMutex() ); 578 return A2OU( "Hunspell SpellChecker" ); 579 } 580 581 582 void SAL_CALL SpellChecker::initialize( const Sequence< Any >& rArguments ) 583 throw(Exception, RuntimeException) 584 { 585 MutexGuard aGuard( GetLinguMutex() ); 586 587 if (!pPropHelper) 588 { 589 sal_Int32 nLen = rArguments.getLength(); 590 if (2 == nLen) 591 { 592 Reference< XPropertySet > xPropSet; 593 rArguments.getConstArray()[0] >>= xPropSet; 594 //rArguments.getConstArray()[1] >>= xDicList; 595 596 //! Pointer allows for access of the non-UNO functions. 597 //! And the reference to the UNO-functions while increasing 598 //! the ref-count and will implicitly free the memory 599 //! when the object is not longer used. 600 pPropHelper = new PropertyHelper_Spelling( (XSpellChecker *) this, xPropSet ); 601 pPropHelper->AddAsPropListener(); //! after a reference is established 602 } 603 else 604 { 605 DBG_ERROR( "wrong number of arguments in sequence" ); 606 } 607 } 608 } 609 610 611 void SAL_CALL SpellChecker::dispose() 612 throw(RuntimeException) 613 { 614 MutexGuard aGuard( GetLinguMutex() ); 615 616 if (!bDisposing) 617 { 618 bDisposing = sal_True; 619 EventObject aEvtObj( (XSpellChecker *) this ); 620 aEvtListeners.disposeAndClear( aEvtObj ); 621 } 622 } 623 624 625 void SAL_CALL SpellChecker::addEventListener( const Reference< XEventListener >& rxListener ) 626 throw(RuntimeException) 627 { 628 MutexGuard aGuard( GetLinguMutex() ); 629 630 if (!bDisposing && rxListener.is()) 631 aEvtListeners.addInterface( rxListener ); 632 } 633 634 635 void SAL_CALL SpellChecker::removeEventListener( const Reference< XEventListener >& rxListener ) 636 throw(RuntimeException) 637 { 638 MutexGuard aGuard( GetLinguMutex() ); 639 640 if (!bDisposing && rxListener.is()) 641 aEvtListeners.removeInterface( rxListener ); 642 } 643 644 645 /////////////////////////////////////////////////////////////////////////// 646 // Service specific part 647 // 648 649 OUString SAL_CALL SpellChecker::getImplementationName() 650 throw(RuntimeException) 651 { 652 MutexGuard aGuard( GetLinguMutex() ); 653 654 return getImplementationName_Static(); 655 } 656 657 658 sal_Bool SAL_CALL SpellChecker::supportsService( const OUString& ServiceName ) 659 throw(RuntimeException) 660 { 661 MutexGuard aGuard( GetLinguMutex() ); 662 663 Sequence< OUString > aSNL = getSupportedServiceNames(); 664 const OUString * pArray = aSNL.getConstArray(); 665 for( sal_Int32 i = 0; i < aSNL.getLength(); i++ ) 666 if( pArray[i] == ServiceName ) 667 return sal_True; 668 return sal_False; 669 } 670 671 672 Sequence< OUString > SAL_CALL SpellChecker::getSupportedServiceNames() 673 throw(RuntimeException) 674 { 675 MutexGuard aGuard( GetLinguMutex() ); 676 677 return getSupportedServiceNames_Static(); 678 } 679 680 681 Sequence< OUString > SpellChecker::getSupportedServiceNames_Static() 682 throw() 683 { 684 MutexGuard aGuard( GetLinguMutex() ); 685 686 Sequence< OUString > aSNS( 1 ); // auch mehr als 1 Service moeglich 687 aSNS.getArray()[0] = A2OU( SN_SPELLCHECKER ); 688 return aSNS; 689 } 690 691 void * SAL_CALL SpellChecker_getFactory( const sal_Char * pImplName, 692 XMultiServiceFactory * pServiceManager, void * ) 693 { 694 void * pRet = 0; 695 if ( !SpellChecker::getImplementationName_Static().compareToAscii( pImplName ) ) 696 { 697 Reference< XSingleServiceFactory > xFactory = 698 cppu::createOneInstanceFactory( 699 pServiceManager, 700 SpellChecker::getImplementationName_Static(), 701 SpellChecker_CreateInstance, 702 SpellChecker::getSupportedServiceNames_Static()); 703 // acquire, because we return an interface pointer instead of a reference 704 xFactory->acquire(); 705 pRet = xFactory.get(); 706 } 707 return pRet; 708 } 709 710 711 /////////////////////////////////////////////////////////////////////////// 712