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