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