xref: /aoo41x/main/linguistic/source/misc.cxx (revision cdf0e10c)
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_linguistic.hxx"
30 #include <tools/string.hxx>
31 #include <tools/fsys.hxx>
32 #include <tools/debug.hxx>
33 #include <unotools/pathoptions.hxx>
34 #include <svl/lngmisc.hxx>
35 #include <ucbhelper/content.hxx>
36 #include <i18npool/mslangid.hxx>
37 #include <com/sun/star/ucb/XCommandEnvironment.hpp>
38 #include <com/sun/star/beans/XPropertySet.hpp>
39 #include <com/sun/star/beans/XFastPropertySet.hpp>
40 #include <com/sun/star/beans/XPropertyChangeListener.hpp>
41 #include <com/sun/star/frame/XTerminateListener.hpp>
42 #include <com/sun/star/frame/XDesktop.hpp>
43 #include <com/sun/star/frame/XStorable.hpp>
44 
45 #include <com/sun/star/beans/PropertyValues.hpp>
46 #include <com/sun/star/uno/Sequence.hxx>
47 #include <com/sun/star/uno/Reference.h>
48 #include <com/sun/star/linguistic2/DictionaryType.hpp>
49 #include <com/sun/star/linguistic2/XSearchableDictionaryList.hpp>
50 #include <unotools/processfactory.hxx>
51 #include <unotools/localedatawrapper.hxx>
52 #include <unotools/syslocale.hxx>
53 
54 #include <rtl/instance.hxx>
55 
56 #include "linguistic/misc.hxx"
57 #include "defs.hxx"
58 #include "linguistic/lngprops.hxx"
59 #include "linguistic/hyphdta.hxx"
60 #include <i18npool/mslangid.hxx>
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::i18n;
70 using namespace com::sun::star::linguistic2;
71 
72 namespace linguistic
73 {
74 
75 ///////////////////////////////////////////////////////////////////////////
76 
77 //!! multi-thread safe mutex for all platforms !!
78 struct LinguMutex : public rtl::Static< osl::Mutex, LinguMutex >
79 {
80 };
81 
82 osl::Mutex &	GetLinguMutex()
83 {
84 	return LinguMutex::get();
85 }
86 
87 ///////////////////////////////////////////////////////////////////////////
88 
89 LocaleDataWrapper & GetLocaleDataWrapper( sal_Int16 nLang )
90 {
91     static LocaleDataWrapper aLclDtaWrp(
92                 getProcessServiceFactory(),
93                 CreateLocale( SvtSysLocale().GetUILanguage() ) );
94 
95     const Locale &rLcl = aLclDtaWrp.getLoadedLocale();
96     Locale aLcl( CreateLocale( nLang ) );
97     if (aLcl.Language != rLcl.Language ||
98         aLcl.Country  != rLcl.Country  ||
99         aLcl.Variant  != rLcl.Variant)
100         aLclDtaWrp.setLocale( aLcl );
101     return aLclDtaWrp;
102 }
103 
104 ///////////////////////////////////////////////////////////////////////////
105 
106 /**
107     returns text-encoding used for ByteString unicode String conversion
108  */
109 rtl_TextEncoding GetTextEncoding( sal_Int16 nLanguage )
110 {
111     DBG_ASSERT( nLanguage != LANGUAGE_NONE, "invalid language argument" );
112     static sal_Int16 nLastLanguage = LANGUAGE_NONE;
113 
114     // set default value for unknown languages
115     static rtl_TextEncoding nEncoding = RTL_TEXTENCODING_DONTKNOW;
116 
117     if (nLastLanguage != nLanguage)
118     {
119         //!! IPR uses textencodings Latin-1, Latin-2, Latin-5 and Latin-7 !!
120 
121         nLastLanguage = nLanguage;
122         switch (nLanguage)
123         {
124             case LANGUAGE_GERMAN :
125             case LANGUAGE_GERMAN_SWISS :
126             case LANGUAGE_ENGLISH_US :
127             case LANGUAGE_ENGLISH_UK :
128             case LANGUAGE_FRENCH :
129             case LANGUAGE_ITALIAN :
130             case LANGUAGE_SPANISH :
131             case LANGUAGE_CATALAN :
132             case LANGUAGE_PORTUGUESE :
133             case LANGUAGE_PORTUGUESE_BRAZILIAN :
134             case LANGUAGE_DANISH :
135             case LANGUAGE_DUTCH :
136             case LANGUAGE_SWEDISH :
137             case LANGUAGE_FINNISH :
138             case LANGUAGE_NORWEGIAN_BOKMAL :
139             case LANGUAGE_NORWEGIAN_NYNORSK :
140             case LANGUAGE_AFRIKAANS :
141             case LANGUAGE_ENGLISH_EIRE :
142             case LANGUAGE_ENGLISH_AUS :
143 #ifdef WNT
144                     nEncoding = RTL_TEXTENCODING_MS_1252;   break;
145 #else
146                     nEncoding = RTL_TEXTENCODING_ISO_8859_1;   break;
147 #endif
148             case LANGUAGE_CZECH :
149             case LANGUAGE_HUNGARIAN :
150             case LANGUAGE_POLISH :
151 #ifdef WNT
152                     nEncoding = RTL_TEXTENCODING_MS_1250;   break;
153 #else
154                     nEncoding = RTL_TEXTENCODING_ISO_8859_2;   break;
155 #endif
156             case LANGUAGE_RUSSIAN :
157 #ifdef WNT
158                     nEncoding = RTL_TEXTENCODING_MS_1251;   break;
159 #else
160                     nEncoding = RTL_TEXTENCODING_ISO_8859_5;   break;
161 #endif
162             case LANGUAGE_GREEK :
163 #ifdef WNT
164                     nEncoding = RTL_TEXTENCODING_MS_1253;   break;
165 #else
166                     nEncoding = RTL_TEXTENCODING_ISO_8859_7;   break;
167 #endif
168             default:
169                     DBG_ASSERT( 0, "unexpected language" );
170         }
171     }
172 
173     return nEncoding;
174 }
175 
176 ///////////////////////////////////////////////////////////////////////////
177 
178 static inline sal_Int32 Minimum( sal_Int32 n1, sal_Int32 n2, sal_Int32 n3 )
179 {
180     sal_Int32 nMin = n1 < n2 ? n1 : n2;
181     return nMin < n3 ? nMin : n3;
182 }
183 
184 ///////////////////////////////////////////////////////////////////////////
185 
186 class IntArray2D
187 {
188 private:
189     sal_Int32  *pData;
190     int         n1, n2;
191 
192 public:
193     IntArray2D( int nDim1, int nDim2 );
194     ~IntArray2D();
195 
196     sal_Int32 & Value( int i, int k  );
197 };
198 
199 IntArray2D::IntArray2D( int nDim1, int nDim2 )
200 {
201     n1 = nDim1;
202     n2 = nDim2;
203     pData = new sal_Int32[n1 * n2];
204 }
205 
206 IntArray2D::~IntArray2D()
207 {
208     delete[] pData;
209 }
210 
211 sal_Int32 & IntArray2D::Value( int i, int k  )
212 {
213     DBG_ASSERT( 0 <= i && i < n1, "first index out of range" );
214     DBG_ASSERT( 0 <= k && k < n2, "first index out of range" );
215     DBG_ASSERT( i * n2 + k < n1 * n2, "index out of range" );
216     return pData[ i * n2 + k ];
217 }
218 
219 
220 sal_Int32 LevDistance( const OUString &rTxt1, const OUString &rTxt2 )
221 {
222     sal_Int32 nLen1 = rTxt1.getLength();
223     sal_Int32 nLen2 = rTxt2.getLength();
224 
225     if (nLen1 == 0)
226         return nLen2;
227     if (nLen2 == 0)
228         return nLen1;
229 
230     IntArray2D aData( nLen1 + 1, nLen2 + 1 );
231 
232     sal_Int32 i, k;
233     for (i = 0;  i <= nLen1;  ++i)
234         aData.Value(i, 0) = i;
235     for (k = 0;  k <= nLen2;  ++k)
236         aData.Value(0, k) = k;
237     for (i = 1;  i <= nLen1;  ++i)
238     {
239         for (k = 1;  k <= nLen2;  ++k)
240         {
241             sal_Unicode c1i = rTxt1.getStr()[i - 1];
242             sal_Unicode c2k = rTxt2.getStr()[k - 1];
243             sal_Int32 nCost = c1i == c2k ? 0 : 1;
244             sal_Int32 nNew = Minimum( aData.Value(i-1, k  ) + 1,
245                                        aData.Value(i  , k-1) + 1,
246                                        aData.Value(i-1, k-1) + nCost );
247             // take transposition (exchange with left or right char) in account
248             if (2 < i && 2 < k)
249             {
250                 int nT = aData.Value(i-2, k-2) + 1;
251                 if (rTxt1.getStr()[i - 2] != c1i)
252                     ++nT;
253                 if (rTxt2.getStr()[k - 2] != c2k)
254                     ++nT;
255                 if (nT < nNew)
256                     nNew = nT;
257             }
258 
259             aData.Value(i, k) = nNew;
260         }
261     }
262     sal_Int32 nDist = aData.Value(nLen1, nLen2);
263     return nDist;
264  }
265 
266 ///////////////////////////////////////////////////////////////////////////
267 
268 sal_Bool IsUseDicList( const PropertyValues &rProperties,
269 		const uno::Reference< XPropertySet > &rxProp )
270 {
271 	sal_Bool bRes = sal_True;
272 
273 	sal_Int32 nLen = rProperties.getLength();
274 	const PropertyValue *pVal = rProperties.getConstArray();
275 	sal_Int32 i;
276 
277 	for ( i = 0;  i < nLen;  ++i)
278 	{
279 		if (UPH_IS_USE_DICTIONARY_LIST == pVal[i].Handle)
280 		{
281 			pVal[i].Value >>= bRes;
282 			break;
283 		}
284 	}
285 	if (i >= nLen)	// no temporary value found in 'rProperties'
286 	{
287 		uno::Reference< XFastPropertySet > xFast( rxProp, UNO_QUERY );
288 		if (xFast.is())
289 			xFast->getFastPropertyValue( UPH_IS_USE_DICTIONARY_LIST ) >>= bRes;
290 	}
291 
292 	return bRes;
293 }
294 
295 
296 sal_Bool IsIgnoreControlChars( const PropertyValues &rProperties,
297 		const uno::Reference< XPropertySet > &rxProp )
298 {
299 	sal_Bool bRes = sal_True;
300 
301 	sal_Int32 nLen = rProperties.getLength();
302 	const PropertyValue *pVal = rProperties.getConstArray();
303 	sal_Int32 i;
304 
305 	for ( i = 0;  i < nLen;  ++i)
306 	{
307 		if (UPH_IS_IGNORE_CONTROL_CHARACTERS == pVal[i].Handle)
308 		{
309 			pVal[i].Value >>= bRes;
310 			break;
311 		}
312 	}
313 	if (i >= nLen)	// no temporary value found in 'rProperties'
314 	{
315 		uno::Reference< XFastPropertySet > xFast( rxProp, UNO_QUERY );
316 		if (xFast.is())
317 			xFast->getFastPropertyValue( UPH_IS_IGNORE_CONTROL_CHARACTERS ) >>= bRes;
318 	}
319 
320 	return bRes;
321 }
322 
323 
324 static sal_Bool lcl_HasHyphInfo( const uno::Reference<XDictionaryEntry> &xEntry )
325 {
326 	sal_Bool bRes = sal_False;
327 	if (xEntry.is())
328 	{
329 		// there has to be (at least one) '=' denoting a hyphenation position
330 		// and it must not be before any character of the word
331 		sal_Int32 nIdx = xEntry->getDictionaryWord().indexOf( '=' );
332 		bRes = nIdx != -1  &&  nIdx != 0;
333 	}
334 	return bRes;
335 }
336 
337 
338 uno::Reference< XDictionaryEntry > SearchDicList(
339 		const uno::Reference< XDictionaryList > &xDicList,
340 		const OUString &rWord, sal_Int16 nLanguage,
341 		sal_Bool bSearchPosDics, sal_Bool bSearchSpellEntry )
342 {
343 	MutexGuard	aGuard( GetLinguMutex() );
344 
345 	uno::Reference< XDictionaryEntry > xEntry;
346 
347 	if (!xDicList.is())
348 		return xEntry;
349 
350 	const uno::Sequence< uno::Reference< XDictionary > >
351 			aDics( xDicList->getDictionaries() );
352 	const uno::Reference< XDictionary >
353 			*pDic = aDics.getConstArray();
354 	sal_Int32 nDics = xDicList->getCount();
355 
356 	sal_Int32 i;
357 	for (i = 0;  i < nDics;  i++)
358 	{
359 		uno::Reference< XDictionary > axDic( pDic[i], UNO_QUERY );
360 
361 		DictionaryType	eType = axDic->getDictionaryType();
362 		sal_Int16			nLang = LocaleToLanguage( axDic->getLocale() );
363 
364 		if ( axDic.is() && axDic->isActive()
365 			&& (nLang == nLanguage  ||  nLang == LANGUAGE_NONE) )
366 		{
367 			DBG_ASSERT( eType != DictionaryType_MIXED,
368 				"lng : unexpected dictionary type" );
369 
370 			if (   (!bSearchPosDics  &&  eType == DictionaryType_NEGATIVE)
371 				|| ( bSearchPosDics  &&  eType == DictionaryType_POSITIVE))
372 			{
373 				if ( (xEntry = axDic->getEntry( rWord )).is() )
374 				{
375 					if (bSearchSpellEntry || lcl_HasHyphInfo( xEntry ))
376 						break;
377                 }
378                 xEntry = 0;
379 			}
380 		}
381 	}
382 
383 	return xEntry;
384 }
385 
386 
387 sal_Bool SaveDictionaries( const uno::Reference< XDictionaryList > &xDicList )
388 {
389     if (!xDicList.is())
390         return sal_True;
391 
392     sal_Bool bRet = sal_True;
393 
394     Sequence< uno::Reference< XDictionary >  > aDics( xDicList->getDictionaries() );
395     const uno::Reference< XDictionary >  *pDic = aDics.getConstArray();
396     sal_Int32 nCount = aDics.getLength();
397     for (sal_Int32 i = 0;  i < nCount;  i++)
398     {
399         try
400         {
401             uno::Reference< frame::XStorable >  xStor( pDic[i], UNO_QUERY );
402             if (xStor.is())
403             {
404                 if (!xStor->isReadonly() && xStor->hasLocation())
405                     xStor->store();
406             }
407         }
408         catch(uno::Exception &)
409         {
410             bRet = sal_False;
411         }
412     }
413 
414     return bRet;
415 }
416 
417 
418 sal_uInt8 AddEntryToDic(
419         uno::Reference< XDictionary >  &rxDic,
420         const OUString &rWord, sal_Bool bIsNeg,
421         const OUString &rRplcTxt, sal_Int16 /* nRplcLang */,
422         sal_Bool bStripDot )
423 {
424     if (!rxDic.is())
425         return DIC_ERR_NOT_EXISTS;
426 
427     OUString aTmp( rWord );
428     if (bStripDot)
429     {
430         sal_Int32 nLen = rWord.getLength();
431         if (nLen > 0  &&  '.' == rWord[ nLen - 1])
432         {
433             // remove trailing '.'
434             // (this is the official way to do this :-( )
435             aTmp = aTmp.copy( 0, nLen - 1 );
436         }
437     }
438     sal_Bool bAddOk = rxDic->add( aTmp, bIsNeg, rRplcTxt );
439 
440     sal_uInt8 nRes = DIC_ERR_NONE;
441     if (!bAddOk)
442     {
443         if (rxDic->isFull())
444             nRes = DIC_ERR_FULL;
445         else
446         {
447             uno::Reference< frame::XStorable >  xStor( rxDic, UNO_QUERY );
448             if (xStor.is() && xStor->isReadonly())
449                 nRes = DIC_ERR_READONLY;
450             else
451                 nRes = DIC_ERR_UNKNOWN;
452         }
453     }
454 
455     return nRes;
456 }
457 
458 
459 ///////////////////////////////////////////////////////////////////////////
460 
461 LanguageType LocaleToLanguage( const Locale& rLocale )
462 {
463 	//	empty Locale -> LANGUAGE_NONE
464 	if ( rLocale.Language.getLength() == 0 )
465 		return LANGUAGE_NONE;
466 
467 	return MsLangId::convertLocaleToLanguage( rLocale );
468 }
469 
470 
471 Locale& LanguageToLocale( Locale& rLocale, LanguageType eLang )
472 {
473 	if ( eLang != LANGUAGE_NONE	/* &&  eLang != LANGUAGE_SYSTEM */)
474 		MsLangId::convertLanguageToLocale( eLang, rLocale );
475 
476 	return rLocale;
477 }
478 
479 Locale CreateLocale( LanguageType eLang )
480 {
481 	Locale aLocale;
482 	if ( eLang != LANGUAGE_NONE /* &&  eLang != LANGUAGE_SYSTEM */)
483 		return MsLangId::convertLanguageToLocale( eLang );
484 
485 	return aLocale;
486 }
487 
488 uno::Sequence< Locale > LangSeqToLocaleSeq( const uno::Sequence< sal_Int16 > &rLangSeq )
489 {
490     const sal_Int16 *pLang = rLangSeq.getConstArray();
491 	sal_Int32 nCount = rLangSeq.getLength();
492 
493 	uno::Sequence< Locale > aLocales( nCount );
494 	Locale *pLocale = aLocales.getArray();
495 	for (sal_Int32 i = 0;  i < nCount;  ++i)
496 	{
497 		LanguageToLocale( pLocale[i], pLang[ i ] );
498 	}
499 
500 	return aLocales;
501 }
502 
503 uno::Sequence< sal_Int16 >
504 	LocaleSeqToLangSeq( uno::Sequence< Locale > &rLocaleSeq )
505 {
506 	const Locale *pLocale = rLocaleSeq.getConstArray();
507 	sal_Int32 nCount = rLocaleSeq.getLength();
508 
509     uno::Sequence< sal_Int16 >   aLangs( nCount );
510     sal_Int16 *pLang = aLangs.getArray();
511 	for (sal_Int32 i = 0;  i < nCount;  ++i)
512 	{
513 		pLang[i] = LocaleToLanguage( pLocale[i] );
514 	}
515 
516 	return aLangs;
517 }
518 
519 ///////////////////////////////////////////////////////////////////////////
520 
521 sal_Bool    IsReadOnly( const String &rURL, sal_Bool *pbExist )
522 {
523     sal_Bool bRes = sal_False;
524     sal_Bool bExists = sal_False;
525 
526     if (rURL.Len() > 0)
527     {
528         try
529         {
530             uno::Reference< ::com::sun::star::ucb::XCommandEnvironment > xCmdEnv;
531             ::ucbhelper::Content aContent( rURL, xCmdEnv );
532 
533             bExists = aContent.isDocument();
534             if (bExists)
535             {
536                 Any aAny( aContent.getPropertyValue( A2OU( "IsReadOnly" ) ) );
537                 aAny >>= bRes;
538             }
539         }
540         catch (Exception &)
541         {
542             bRes = sal_True;
543         }
544     }
545 
546     if (pbExist)
547         *pbExist = bExists;
548     return bRes;
549 }
550 
551 ///////////////////////////////////////////////////////////////////////////
552 
553 
554 static sal_Bool GetAltSpelling( sal_Int16 &rnChgPos, sal_Int16 &rnChgLen, OUString &rRplc,
555 		uno::Reference< XHyphenatedWord > &rxHyphWord )
556 {
557 	sal_Bool bRes = rxHyphWord->isAlternativeSpelling();
558 	if (bRes)
559 	{
560 		OUString aWord( rxHyphWord->getWord() ),
561 			 	aHyphenatedWord( rxHyphWord->getHyphenatedWord() );
562         sal_Int16   nHyphenationPos     = rxHyphWord->getHyphenationPos();
563         /*sal_Int16   nHyphenPos          = rxHyphWord->getHyphenPos()*/;
564 		const sal_Unicode *pWord	= aWord.getStr(),
565 					  	*pAltWord = aHyphenatedWord.getStr();
566 
567 		// at least char changes directly left or right to the hyphen
568 		// should(!) be handled properly...
569 		//! nHyphenationPos and nHyphenPos differ at most by 1 (see above)
570 		//! Beware: eg "Schiffahrt" in German (pre spelling reform)
571 		//! proves to be a bit nasty (nChgPosLeft and nChgPosRight overlap
572 		//! to an extend.)
573 
574 		// find first different char from left
575 		sal_Int32	nPosL    = 0,
576 					nAltPosL = 0;
577 		for (sal_Int16 i = 0 ;  pWord[ nPosL ] == pAltWord[ nAltPosL ];  nPosL++, nAltPosL++, i++)
578 		{
579 			// restrict changes area beginning to the right to
580 			// the char immediately following the hyphen.
581 			//! serves to insert the additional "f" in "Schiffahrt" at
582 			//! position 5 rather than position 6.
583 			if (i >= nHyphenationPos + 1)
584 				break;
585 		}
586 
587 		// find first different char from right
588 		sal_Int32	nPosR 	 = aWord.getLength() - 1,
589 					nAltPosR = aHyphenatedWord.getLength() - 1;
590 		for ( ;  nPosR >= nPosL  &&  nAltPosR >= nAltPosL
591 					&&  pWord[ nPosR ] == pAltWord[ nAltPosR ];
592 				nPosR--, nAltPosR--)
593 			;
594 
595         rnChgPos = sal::static_int_cast< sal_Int16 >(nPosL);
596         rnChgLen = sal::static_int_cast< sal_Int16 >(nPosR - nPosL + 1);
597 		DBG_ASSERT( rnChgLen >= 0, "nChgLen < 0");
598 
599 		sal_Int32 nTxtStart = nPosL;
600 		sal_Int32 nTxtLen   = nAltPosL - nPosL + 1;
601 		rRplc = aHyphenatedWord.copy( nTxtStart, nTxtLen );
602 	}
603 	return bRes;
604 }
605 
606 
607 static sal_Int16 GetOrigWordPos( const OUString &rOrigWord, sal_Int16 nPos )
608 {
609     sal_Int32 nLen = rOrigWord.getLength();
610     sal_Int32 i = -1;
611     while (nPos >= 0  &&  i++ < nLen)
612 	{
613 		sal_Unicode cChar = rOrigWord[i];
614         sal_Bool bSkip = IsHyphen( cChar ) || IsControlChar( cChar );
615         if (!bSkip)
616             --nPos;
617 	}
618     return sal::static_int_cast< sal_Int16 >((0 <= i  &&  i < nLen) ? i : -1);
619 }
620 
621 
622 sal_Int32 GetPosInWordToCheck( const OUString &rTxt, sal_Int32 nPos )
623 {
624 	sal_Int32 nRes = -1;
625 	sal_Int32 nLen = rTxt.getLength();
626 	if (0 <= nPos  &&  nPos < nLen)
627 	{
628         nRes = 0;
629         for (sal_Int32 i = 0;  i < nPos;  ++i)
630 		{
631             sal_Unicode cChar = rTxt[i];
632             sal_Bool bSkip = IsHyphen( cChar ) || IsControlChar( cChar );
633             if (!bSkip)
634                 ++nRes;
635 		}
636 	}
637 	return nRes;
638 }
639 
640 
641 uno::Reference< XHyphenatedWord > RebuildHyphensAndControlChars(
642 		const OUString &rOrigWord,
643 		uno::Reference< XHyphenatedWord > &rxHyphWord )
644 {
645 	uno::Reference< XHyphenatedWord > xRes;
646 	if (rOrigWord.getLength() && rxHyphWord.is())
647 	{
648 		sal_Int16 	 nChgPos = 0,
649 				 nChgLen = 0;
650 		OUString aRplc;
651 		sal_Bool bAltSpelling = GetAltSpelling( nChgPos, nChgLen, aRplc, rxHyphWord );
652 #if OSL_DEBUG_LEVEL > 1
653         OUString aWord( rxHyphWord->getWord() );
654 #endif
655 
656 		OUString aOrigHyphenatedWord;
657         sal_Int16 nOrigHyphenPos        = -1;
658         sal_Int16 nOrigHyphenationPos   = -1;
659 		if (!bAltSpelling)
660 		{
661 			aOrigHyphenatedWord = rOrigWord;
662             nOrigHyphenPos      = GetOrigWordPos( rOrigWord, rxHyphWord->getHyphenPos() );
663             nOrigHyphenationPos = GetOrigWordPos( rOrigWord, rxHyphWord->getHyphenationPos() );
664 		}
665 		else
666 		{
667             //! should at least work with the German words
668             //! B�-c-k-er and Sc-hif-fah-rt
669 
670 			OUString aLeft, aRight;
671             sal_Int16 nPos = GetOrigWordPos( rOrigWord, nChgPos );
672 
673             // get words like Sc-hif-fah-rt to work correct
674             sal_Int16 nHyphenationPos = rxHyphWord->getHyphenationPos();
675             if (nChgPos > nHyphenationPos)
676                 --nPos;
677 
678             aLeft = rOrigWord.copy( 0, nPos );
679             aRight = rOrigWord.copy( nPos + nChgLen );
680 
681 			aOrigHyphenatedWord =  aLeft;
682 			aOrigHyphenatedWord += aRplc;
683 			aOrigHyphenatedWord += aRight;
684 
685             nOrigHyphenPos      = sal::static_int_cast< sal_Int16 >(aLeft.getLength() +
686                                   rxHyphWord->getHyphenPos() - nChgPos);
687             nOrigHyphenationPos = GetOrigWordPos( rOrigWord, nHyphenationPos );
688 		}
689 
690         if (nOrigHyphenPos == -1  ||  nOrigHyphenationPos == -1)
691 		{
692             DBG_ASSERT( 0, "failed to get nOrigHyphenPos or nOrigHyphenationPos" );
693 		}
694 		else
695 		{
696 			sal_Int16 nLang = LocaleToLanguage( rxHyphWord->getLocale() );
697 			xRes = new HyphenatedWord(
698                         rOrigWord, nLang, nOrigHyphenationPos,
699 						aOrigHyphenatedWord, nOrigHyphenPos );
700 		}
701 
702 	}
703 	return xRes;
704 }
705 
706 
707 ///////////////////////////////////////////////////////////////////////////
708 
709 
710 static CharClass & lcl_GetCharClass()
711 {
712     static CharClass aCC( CreateLocale( LANGUAGE_ENGLISH_US ) );
713     return aCC;
714 }
715 
716 
717 osl::Mutex & lcl_GetCharClassMutex()
718 {
719 	static osl::Mutex	aMutex;
720 	return aMutex;
721 }
722 
723 
724 sal_Bool IsUpper( const String &rText, xub_StrLen nPos, xub_StrLen nLen, sal_Int16 nLanguage )
725 {
726     MutexGuard  aGuard( lcl_GetCharClassMutex() );
727 
728     CharClass &rCC = lcl_GetCharClass();
729     rCC.setLocale( CreateLocale( nLanguage ) );
730     sal_Int32 nFlags = rCC.getStringType( rText, nPos, nLen );
731 	return 		(nFlags & KCharacterType::UPPER)
732 			&& !(nFlags & KCharacterType::LOWER);
733 }
734 
735 
736 sal_Bool IsLower( const String &rText, xub_StrLen nPos, xub_StrLen nLen, sal_Int16 nLanguage )
737 {
738     MutexGuard  aGuard( lcl_GetCharClassMutex() );
739 
740     CharClass &rCC = lcl_GetCharClass();
741     rCC.setLocale( CreateLocale( nLanguage ) );
742     sal_Int32 nFlags = rCC.getStringType( rText, nPos, nLen );
743     return      (nFlags & KCharacterType::LOWER)
744             && !(nFlags & KCharacterType::UPPER);
745 }
746 
747 
748 String ToLower( const String &rText, sal_Int16 nLanguage )
749 {
750     MutexGuard  aGuard( lcl_GetCharClassMutex() );
751 
752     CharClass &rCC = lcl_GetCharClass();
753     rCC.setLocale( CreateLocale( nLanguage ) );
754     return rCC.lower( rText );
755 }
756 
757 
758 String ToUpper( const String &rText, sal_Int16 nLanguage )
759 {
760     MutexGuard  aGuard( lcl_GetCharClassMutex() );
761 
762     CharClass &rCC = lcl_GetCharClass();
763     rCC.setLocale( CreateLocale( nLanguage ) );
764     return rCC.upper( rText );
765 }
766 
767 
768 String ToTitle( const String &rText, sal_Int16 nLanguage )
769 {
770     MutexGuard  aGuard( lcl_GetCharClassMutex() );
771 
772     CharClass &rCC = lcl_GetCharClass();
773     rCC.setLocale( CreateLocale( nLanguage ) );
774     return rCC.toTitle( rText, 0, rText.Len() );
775 }
776 
777 
778 sal_Unicode	ToLower( const sal_Unicode cChar, sal_Int16 nLanguage )
779 {
780     MutexGuard  aGuard( lcl_GetCharClassMutex() );
781 
782     CharClass &rCC = lcl_GetCharClass();
783     rCC.setLocale( CreateLocale( nLanguage ) );
784     return rCC.lower( cChar ).GetChar(0);
785 }
786 
787 
788 sal_Unicode	ToUpper( const sal_Unicode cChar, sal_Int16 nLanguage )
789 {
790     MutexGuard  aGuard( lcl_GetCharClassMutex() );
791 
792     CharClass &rCC = lcl_GetCharClass();
793     rCC.setLocale( CreateLocale( nLanguage ) );
794     return rCC.upper( cChar ).GetChar(0);
795 }
796 
797 // sorted(!) array of unicode ranges for code points that are exclusively(!) used as numbers
798 // and thus may NOT not be part of names or words like the Chinese/Japanese number characters
799 static const sal_uInt32 the_aDigitZeroes [] =
800 {
801     0x00000030, //0039    ; Decimal # Nd  [10] DIGIT ZERO..DIGIT NINE
802     0x00000660, //0669    ; Decimal # Nd  [10] ARABIC-INDIC DIGIT ZERO..ARABIC-INDIC DIGIT NINE
803     0x000006F0, //06F9    ; Decimal # Nd  [10] EXTENDED ARABIC-INDIC DIGIT ZERO..EXTENDED ARABIC-INDIC DIGIT NINE
804     0x000007C0, //07C9    ; Decimal # Nd  [10] NKO DIGIT ZERO..NKO DIGIT NINE
805     0x00000966, //096F    ; Decimal # Nd  [10] DEVANAGARI DIGIT ZERO..DEVANAGARI DIGIT NINE
806     0x000009E6, //09EF    ; Decimal # Nd  [10] BENGALI DIGIT ZERO..BENGALI DIGIT NINE
807     0x00000A66, //0A6F    ; Decimal # Nd  [10] GURMUKHI DIGIT ZERO..GURMUKHI DIGIT NINE
808     0x00000AE6, //0AEF    ; Decimal # Nd  [10] GUJARATI DIGIT ZERO..GUJARATI DIGIT NINE
809     0x00000B66, //0B6F    ; Decimal # Nd  [10] ORIYA DIGIT ZERO..ORIYA DIGIT NINE
810     0x00000BE6, //0BEF    ; Decimal # Nd  [10] TAMIL DIGIT ZERO..TAMIL DIGIT NINE
811     0x00000C66, //0C6F    ; Decimal # Nd  [10] TELUGU DIGIT ZERO..TELUGU DIGIT NINE
812     0x00000CE6, //0CEF    ; Decimal # Nd  [10] KANNADA DIGIT ZERO..KANNADA DIGIT NINE
813     0x00000D66, //0D6F    ; Decimal # Nd  [10] MALAYALAM DIGIT ZERO..MALAYALAM DIGIT NINE
814     0x00000E50, //0E59    ; Decimal # Nd  [10] THAI DIGIT ZERO..THAI DIGIT NINE
815     0x00000ED0, //0ED9    ; Decimal # Nd  [10] LAO DIGIT ZERO..LAO DIGIT NINE
816     0x00000F20, //0F29    ; Decimal # Nd  [10] TIBETAN DIGIT ZERO..TIBETAN DIGIT NINE
817     0x00001040, //1049    ; Decimal # Nd  [10] MYANMAR DIGIT ZERO..MYANMAR DIGIT NINE
818     0x00001090, //1099    ; Decimal # Nd  [10] MYANMAR SHAN DIGIT ZERO..MYANMAR SHAN DIGIT NINE
819     0x000017E0, //17E9    ; Decimal # Nd  [10] KHMER DIGIT ZERO..KHMER DIGIT NINE
820     0x00001810, //1819    ; Decimal # Nd  [10] MONGOLIAN DIGIT ZERO..MONGOLIAN DIGIT NINE
821     0x00001946, //194F    ; Decimal # Nd  [10] LIMBU DIGIT ZERO..LIMBU DIGIT NINE
822     0x000019D0, //19D9    ; Decimal # Nd  [10] NEW TAI LUE DIGIT ZERO..NEW TAI LUE DIGIT NINE
823     0x00001B50, //1B59    ; Decimal # Nd  [10] BALINESE DIGIT ZERO..BALINESE DIGIT NINE
824     0x00001BB0, //1BB9    ; Decimal # Nd  [10] SUNDANESE DIGIT ZERO..SUNDANESE DIGIT NINE
825     0x00001C40, //1C49    ; Decimal # Nd  [10] LEPCHA DIGIT ZERO..LEPCHA DIGIT NINE
826     0x00001C50, //1C59    ; Decimal # Nd  [10] OL CHIKI DIGIT ZERO..OL CHIKI DIGIT NINE
827     0x0000A620, //A629    ; Decimal # Nd  [10] VAI DIGIT ZERO..VAI DIGIT NINE
828     0x0000A8D0, //A8D9    ; Decimal # Nd  [10] SAURASHTRA DIGIT ZERO..SAURASHTRA DIGIT NINE
829     0x0000A900, //A909    ; Decimal # Nd  [10] KAYAH LI DIGIT ZERO..KAYAH LI DIGIT NINE
830     0x0000AA50, //AA59    ; Decimal # Nd  [10] CHAM DIGIT ZERO..CHAM DIGIT NINE
831     0x0000FF10, //FF19    ; Decimal # Nd  [10] FULLWIDTH DIGIT ZERO..FULLWIDTH DIGIT NINE
832     0x000104A0, //104A9   ; Decimal # Nd  [10] OSMANYA DIGIT ZERO..OSMANYA DIGIT NINE
833     0x0001D7CE  //1D7FF   ; Decimal # Nd  [50] MATHEMATICAL BOLD DIGIT ZERO..MATHEMATICAL MONOSPACE DIGIT NINE
834 };
835 
836 sal_Bool HasDigits( const OUString &rText )
837 {
838     static const int nNumDigitZeroes = sizeof(the_aDigitZeroes) / sizeof(the_aDigitZeroes[0]);
839     const sal_Int32 nLen = rText.getLength();
840 
841     sal_Int32 i = 0;
842     while (i < nLen) // for all characters ...
843     {
844 		const sal_uInt32 nCodePoint = rText.iterateCodePoints( &i );    // handle unicode surrogates correctly...
845         for (int j = 0; j < nNumDigitZeroes; ++j)   // ... check in all 0..9 ranges
846         {
847 			sal_uInt32 nDigitZero = the_aDigitZeroes[ j ];
848 			if (nDigitZero > nCodePoint)
849 				break;
850 			if (/*nDigitZero <= nCodePoint &&*/ nCodePoint <= nDigitZero + 9)
851 				return sal_True;
852         }
853     }
854     return sal_False;
855 }
856 
857 
858 sal_Bool IsNumeric( const String &rText )
859 {
860 	sal_Bool bRes = sal_False;
861 	xub_StrLen nLen = rText.Len();
862 	if (nLen)
863 	{
864 		bRes = sal_True;
865 		xub_StrLen i = 0;
866 		while (i < nLen)
867 		{
868 			sal_Unicode cChar = rText.GetChar( i++ );
869 			if ( !((sal_Unicode)'0' <= cChar  &&  cChar <= (sal_Unicode)'9') )
870 			{
871 				bRes = sal_False;
872 				break;
873 			}
874 		}
875 	}
876 	return bRes;
877 }
878 
879 
880 ///////////////////////////////////////////////////////////////////////////
881 
882 uno::Reference< XInterface > GetOneInstanceService( const char *pServiceName )
883 {
884 	uno::Reference< XInterface > xRef;
885 
886 	if (pServiceName)
887 	{
888 		uno::Reference< XMultiServiceFactory >  xMgr( getProcessServiceFactory() );
889 		if (xMgr.is())
890 		{
891 			try
892 			{
893 				xRef = xMgr->createInstance( A2OU( pServiceName ) );
894 			}
895 			catch (uno::Exception &)
896 			{
897                 DBG_ASSERT( 0, "createInstance failed" );
898 			}
899 		}
900 	}
901 
902 	return xRef;
903 }
904 
905 uno::Reference< XPropertySet > GetLinguProperties()
906 {
907 	return uno::Reference< XPropertySet > (
908 		GetOneInstanceService( SN_LINGU_PROPERTIES ), UNO_QUERY );
909 }
910 
911 uno::Reference< XSearchableDictionaryList > GetSearchableDictionaryList()
912 {
913 	return uno::Reference< XSearchableDictionaryList > (
914 		GetOneInstanceService( SN_DICTIONARY_LIST ), UNO_QUERY );
915 }
916 
917 uno::Reference< XDictionaryList > GetDictionaryList()
918 {
919 	return uno::Reference< XDictionaryList > (
920 		GetOneInstanceService( SN_DICTIONARY_LIST ), UNO_QUERY );
921 }
922 
923 uno::Reference< XDictionary > GetIgnoreAllList()
924 {
925     uno::Reference< XDictionary > xRes;
926     uno::Reference< XDictionaryList > xDL( GetDictionaryList() );
927     if (xDL.is())
928         xRes = xDL->getDictionaryByName( A2OU("IgnoreAllList") );
929     return xRes;
930 }
931 
932 ///////////////////////////////////////////////////////////////////////////
933 
934 AppExitListener::AppExitListener()
935 {
936 	// add object to Desktop EventListeners in order to properly call
937 	// the AtExit function at appliction exit.
938 	uno::Reference< XMultiServiceFactory > xMgr = getProcessServiceFactory();
939 
940 	if (xMgr.is())
941 	{
942 		try
943 		{
944 			xDesktop = uno::Reference< frame::XDesktop >(
945 					xMgr->createInstance( A2OU( SN_DESKTOP ) ), UNO_QUERY );
946 		}
947 		catch (uno::Exception &)
948 		{
949             DBG_ASSERT( 0, "createInstance failed" );
950 		}
951 	}
952 }
953 
954 AppExitListener::~AppExitListener()
955 {
956 }
957 
958 
959 void AppExitListener::Activate()
960 {
961 	if (xDesktop.is())
962 		xDesktop->addTerminateListener( this );
963 }
964 
965 
966 void AppExitListener::Deactivate()
967 {
968 	if (xDesktop.is())
969 		xDesktop->removeTerminateListener( this );
970 }
971 
972 
973 void SAL_CALL
974 	AppExitListener::disposing( const EventObject& rEvtSource )
975 		throw(RuntimeException)
976 {
977 	MutexGuard	aGuard( GetLinguMutex() );
978 
979 	if (xDesktop.is()  &&  rEvtSource.Source == xDesktop)
980 	{
981 		xDesktop = NULL;	//! release reference to desktop
982 	}
983 }
984 
985 
986 void SAL_CALL
987     AppExitListener::queryTermination( const EventObject& /*rEvtSource*/ )
988 		throw(frame::TerminationVetoException, RuntimeException)
989 {
990 	//MutexGuard	aGuard( GetLinguMutex() );
991 }
992 
993 
994 void SAL_CALL
995 	AppExitListener::notifyTermination( const EventObject& rEvtSource )
996 		throw(RuntimeException)
997 {
998 	MutexGuard	aGuard( GetLinguMutex() );
999 
1000 	if (xDesktop.is()  &&  rEvtSource.Source == xDesktop)
1001 	{
1002 		AtExit();
1003 	}
1004 }
1005 
1006 ///////////////////////////////////////////////////////////////////////////
1007 
1008 }	// namespace linguistic
1009 
1010