xref: /AOO41X/main/lingucomponent/source/spellcheck/macosxspell/macspellimp.cxx (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
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 #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 //#include <hunspell.hxx>
41 #include <dictmgr.hxx>
42 #include <macspellimp.hxx>
43 
44 //#include <linguistic/lngprops.hxx>
45 #include <linguistic/spelldta.hxx>
46 #include <unotools/pathoptions.hxx>
47 #include <unotools/useroptions.hxx>
48 #include <osl/file.hxx>
49 #include <rtl/ustrbuf.hxx>
50 
51 
52 using namespace utl;
53 using namespace osl;
54 using namespace rtl;
55 using namespace com::sun::star;
56 using namespace com::sun::star::beans;
57 using namespace com::sun::star::lang;
58 using namespace com::sun::star::uno;
59 using namespace com::sun::star::linguistic2;
60 using namespace linguistic;
61 ///////////////////////////////////////////////////////////////////////////
62 // dbg_dump for development
63 #if OSL_DEBUG_LEVEL > 1
64 #include <rtl/strbuf.hxx>
65 #include <rtl/ustring.hxx>
66 
67 const sal_Char *dbg_dump(const rtl::OString &rStr)
68 {
69     static rtl::OStringBuffer aStr;
70 
71     aStr = rtl::OStringBuffer(rStr);
72     aStr.append(static_cast<char>(0));
73     return aStr.getStr();
74 }
75 
76 const sal_Char *dbg_dump(const rtl::OUString &rStr)
77 {
78     return dbg_dump(rtl::OUStringToOString(rStr, RTL_TEXTENCODING_UTF8));
79 }
80 
81 const sal_Char *dbg_dump(rtl_String *pStr)
82 {
83     return dbg_dump(rtl::OString(pStr));
84 }
85 
86 const sal_Char *dbg_dump(rtl_uString *pStr)
87 {
88     return dbg_dump(rtl::OUString(pStr));
89 }
90 
91 #endif
92 ///////////////////////////////////////////////////////////////////////////
93 
94 MacSpellChecker::MacSpellChecker() :
95 	aEvtListeners	( GetLinguMutex() )
96 {
97 //    aDicts = NULL;
98 	aDEncs = NULL;
99 	aDLocs = NULL;
100 	aDNames = NULL;
101 	bDisposing = sal_False;
102 	pPropHelper = NULL;
103     numdict = 0;
104     NSApplicationLoad();
105 	NSAutoreleasePool* pool	= [[NSAutoreleasePool alloc] init];
106     macSpell = [NSSpellChecker sharedSpellChecker];
107     macTag = [NSSpellChecker uniqueSpellDocumentTag];
108     [pool release];
109 }
110 
111 
112 MacSpellChecker::~MacSpellChecker()
113 {
114   // if (aDicts) {
115   //    for (int i = 0; i < numdict; i++) {
116   //           if (aDicts[i]) delete aDicts[i];
117   //           aDicts[i] = NULL;
118   //    }
119   //    delete[] aDicts;
120   // }
121   // aDicts = NULL;
122   numdict = 0;
123   if (aDEncs) delete[] aDEncs;
124   aDEncs = NULL;
125   if (aDLocs) delete[] aDLocs;
126   aDLocs = NULL;
127   if (aDNames) delete[] aDNames;
128   aDNames = NULL;
129   if (pPropHelper)
130 	 pPropHelper->RemoveAsPropListener();
131 }
132 
133 
134 PropertyHelper_Spell & MacSpellChecker::GetPropHelper_Impl()
135 {
136 	if (!pPropHelper)
137 	{
138 		Reference< XPropertySet	>	xPropSet( GetLinguProperties(), UNO_QUERY );
139 
140 		pPropHelper	= new PropertyHelper_Spell( (XSpellChecker *) this, xPropSet );
141 		xPropHelper = pPropHelper;
142 		pPropHelper->AddAsPropListener();	//! after a reference is established
143 	}
144 	return *pPropHelper;
145 }
146 
147 
148 Sequence< Locale > SAL_CALL MacSpellChecker::getLocales()
149 		throw(RuntimeException)
150 {
151 	MutexGuard	aGuard( GetLinguMutex() );
152 
153         // this routine should return the locales supported by the installed
154         // dictionaries.  So here we need to parse both the user edited
155         // dictionary list and the shared dictionary list
156         // to see what dictionaries the admin/user has installed
157 
158         int numusr;          // number of user dictionary entries
159         int numshr;          // number of shared dictionary entries
160         dictentry * spdict;  // shared dict entry pointer
161         dictentry * updict;  // user dict entry pointer
162         SvtPathOptions aPathOpt;
163 		rtl_TextEncoding aEnc = RTL_TEXTENCODING_UTF8;
164 
165     	std::vector<objc_object *> postspdict;
166     	//std::vector<dictentry *> postspdict;
167         std::vector<dictentry *> postupdict;
168 
169 
170 	if (!numdict) {
171 
172         // invoke a dictionary manager to get the user dictionary list
173         // TODO How on Mac OS X?
174 
175         // invoke a second  dictionary manager to get the shared dictionary list
176         NSArray *aLocales = [NSLocale availableLocaleIdentifiers];
177 
178         //Test for existence of the dictionaries
179         for (unsigned int i = 0; i < [aLocales count]; i++)
180         {
181             if( [macSpell setLanguage:[aLocales objectAtIndex:i] ] )
182             {
183                 postspdict.push_back( [ aLocales objectAtIndex:i ] );
184             }
185         }
186 
187 	    numusr = postupdict.size();
188         numshr = postspdict.size();
189 
190         // we really should merge these and remove duplicates but since
191         // users can name their dictionaries anything they want it would
192         // be impossible to know if a real duplication exists unless we
193         // add some unique key to each myspell dictionary
194         numdict = numshr + numusr;
195 
196         if (numdict) {
197             aDLocs = new Locale [numdict];
198             aDEncs  = new rtl_TextEncoding [numdict];
199             aDNames = new OUString [numdict];
200             aSuppLocales.realloc(numdict);
201             Locale * pLocale = aSuppLocales.getArray();
202             int numlocs = 0;
203             int newloc;
204             int i,j;
205             int k = 0;
206 
207             //first add the user dictionaries
208             //TODO for MAC?
209 
210             // now add the shared dictionaries
211             for (i = 0; i < numshr; i++) {
212                 NSDictionary *aLocDict = [ NSLocale componentsFromLocaleIdentifier:postspdict[i] ];
213                 NSString* aLang = [ aLocDict objectForKey:NSLocaleLanguageCode ];
214                 NSString* aCountry = [ aLocDict objectForKey:NSLocaleCountryCode ];
215                 OUString lang([aLang cStringUsingEncoding: NSUTF8StringEncoding], [aLang length], aEnc);
216                 OUString country([ aCountry cStringUsingEncoding: NSUTF8StringEncoding], [aCountry length], aEnc);
217                 Locale nLoc( lang, country, OUString() );
218                 newloc = 1;
219                 //eliminate duplicates (is this needed for MacOS?)
220                 for (j = 0; j < numlocs; j++) {
221                     if (nLoc == pLocale[j]) newloc = 0;
222                 }
223                 if (newloc) {
224                     pLocale[numlocs] = nLoc;
225                     numlocs++;
226                 }
227                 aDLocs[k] = nLoc;
228                 //pointer to Hunspell dictionary - not needed for MAC
229                 //aDicts[k] = NULL;
230                 aDEncs[k] = 0;
231                 // Dictionary file names not valid for Mac Spell
232                 //aDNames[k] = aPathOpt.GetLinguisticPath() + A2OU("/ooo/") + A2OU(postspdict[i]->filename);
233                 k++;
234             }
235 
236             aSuppLocales.realloc(numlocs);
237 
238         } else {
239             /* no dictionary.lst 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             /* de-allocation of memory is handled inside the DictMgr */
249             updict = NULL;
250             spdict = NULL;
251 
252         }
253 
254 	return aSuppLocales;
255 }
256 
257 
258 
259 sal_Bool SAL_CALL MacSpellChecker::hasLocale(const Locale& rLocale)
260 		throw(RuntimeException)
261 {
262 	MutexGuard	aGuard( GetLinguMutex() );
263 
264 	sal_Bool bRes = sal_False;
265 	if (!aSuppLocales.getLength())
266 		getLocales();
267 
268 	sal_Int32 nLen = aSuppLocales.getLength();
269 	for (sal_Int32 i = 0;  i < nLen;  ++i)
270 	{
271 		const Locale *pLocale = aSuppLocales.getConstArray();
272 		if (rLocale == pLocale[i])
273 		{
274 			bRes = sal_True;
275 			break;
276 		}
277 	}
278 	return bRes;
279 }
280 
281 
282 sal_Int16 MacSpellChecker::GetSpellFailure( const OUString &rWord, const Locale &rLocale )
283 {
284     rtl_TextEncoding aEnc;
285 
286 	// initialize a myspell object for each dictionary once
287         // (note: mutex is held higher up in isValid)
288 
289 
290 	sal_Int16 nRes = -1;
291 
292         // first handle smart quotes both single and double
293 	OUStringBuffer rBuf(rWord);
294         sal_Int32 n = rBuf.getLength();
295         sal_Unicode c;
296 	for (sal_Int32 ix=0; ix < n; ix++) {
297 	    c = rBuf.charAt(ix);
298             if ((c == 0x201C) || (c == 0x201D)) rBuf.setCharAt(ix,(sal_Unicode)0x0022);
299             if ((c == 0x2018) || (c == 0x2019)) rBuf.setCharAt(ix,(sal_Unicode)0x0027);
300         }
301         OUString nWord(rBuf.makeStringAndClear());
302 
303 	if (n)
304 	{
305         aEnc = 0;
306 		NSAutoreleasePool* pool	= [[NSAutoreleasePool alloc] init];
307         NSString* aNSStr = [[NSString alloc] initWithCharacters: nWord.getStr() length: nWord.getLength()];
308         NSString* aLang = [[NSString alloc] initWithCharacters: rLocale.Language.getStr() length: rLocale.Language.getLength()];
309         if(rLocale.Country.getLength()>0)
310         {
311             NSString* aCountry = [[NSString alloc] initWithCharacters: rLocale.Country.getStr() length: rLocale.Country.getLength()];
312             NSString* aTag = @"_";
313             NSString* aTaggedCountry = [aTag stringByAppendingString:aCountry];
314             [aLang autorelease];
315             aLang = [aLang  stringByAppendingString:aTaggedCountry];
316         }
317 
318         int aCount;
319         NSRange range = [macSpell checkSpellingOfString:aNSStr startingAt:0 language:aLang wrap:sal_False inSpellDocumentWithTag:macTag wordCount:&aCount];
320 		int rVal = 0;
321 		if(range.length>0)
322 		{
323 			rVal = -1;
324 		}
325 		else
326 		{
327 			rVal = 1;
328 		}
329         [pool release];
330         if (rVal != 1)
331         {
332             nRes = SpellFailure::SPELLING_ERROR;
333         } else {
334             return -1;
335         }
336     }
337 	return nRes;
338 }
339 
340 
341 
342 sal_Bool SAL_CALL
343 	MacSpellChecker::isValid( const OUString& rWord, const Locale& rLocale,
344 			const PropertyValues& rProperties )
345 		throw(IllegalArgumentException, RuntimeException)
346 {
347 	MutexGuard	aGuard( GetLinguMutex() );
348 
349  	if (rLocale == Locale()  ||  !rWord.getLength())
350 		return sal_True;
351 
352 	if (!hasLocale( rLocale ))
353 #ifdef LINGU_EXCEPTIONS
354 		throw( IllegalArgumentException() );
355 #else
356 		return sal_True;
357 #endif
358 
359 	// Get property values to be used.
360 	// These are be the default values set in the SN_LINGU_PROPERTIES
361 	// PropertySet which are overridden by the supplied ones from the
362 	// last argument.
363 	// You'll probably like to use a simplier solution than the provided
364 	// one using the PropertyHelper_Spell.
365 
366 	PropertyHelper_Spell &rHelper = GetPropHelper();
367 	rHelper.SetTmpPropVals( rProperties );
368 
369 	sal_Int16 nFailure = GetSpellFailure( rWord, rLocale );
370 	if (nFailure != -1)
371 	{
372 		sal_Int16 nLang = LocaleToLanguage( rLocale );
373 		// postprocess result for errors that should be ignored
374 		if (   (!rHelper.IsSpellUpperCase()  && IsUpper( rWord, nLang ))
375 			|| (!rHelper.IsSpellWithDigits() && HasDigits( rWord ))
376 			|| (!rHelper.IsSpellCapitalization()
377 				&&  nFailure == SpellFailure::CAPTION_ERROR)
378 		)
379 			nFailure = -1;
380 	}
381 
382 	return (nFailure == -1);
383 }
384 
385 
386 Reference< XSpellAlternatives >
387 	MacSpellChecker::GetProposals( const OUString &rWord, const Locale &rLocale )
388 {
389 	// Retrieves the return values for the 'spell' function call in case
390 	// of a misspelled word.
391 	// Especially it may give a list of suggested (correct) words:
392 
393 	Reference< XSpellAlternatives > xRes;
394         // note: mutex is held by higher up by spell which covers both
395 
396     sal_Int16 nLang = LocaleToLanguage( rLocale );
397 	int count;
398 	Sequence< OUString > aStr( 0 );
399 
400         // first handle smart quotes (single and double)
401 	OUStringBuffer rBuf(rWord);
402         sal_Int32 n = rBuf.getLength();
403         sal_Unicode c;
404 	for (sal_Int32 ix=0; ix < n; ix++) {
405 	     c = rBuf.charAt(ix);
406              if ((c == 0x201C) || (c == 0x201D)) rBuf.setCharAt(ix,(sal_Unicode)0x0022);
407              if ((c == 0x2018) || (c == 0x2019)) rBuf.setCharAt(ix,(sal_Unicode)0x0027);
408         }
409         OUString nWord(rBuf.makeStringAndClear());
410 
411 	if (n)
412 	{
413 		NSAutoreleasePool* pool	= [[NSAutoreleasePool alloc] init];
414         NSString* aNSStr = [[NSString alloc] initWithCharacters: nWord.getStr() length: nWord.getLength()];
415         NSString* aLang = [[NSString alloc] initWithCharacters: rLocale.Language.getStr() length: rLocale.Language.getLength() ];
416         if(rLocale.Country.getLength()>0)
417         {
418             NSString* aCountry = [[NSString alloc] initWithCharacters: rLocale.Country.getStr() length: rLocale.Country.getLength() ];
419             NSString* aTag = @"_";
420             NSString* aTaggedCountry = [aTag stringByAppendingString:aCountry];
421             [aLang autorelease];
422             aLang = [aLang  stringByAppendingString:aTaggedCountry];
423         }
424         [macSpell setLanguage:aLang];
425         NSArray *guesses = [macSpell guessesForWord:aNSStr];
426         count = [guesses count];
427         if (count)
428         {
429            aStr.realloc( count );
430            OUString *pStr = aStr.getArray();
431                for (int ii=0; ii < count; ii++)
432                {
433                   // if needed add: if (suglst[ii] == NULL) continue;
434                   NSString* guess = [guesses objectAtIndex:ii];
435                   OUString cvtwrd((const sal_Unicode*)[guess cStringUsingEncoding:NSUnicodeStringEncoding], (sal_Int32)[guess length]);
436                   pStr[ii] = cvtwrd;
437                }
438         }
439        [pool release];
440 	}
441 
442             // now return an empty alternative for no suggestions or the list of alternatives if some found
443 	    SpellAlternatives *pAlt = new SpellAlternatives;
444             String aTmp(rWord);
445 	    pAlt->SetWordLanguage( aTmp, nLang );
446 	    pAlt->SetFailureType( SpellFailure::SPELLING_ERROR );
447 	    pAlt->SetAlternatives( aStr );
448 	    xRes = pAlt;
449         return xRes;
450 
451 }
452 
453 
454 
455 
456 Reference< XSpellAlternatives > SAL_CALL
457 	MacSpellChecker::spell( const OUString& rWord, const Locale& rLocale,
458 			const PropertyValues& rProperties )
459 		throw(IllegalArgumentException, RuntimeException)
460 {
461 	MutexGuard	aGuard( GetLinguMutex() );
462 
463  	if (rLocale == Locale()  ||  !rWord.getLength())
464 		return NULL;
465 
466 	if (!hasLocale( rLocale ))
467 #ifdef LINGU_EXCEPTIONS
468 		throw( IllegalArgumentException() );
469 #else
470 		return NULL;
471 #endif
472 
473 	Reference< XSpellAlternatives > xAlt;
474 	if (!isValid( rWord, rLocale, rProperties ))
475 	{
476 		xAlt =  GetProposals( rWord, rLocale );
477 	}
478 	return xAlt;
479 }
480 
481 
482 Reference< XInterface > SAL_CALL MacSpellChecker_CreateInstance(
483             const Reference< XMultiServiceFactory > & /*rSMgr*/ )
484 		throw(Exception)
485 {
486 
487 	Reference< XInterface > xService = (cppu::OWeakObject*) new MacSpellChecker;
488 	return xService;
489 }
490 
491 
492 sal_Bool SAL_CALL
493 	MacSpellChecker::addLinguServiceEventListener(
494 			const Reference< XLinguServiceEventListener >& rxLstnr )
495 		throw(RuntimeException)
496 {
497 	MutexGuard	aGuard( GetLinguMutex() );
498 
499 	sal_Bool bRes = sal_False;
500 	if (!bDisposing && rxLstnr.is())
501 	{
502 		bRes = GetPropHelper().addLinguServiceEventListener( rxLstnr );
503 	}
504 	return bRes;
505 }
506 
507 
508 sal_Bool SAL_CALL
509 	MacSpellChecker::removeLinguServiceEventListener(
510 			const Reference< XLinguServiceEventListener >& rxLstnr )
511 		throw(RuntimeException)
512 {
513 	MutexGuard	aGuard( GetLinguMutex() );
514 
515 	sal_Bool bRes = sal_False;
516 	if (!bDisposing && rxLstnr.is())
517 	{
518 		DBG_ASSERT( xPropHelper.is(), "xPropHelper non existent" );
519 		bRes = GetPropHelper().removeLinguServiceEventListener( rxLstnr );
520 	}
521 	return bRes;
522 }
523 
524 
525 OUString SAL_CALL
526     MacSpellChecker::getServiceDisplayName( const Locale& /*rLocale*/ )
527 		throw(RuntimeException)
528 {
529 	MutexGuard	aGuard( GetLinguMutex() );
530 	return A2OU( "Mac OS X Spell Checker" );
531 }
532 
533 
534 void SAL_CALL
535 	MacSpellChecker::initialize( const Sequence< Any >& rArguments )
536 		throw(Exception, RuntimeException)
537 {
538 	MutexGuard	aGuard( GetLinguMutex() );
539 
540 	if (!pPropHelper)
541 	{
542 		sal_Int32 nLen = rArguments.getLength();
543 		if (2 == nLen)
544 		{
545 			Reference< XPropertySet	>	xPropSet;
546 			rArguments.getConstArray()[0] >>= xPropSet;
547 			//rArguments.getConstArray()[1] >>= xDicList;
548 
549 			//! Pointer allows for access of the non-UNO functions.
550 			//! And the reference to the UNO-functions while increasing
551 			//! the ref-count and will implicitly free the memory
552 			//! when the object is not longer used.
553 			pPropHelper = new PropertyHelper_Spell( (XSpellChecker *) this, xPropSet );
554 			xPropHelper = pPropHelper;
555 			pPropHelper->AddAsPropListener();	//! after a reference is established
556 		}
557 		else
558 			DBG_ERROR( "wrong number of arguments in sequence" );
559 
560 	}
561 }
562 
563 
564 void SAL_CALL
565 	MacSpellChecker::dispose()
566 		throw(RuntimeException)
567 {
568 	MutexGuard	aGuard( GetLinguMutex() );
569 
570 	if (!bDisposing)
571 	{
572 		bDisposing = sal_True;
573 		EventObject	aEvtObj( (XSpellChecker *) this );
574 		aEvtListeners.disposeAndClear( aEvtObj );
575 	}
576 }
577 
578 
579 void SAL_CALL
580 	MacSpellChecker::addEventListener( const Reference< XEventListener >& rxListener )
581 		throw(RuntimeException)
582 {
583 	MutexGuard	aGuard( GetLinguMutex() );
584 
585 	if (!bDisposing && rxListener.is())
586 		aEvtListeners.addInterface( rxListener );
587 }
588 
589 
590 void SAL_CALL
591 	MacSpellChecker::removeEventListener( const Reference< XEventListener >& rxListener )
592 		throw(RuntimeException)
593 {
594 	MutexGuard	aGuard( GetLinguMutex() );
595 
596 	if (!bDisposing && rxListener.is())
597 		aEvtListeners.removeInterface( rxListener );
598 }
599 
600 
601 ///////////////////////////////////////////////////////////////////////////
602 // Service specific part
603 //
604 
605 OUString SAL_CALL MacSpellChecker::getImplementationName()
606 		throw(RuntimeException)
607 {
608 	MutexGuard	aGuard( GetLinguMutex() );
609 
610 	return getImplementationName_Static();
611 }
612 
613 
614 sal_Bool SAL_CALL MacSpellChecker::supportsService( const OUString& ServiceName )
615 		throw(RuntimeException)
616 {
617 	MutexGuard	aGuard( GetLinguMutex() );
618 
619 	Sequence< OUString > aSNL = getSupportedServiceNames();
620 	const OUString * pArray = aSNL.getConstArray();
621 	for( sal_Int32 i = 0; i < aSNL.getLength(); i++ )
622 		if( pArray[i] == ServiceName )
623 			return sal_True;
624 	return sal_False;
625 }
626 
627 
628 Sequence< OUString > SAL_CALL MacSpellChecker::getSupportedServiceNames()
629 		throw(RuntimeException)
630 {
631 	MutexGuard	aGuard( GetLinguMutex() );
632 
633 	return getSupportedServiceNames_Static();
634 }
635 
636 
637 Sequence< OUString > MacSpellChecker::getSupportedServiceNames_Static()
638 		throw()
639 {
640 	MutexGuard	aGuard( GetLinguMutex() );
641 
642 	Sequence< OUString > aSNS( 1 );	// auch mehr als 1 Service moeglich
643 	aSNS.getArray()[0] = A2OU( SN_SPELLCHECKER );
644 	return aSNS;
645 }
646 
647 void * SAL_CALL MacSpellChecker_getFactory( const sal_Char * pImplName,
648 			XMultiServiceFactory * pServiceManager, void *  )
649 {
650 	void * pRet = 0;
651 	if ( !MacSpellChecker::getImplementationName_Static().compareToAscii( pImplName ) )
652 	{
653 		Reference< XSingleServiceFactory > xFactory =
654 			cppu::createOneInstanceFactory(
655 				pServiceManager,
656 				MacSpellChecker::getImplementationName_Static(),
657 				MacSpellChecker_CreateInstance,
658 				MacSpellChecker::getSupportedServiceNames_Static());
659 		// acquire, because we return an interface pointer instead of a reference
660 		xFactory->acquire();
661 		pRet = xFactory.get();
662 	}
663 	return pRet;
664 }
665 
666 
667 ///////////////////////////////////////////////////////////////////////////
668