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