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