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 <cppuhelper/factory.hxx>	// helper for factories
29 #include <com/sun/star/registry/XRegistryKey.hpp>
30 #include <com/sun/star/beans/XPropertySet.hpp>
31 #include <i18npool/mslangid.hxx>
32 #include <tools/debug.hxx>
33 #include <unotools/processfactory.hxx>
34 #include <osl/mutex.hxx>
35 #include <unotools/pathoptions.hxx>
36 #include <unotools/lingucfg.hxx>
37 
38 #include <rtl/string.hxx>
39 #include <rtl/ustrbuf.hxx>
40 #include <rtl/textenc.h>
41 
42 #include "nthesimp.hxx"
43 #include <linguistic/misc.hxx>
44 #include <linguistic/lngprops.hxx>
45 #include "nthesdta.hxx"
46 #include <dictmgr.hxx>
47 
48 #include <list>
49 #include <set>
50 #include <string.h>
51 
52 // values asigned to capitalization types
53 #define CAPTYPE_UNKNOWN 0
54 #define CAPTYPE_NOCAP   1
55 #define CAPTYPE_INITCAP 2
56 #define CAPTYPE_ALLCAP  3
57 #define CAPTYPE_MIXED   4
58 
59 // XML-header to query SPELLML support
60 #define SPELLML_SUPPORT "<?xml?>"
61 
62 using namespace utl;
63 using namespace osl;
64 using namespace rtl;
65 using namespace com::sun::star;
66 using namespace com::sun::star::beans;
67 using namespace com::sun::star::lang;
68 using namespace com::sun::star::uno;
69 using namespace com::sun::star::linguistic2;
70 using namespace linguistic;
71 
72 
73 
74 ///////////////////////////////////////////////////////////////////////////
75 
76 static uno::Reference< XLinguServiceManager > GetLngSvcMgr_Impl()
77 {
78 	uno::Reference< XLinguServiceManager > xRes;
79 	uno::Reference< XMultiServiceFactory >  xMgr = getProcessServiceFactory();
80 	if (xMgr.is())
81 	{
82 		xRes = uno::Reference< XLinguServiceManager > ( xMgr->createInstance(
83 				OUString( RTL_CONSTASCII_USTRINGPARAM(
84 					"com.sun.star.linguistic2.LinguServiceManager" ) ) ), UNO_QUERY ) ;
85 	}
86 	return xRes;
87 }
88 
89 Thesaurus::Thesaurus() :
90 	aEvtListeners	( GetLinguMutex() )
91 {
92 	bDisposing = sal_False;
93 	pPropHelper = NULL;
94 	aThes = NULL;
95 	aCharSetInfo = NULL;
96 	aTEncs = NULL;
97 	aTLocs = NULL;
98 	aTNames = NULL;
99     numthes = 0;
100 }
101 
102 
103 Thesaurus::~Thesaurus()
104 {
105 
106     if (aThes)
107     {
108         for (int i = 0; i < numthes; i++)
109         {
110             if (aThes[i]) delete aThes[i];
111             aThes[i] = NULL;
112         }
113         delete[] aThes;
114     }
115     aThes = NULL;
116     if (aCharSetInfo)
117     {
118         for (int i = 0; i < numthes; i++)
119         {
120             if (aCharSetInfo[i]) delete aCharSetInfo[i];
121             aCharSetInfo[i] = NULL;
122         }
123         delete[] aCharSetInfo;
124     }
125     aCharSetInfo = NULL;
126     numthes = 0;
127     if (aTEncs) delete[] aTEncs;
128     aTEncs = NULL;
129     if (aTLocs) delete[] aTLocs;
130     aTLocs = NULL;
131     if (aTNames) delete[] aTNames;
132     aTNames = NULL;
133 
134     if (pPropHelper)
135 		pPropHelper->RemoveAsPropListener();
136 }
137 
138 
139 PropertyHelper_Thes & Thesaurus::GetPropHelper_Impl()
140 {
141 	if (!pPropHelper)
142 	{
143 		Reference< XPropertySet	>	xPropSet( GetLinguProperties(), UNO_QUERY );
144 
145 		pPropHelper	= new PropertyHelper_Thes( (XThesaurus *) this, xPropSet );
146 		xPropHelper = pPropHelper;
147 		pPropHelper->AddAsPropListener();	//! after a reference is established
148 	}
149 	return *pPropHelper;
150 }
151 
152 
153 Sequence< Locale > SAL_CALL Thesaurus::getLocales()
154         throw(RuntimeException)
155 {
156     MutexGuard  aGuard( GetLinguMutex() );
157 
158     // this routine should return the locales supported by the installed
159     // dictionaries.
160 
161     if (!numthes)
162     {
163         SvtLinguConfig aLinguCfg;
164 
165         // get list of dictionaries-to-use
166         std::list< SvtLinguConfigDictionaryEntry > aDics;
167         uno::Sequence< rtl::OUString > aFormatList;
168         aLinguCfg.GetSupportedDictionaryFormatsFor( A2OU("Thesauri"),
169                 A2OU("org.openoffice.lingu.new.Thesaurus"), aFormatList );
170         sal_Int32 nLen = aFormatList.getLength();
171         for (sal_Int32 i = 0;  i < nLen;  ++i)
172         {
173             std::vector< SvtLinguConfigDictionaryEntry > aTmpDic(
174                     aLinguCfg.GetActiveDictionariesByFormat( aFormatList[i] ) );
175             aDics.insert( aDics.end(), aTmpDic.begin(), aTmpDic.end() );
176         }
177 
178         //!! for compatibility with old dictionaries (the ones not using extensions
179         //!! or new configuration entries, but still using the dictionary.lst file)
180 		//!! Get the list of old style spell checking dictionaries to use...
181         std::vector< SvtLinguConfigDictionaryEntry > aOldStyleDics(
182 				GetOldStyleDics( "THES" ) );
183 
184 		// to prefer dictionaries with configuration entries we will only
185 		// use those old style dictionaries that add a language that
186 		// is not yet supported by the list od new style dictionaries
187 		MergeNewStyleDicsAndOldStyleDics( aDics, aOldStyleDics );
188 
189         numthes = aDics.size();
190         if (numthes)
191         {
192             // get supported locales from the dictionaries-to-use...
193             sal_Int32 k = 0;
194             std::set< rtl::OUString, lt_rtl_OUString > aLocaleNamesSet;
195             std::list< SvtLinguConfigDictionaryEntry >::const_iterator aDictIt;
196             for (aDictIt = aDics.begin();  aDictIt != aDics.end();  ++aDictIt)
197             {
198                 uno::Sequence< rtl::OUString > aLocaleNames( aDictIt->aLocaleNames );
199                 sal_Int32 nLen2 = aLocaleNames.getLength();
200                 for (k = 0;  k < nLen2;  ++k)
201                 {
202                     aLocaleNamesSet.insert( aLocaleNames[k] );
203                 }
204             }
205             // ... and add them to the resulting sequence
206             aSuppLocales.realloc( aLocaleNamesSet.size() );
207             std::set< rtl::OUString, lt_rtl_OUString >::const_iterator aItB;
208             k = 0;
209             for (aItB = aLocaleNamesSet.begin();  aItB != aLocaleNamesSet.end();  ++aItB)
210             {
211                 Locale aTmp( MsLangId::convertLanguageToLocale(
212                         MsLangId::convertIsoStringToLanguage( *aItB )));
213                 aSuppLocales[k++] = aTmp;
214             }
215 
216             //! For each dictionary and each locale we need a seperate entry.
217             //! If this results in more than one dictionary per locale than (for now)
218 			//! it is undefined which dictionary gets used.
219 			//! In the future the implementation should support using several dictionaries
220 			//! for one locale.
221 			numthes = 0;
222             for (aDictIt = aDics.begin();  aDictIt != aDics.end();  ++aDictIt)
223 				numthes = numthes + aDictIt->aLocaleNames.getLength();
224 
225             // add dictionary information
226             aThes   = new MyThes* [numthes];
227             aTEncs  = new rtl_TextEncoding [numthes];
228             aTLocs  = new Locale [numthes];
229             aTNames = new OUString [numthes];
230             aCharSetInfo = new CharClass* [numthes];
231 
232             k = 0;
233             for (aDictIt = aDics.begin();  aDictIt != aDics.end();  ++aDictIt)
234             {
235                 if (aDictIt->aLocaleNames.getLength() > 0 &&
236                     aDictIt->aLocations.getLength() > 0)
237                 {
238                     uno::Sequence< rtl::OUString > aLocaleNames( aDictIt->aLocaleNames );
239                     sal_Int32 nLocales = aLocaleNames.getLength();
240 
241                     // currently only one language per dictionary is supported in the actual implementation...
242                     // Thus here we work-around this by adding the same dictionary several times.
243                     // Once for each of it's supported locales.
244                     for (sal_Int32 i = 0;  i < nLocales;  ++i)
245                     {
246                         aThes[k]  = NULL;
247                         aTEncs[k]  = RTL_TEXTENCODING_DONTKNOW;
248                         aTLocs[k]  = MsLangId::convertLanguageToLocale(
249                                         MsLangId::convertIsoStringToLanguage( aDictIt->aLocaleNames[i] ));
250                         aCharSetInfo[k] = new CharClass( aTLocs[k] );
251                         // also both files have to be in the same directory and the
252                         // file names must only differ in the extension (.aff/.dic).
253                         // Thus we use the first location only and strip the extension part.
254                         rtl::OUString aLocation = aDictIt->aLocations[0];
255                         sal_Int32 nPos = aLocation.lastIndexOf( '.' );
256                         aLocation = aLocation.copy( 0, nPos );
257                         aTNames[k] = aLocation;
258 
259                         ++k;
260                     }
261                 }
262             }
263             DBG_ASSERT( k == numthes, "index mismatch?" );
264         }
265         else
266         {
267             /* no dictionary found so register no dictionaries */
268             numthes = 0;
269             aThes  = NULL;
270             aTEncs  = NULL;
271             aTLocs  = NULL;
272             aTNames = NULL;
273             aCharSetInfo = NULL;
274             aSuppLocales.realloc(0);
275         }
276     }
277 
278     return aSuppLocales;
279 }
280 
281 
282 
283 sal_Bool SAL_CALL Thesaurus::hasLocale(const Locale& rLocale)
284 		throw(RuntimeException)
285 {
286 	MutexGuard	aGuard( GetLinguMutex() );
287 
288 	sal_Bool bRes = sal_False;
289 	if (!aSuppLocales.getLength())
290 		getLocales();
291 	sal_Int32 nLen = aSuppLocales.getLength();
292 	for (sal_Int32 i = 0;  i < nLen;  ++i)
293 	{
294 		const Locale *pLocale = aSuppLocales.getConstArray();
295 		if (rLocale == pLocale[i])
296 		{
297 			bRes = sal_True;
298 			break;
299 		}
300 	}
301 	return bRes;
302 }
303 
304 
305 Sequence < Reference < ::com::sun::star::linguistic2::XMeaning > > SAL_CALL Thesaurus::queryMeanings(
306     const OUString& qTerm, const Locale& rLocale,
307     const PropertyValues& rProperties)
308     throw(IllegalArgumentException, RuntimeException)
309 {
310     MutexGuard      aGuard( GetLinguMutex() );
311 
312     uno::Sequence< Reference< XMeaning > > aMeanings( 1 );
313     uno::Sequence< Reference< XMeaning > > noMeanings( 0 );
314     uno::Reference< XLinguServiceManager > xLngSvcMgr( GetLngSvcMgr_Impl() );
315     uno::Reference< XSpellChecker1 > xSpell;
316 
317     OUString rTerm(qTerm);
318     OUString pTerm(qTerm);
319     sal_uInt16 ct = CAPTYPE_UNKNOWN;
320     sal_Int32 stem = 0;
321     sal_Int32 stem2 = 0;
322 
323     sal_Int16 nLanguage = LocaleToLanguage( rLocale );
324 
325     if (nLanguage == LANGUAGE_NONE || !rTerm.getLength())
326         return noMeanings;
327 
328     if (!hasLocale( rLocale ))
329 #ifdef LINGU_EXCEPTIONS
330         throw( IllegalArgumentException() );
331 #else
332         return noMeanings;
333 #endif
334 
335     if (prevTerm == qTerm && prevLocale == nLanguage)
336         return prevMeanings;
337 
338     mentry * pmean = NULL;
339     sal_Int32 nmean = 0;
340 
341     PropertyHelper_Thes &rHelper = GetPropHelper();
342     rHelper.SetTmpPropVals( rProperties );
343 
344     MyThes * pTH = NULL;
345     rtl_TextEncoding eEnc = RTL_TEXTENCODING_DONTKNOW;
346     CharClass * pCC = NULL;
347 
348     // find the first thesaurus that matches the locale
349     for (int i =0; i < numthes; i++)
350     {
351         if (rLocale == aTLocs[i])
352         {
353             // open up and intialize this thesaurus if need be
354             if (!aThes[i])
355             {
356                 OUString datpath = aTNames[i] + A2OU(".dat");
357                 OUString idxpath = aTNames[i] + A2OU(".idx");
358                 OUString ndat;
359                 OUString nidx;
360                 osl::FileBase::getSystemPathFromFileURL(datpath,ndat);
361                 osl::FileBase::getSystemPathFromFileURL(idxpath,nidx);
362                 OString aTmpidx(OU2ENC(nidx,osl_getThreadTextEncoding()));
363                 OString aTmpdat(OU2ENC(ndat,osl_getThreadTextEncoding()));
364 
365 #if defined(WNT)
366                 // workaround for Windows specifc problem that the
367                 // path length in calls to 'fopen' is limted to somewhat
368                 // about 120+ characters which will usually be exceed when
369                 // using dictionaries as extensions.
370                 aTmpidx = Win_GetShortPathName( nidx );
371                 aTmpdat = Win_GetShortPathName( ndat );
372 #endif
373 
374                 aThes[i] = new MyThes(aTmpidx.getStr(),aTmpdat.getStr());
375                 if (aThes[i])
376                     aTEncs[i] = getTextEncodingFromCharset(aThes[i]->get_th_encoding());
377             }
378             pTH = aThes[i];
379             eEnc = aTEncs[i];
380             pCC = aCharSetInfo[i];
381 
382             if (pTH)
383                 break;
384         }
385     }
386 
387     // we don't want to work with a default text encoding since following incorrect
388     // results may occur only for specific text and thus may be hard to notice.
389     // Thus better always make a clean exit here if the text encoding is in question.
390     // Hopefully something not working at all will raise proper attention quickly. ;-)
391     DBG_ASSERT( eEnc != RTL_TEXTENCODING_DONTKNOW, "failed to get text encoding! (maybe incorrect encoding string in file)" );
392     if (eEnc == RTL_TEXTENCODING_DONTKNOW)
393         return noMeanings;
394 
395     while (pTH)
396     {
397         // convert word to all lower case for searching
398         if (!stem)
399             ct = capitalType(rTerm, pCC);
400         OUString nTerm(makeLowerCase(rTerm, pCC));
401         OString aTmp( OU2ENC(nTerm, eEnc) );
402         nmean = pTH->Lookup(aTmp.getStr(),aTmp.getLength(),&pmean);
403 
404         if (nmean)
405             aMeanings.realloc( nmean );
406 
407         mentry * pe = pmean;
408         OUString codeTerm = qTerm;
409         Reference< XSpellAlternatives > xTmpRes2;
410 
411         if (stem)
412         {
413             xTmpRes2 = xSpell->spell( A2OU("<?xml?><query type='analyze'><word>") +
414             pTerm + A2OU("</word></query>"), nLanguage, rProperties );
415             if (xTmpRes2.is())
416             {
417                 Sequence<OUString>seq = xTmpRes2->getAlternatives();
418                 if (seq.getLength() > 0)
419                 {
420                     codeTerm = seq[0];
421                     stem2 = 1;
422                 }
423 #if 0
424                 OString o = OUStringToOString(codeTerm, RTL_TEXTENCODING_UTF8);
425                 fprintf(stderr, "CODETERM: %s\n", o.pData->buffer);
426 #endif
427             }
428         }
429 
430         for (int j = 0; j < nmean; j++)
431         {
432             int count = pe->count;
433             if (count)
434             {
435                 Sequence< OUString > aStr( count );
436                 OUString *pStr = aStr.getArray();
437 
438                 for (int i=0; i < count; i++)
439                 {
440                     OUString sTerm(pe->psyns[i],strlen(pe->psyns[i]),eEnc );
441                     sal_Int32 catpos = sTerm.indexOf('(');
442                     sal_Int32 catpos2 = 0;
443                     OUString catst;
444                     OUString catst2;
445                     if (catpos > 2)
446                     {
447                         // remove category name for affixation and casing
448                         catst = A2OU(" ") + sTerm.copy(catpos);
449                         sTerm = sTerm.copy(0, catpos);
450                         sTerm = sTerm.trim();
451                     }
452                     // generate synonyms with affixes
453                     if (stem && stem2)
454                     {
455                         Reference< XSpellAlternatives > xTmpRes;
456                         xTmpRes = xSpell->spell( A2OU("<?xml?><query type='generate'><word>") +
457                         sTerm + A2OU("</word>") + codeTerm + A2OU("</query>"), nLanguage, rProperties );
458                         if (xTmpRes.is())
459                         {
460                             Sequence<OUString>seq = xTmpRes->getAlternatives();
461                             if (seq.getLength() > 0)
462                                 sTerm = seq[0];
463                         }
464                     }
465                     if (catpos2)
466                         sTerm = catst2 + sTerm;
467 
468                     sal_uInt16 ct1 = capitalType(sTerm, pCC);
469                     if (CAPTYPE_MIXED == ct1)
470                         ct = ct1;
471                     OUString cTerm;
472                     switch (ct)
473                     {
474                         case CAPTYPE_ALLCAP:
475                             cTerm = makeUpperCase(sTerm, pCC);
476                             break;
477                         case CAPTYPE_INITCAP:
478                             cTerm = makeInitCap(sTerm, pCC);
479                             break;
480                         default:
481                             cTerm = sTerm;
482                             break;
483                     }
484                     OUString aAlt( cTerm + catst);
485                     pStr[i] = aAlt;
486                 }
487 #if 0
488                 Meaning * pMn = new Meaning(rTerm,nLanguage,rHelper);
489 #endif
490                 Meaning * pMn = new Meaning(rTerm,nLanguage);
491                 OUString dTerm(pe->defn,strlen(pe->defn),eEnc );
492                 pMn->SetMeaning(dTerm);
493                 pMn->SetSynonyms(aStr);
494                 Reference<XMeaning>* pMeaning = aMeanings.getArray();
495                 pMeaning[j] = pMn;
496             }
497             pe++;
498         }
499         pTH->CleanUpAfterLookup(&pmean,nmean);
500 
501         if (nmean)
502         {
503             prevTerm = qTerm;
504             prevMeanings = aMeanings;
505             prevLocale = nLanguage;
506             return aMeanings;
507         }
508 
509         if (stem || !xLngSvcMgr.is())
510             return noMeanings;
511         stem = 1;
512 
513         xSpell = uno::Reference< XSpellChecker1 >( xLngSvcMgr->getSpellChecker(), UNO_QUERY );
514         if (!xSpell.is() || !xSpell->isValid( A2OU(SPELLML_SUPPORT), nLanguage, rProperties ))
515             return noMeanings;
516         Reference< XSpellAlternatives > xTmpRes;
517         xTmpRes = xSpell->spell( A2OU("<?xml?><query type='stem'><word>") +
518             rTerm + A2OU("</word></query>"), nLanguage, rProperties );
519         if (xTmpRes.is())
520         {
521             Sequence<OUString>seq = xTmpRes->getAlternatives();
522 #if 0
523             for (int i = 0; i < seq.getLength(); i++)
524             {
525                 OString o = OUStringToOString(seq[i], RTL_TEXTENCODING_UTF8);
526                 fprintf(stderr, "%d: %s\n", i + 1, o.pData->buffer);
527             }
528 #endif
529             if (seq.getLength() > 0)
530             {
531                 rTerm = seq[0];  // XXX Use only the first stem
532                 continue;
533             }
534         }
535 
536         // stem the last word of the synonym (for categories after affixation)
537         rTerm = rTerm.trim();
538         sal_Int32 pos = rTerm.lastIndexOf(' ');
539         if (!pos)
540             return noMeanings;
541         xTmpRes = xSpell->spell( A2OU("<?xml?><query type='stem'><word>") +
542             rTerm.copy(pos + 1) + A2OU("</word></query>"), nLanguage, rProperties );
543         if (xTmpRes.is())
544         {
545             Sequence<OUString>seq = xTmpRes->getAlternatives();
546             if (seq.getLength() > 0)
547             {
548                 pTerm = rTerm.copy(pos + 1);
549                 rTerm = rTerm.copy(0, pos + 1) + seq[0];
550 #if  0
551                 for (int i = 0; i < seq.getLength(); i++)
552                 {
553                     OString o = OUStringToOString(seq[i], RTL_TEXTENCODING_UTF8);
554                     fprintf(stderr, "%d: %s\n", i + 1, o.pData->buffer);
555                 }
556 #endif
557                 continue;
558             }
559         }
560         break;
561     }
562     return noMeanings;
563 }
564 
565 
566 Reference< XInterface > SAL_CALL Thesaurus_CreateInstance(
567             const Reference< XMultiServiceFactory > & /*rSMgr*/ )
568 		throw(Exception)
569 {
570 	Reference< XInterface > xService = (cppu::OWeakObject*) new Thesaurus;
571 	return xService;
572 }
573 
574 
575 OUString SAL_CALL Thesaurus::getServiceDisplayName( const Locale& /*rLocale*/ )
576 		throw(RuntimeException)
577 {
578 	MutexGuard	aGuard( GetLinguMutex() );
579 	return A2OU( "OpenOffice.org New Thesaurus" );
580 }
581 
582 
583 void SAL_CALL Thesaurus::initialize( const Sequence< Any >& rArguments )
584 		throw(Exception, RuntimeException)
585 {
586 	MutexGuard	aGuard( GetLinguMutex() );
587 
588 	if (!pPropHelper)
589 	{
590 		sal_Int32 nLen = rArguments.getLength();
591 		if (1 == nLen)
592 		{
593 			Reference< XPropertySet	>	xPropSet;
594 			rArguments.getConstArray()[0] >>= xPropSet;
595 
596 			//! Pointer allows for access of the non-UNO functions.
597 			//! And the reference to the UNO-functions while increasing
598 			//! the ref-count and will implicitly free the memory
599 			//! when the object is not longer used.
600 			pPropHelper = new PropertyHelper_Thes( (XThesaurus *) this, xPropSet );
601 			xPropHelper = pPropHelper;
602 			pPropHelper->AddAsPropListener();	//! after a reference is established
603 		}
604 		else
605 			DBG_ERROR( "wrong number of arguments in sequence" );
606 	}
607 }
608 
609 
610 
611 sal_uInt16 SAL_CALL Thesaurus::capitalType(const OUString& aTerm, CharClass * pCC)
612 {
613         sal_Int32 tlen = aTerm.getLength();
614         if ((pCC) && (tlen))
615         {
616             String aStr(aTerm);
617             sal_Int32 nc = 0;
618             for (sal_uInt16 tindex = 0; tindex < tlen;  tindex++)
619             {
620                 if (pCC->getCharacterType(aStr,tindex) &
621                    ::com::sun::star::i18n::KCharacterType::UPPER) nc++;
622             }
623 
624             if (nc == 0)
625                 return (sal_uInt16) CAPTYPE_NOCAP;
626             if (nc == tlen)
627                 return (sal_uInt16) CAPTYPE_ALLCAP;
628             if ((nc == 1) && (pCC->getCharacterType(aStr,0) &
629                   ::com::sun::star::i18n::KCharacterType::UPPER))
630                 return (sal_uInt16) CAPTYPE_INITCAP;
631 
632             return (sal_uInt16) CAPTYPE_MIXED;
633         }
634         return (sal_uInt16) CAPTYPE_UNKNOWN;
635 }
636 
637 
638 
639 OUString SAL_CALL Thesaurus::makeLowerCase(const OUString& aTerm, CharClass * pCC)
640 {
641     if (pCC)
642         return pCC->toLower_rtl(aTerm, 0, aTerm.getLength());
643     return aTerm;
644 }
645 
646 
647 OUString SAL_CALL Thesaurus::makeUpperCase(const OUString& aTerm, CharClass * pCC)
648 {
649     if (pCC)
650         return pCC->toUpper_rtl(aTerm, 0, aTerm.getLength());
651     return aTerm;
652 }
653 
654 
655 OUString SAL_CALL Thesaurus::makeInitCap(const OUString& aTerm, CharClass * pCC)
656 {
657     sal_Int32 tlen = aTerm.getLength();
658     if ((pCC) && (tlen))
659     {
660         OUString bTemp = aTerm.copy(0,1);
661         if (tlen > 1)
662         {
663             return ( pCC->toUpper_rtl(bTemp, 0, 1)
664                      + pCC->toLower_rtl(aTerm,1,(tlen-1)) );
665         }
666 
667         return pCC->toUpper_rtl(bTemp, 0, 1);
668     }
669     return aTerm;
670 }
671 
672 
673 
674 void SAL_CALL Thesaurus::dispose()
675 		throw(RuntimeException)
676 {
677 	MutexGuard	aGuard( GetLinguMutex() );
678 
679 	if (!bDisposing)
680 	{
681 		bDisposing = sal_True;
682 		EventObject	aEvtObj( (XThesaurus *) this );
683 		aEvtListeners.disposeAndClear( aEvtObj );
684 	}
685 }
686 
687 
688 void SAL_CALL Thesaurus::addEventListener( const Reference< XEventListener >& rxListener )
689 		throw(RuntimeException)
690 {
691 	MutexGuard	aGuard( GetLinguMutex() );
692 
693 	if (!bDisposing && rxListener.is())
694 		aEvtListeners.addInterface( rxListener );
695 }
696 
697 
698 void SAL_CALL Thesaurus::removeEventListener( const Reference< XEventListener >& rxListener )
699 		throw(RuntimeException)
700 {
701 	MutexGuard	aGuard( GetLinguMutex() );
702 
703 	if (!bDisposing && rxListener.is())
704 		aEvtListeners.removeInterface( rxListener );
705 }
706 
707 
708 ///////////////////////////////////////////////////////////////////////////
709 // Service specific part
710 //
711 
712 OUString SAL_CALL Thesaurus::getImplementationName()
713 		throw(RuntimeException)
714 {
715 	MutexGuard	aGuard( GetLinguMutex() );
716 	return getImplementationName_Static();
717 }
718 
719 
720 sal_Bool SAL_CALL Thesaurus::supportsService( const OUString& ServiceName )
721 		throw(RuntimeException)
722 {
723 	MutexGuard	aGuard( GetLinguMutex() );
724 
725 	Sequence< OUString > aSNL = getSupportedServiceNames();
726 	const OUString * pArray = aSNL.getConstArray();
727 	for( sal_Int32 i = 0; i < aSNL.getLength(); i++ )
728 		if( pArray[i] == ServiceName )
729 			return sal_True;
730 	return sal_False;
731 }
732 
733 
734 Sequence< OUString > SAL_CALL Thesaurus::getSupportedServiceNames()
735 		throw(RuntimeException)
736 {
737 	MutexGuard	aGuard( GetLinguMutex() );
738 	return getSupportedServiceNames_Static();
739 }
740 
741 
742 Sequence< OUString > Thesaurus::getSupportedServiceNames_Static()
743 		throw()
744 {
745 	MutexGuard	aGuard( GetLinguMutex() );
746 
747 	Sequence< OUString > aSNS( 1 );	// auch mehr als 1 Service moeglich
748 	aSNS.getArray()[0] = A2OU( SN_THESAURUS );
749 	return aSNS;
750 }
751 
752 void * SAL_CALL Thesaurus_getFactory( const sal_Char * pImplName,
753 			XMultiServiceFactory * pServiceManager, void *  )
754 {
755 	void * pRet = 0;
756 	if ( !Thesaurus::getImplementationName_Static().compareToAscii( pImplName ) )
757 	{
758 
759 		Reference< XSingleServiceFactory > xFactory =
760 			cppu::createOneInstanceFactory(
761 				pServiceManager,
762 				Thesaurus::getImplementationName_Static(),
763 				Thesaurus_CreateInstance,
764 				Thesaurus::getSupportedServiceNames_Static());
765 		// acquire, because we return an interface pointer instead of a reference
766 		xFactory->acquire();
767 		pRet = xFactory.get();
768 	}
769 	return pRet;
770 }
771 
772 
773 ///////////////////////////////////////////////////////////////////////////
774 
775 
776 #undef CAPTYPE_UNKNOWN
777 #undef CAPTYPE_NOCAP
778 #undef CAPTYPE_INITCAP
779 #undef CAPTYPE_ALLCAP
780 #undef CAPTYPE_MIXED
781