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