1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file754
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <memory>
21 #include <sal/log.hxx>
22 #include <svl/lngmisc.hxx>
23 #include <ucbhelper/content.hxx>
24 #include <i18nlangtag/languagetag.hxx>
25 #include <com/sun/star/beans/XPropertySet.hpp>
26 #include <com/sun/star/beans/XFastPropertySet.hpp>
27 #include <com/sun/star/beans/PropertyValues.hpp>
28 #include <com/sun/star/frame/Desktop.hpp>
29 #include <com/sun/star/frame/XStorable.hpp>
30 #include <com/sun/star/linguistic2/DictionaryType.hpp>
31 #include <com/sun/star/linguistic2/DictionaryList.hpp>
32 #include <com/sun/star/linguistic2/LinguProperties.hpp>
33 #include <com/sun/star/ucb/XCommandEnvironment.hpp>
34 #include <com/sun/star/uno/Sequence.hxx>
35 #include <com/sun/star/uno/Reference.h>
36 #include <comphelper/processfactory.hxx>
37 #include <comphelper/sequence.hxx>
38 #include <unotools/charclass.hxx>
39 #include <unotools/linguprops.hxx>
40 #include <unotools/localedatawrapper.hxx>
41 #include <unotools/syslocale.hxx>
42 #include <svtools/strings.hrc>
43 #include <unotools/resmgr.hxx>
44 
45 #include <rtl/instance.hxx>
46 
47 #include <linguistic/misc.hxx>
48 #include <linguistic/hyphdta.hxx>
49 
50 using namespace osl;
51 using namespace com::sun::star;
52 using namespace com::sun::star::beans;
53 using namespace com::sun::star::lang;
54 using namespace com::sun::star::uno;
55 using namespace com::sun::star::i18n;
56 using namespace com::sun::star::linguistic2;
57 
58 namespace linguistic
59 {
60 
61 namespace {
62 
63 //!! multi-thread safe mutex for all platforms !!
64 struct LinguMutex : public rtl::Static< osl::Mutex, LinguMutex >
65 {
66 };
67 
68 }
69 
GetLinguMutex()70 osl::Mutex &    GetLinguMutex()
71 {
72     return LinguMutex::get();
73 }
74 
GetLocaleDataWrapper(LanguageType nLang)75 const LocaleDataWrapper & GetLocaleDataWrapper( LanguageType nLang )
76 {
77     static std::unique_ptr<LocaleDataWrapper> xLclDtaWrp;
78     if (!xLclDtaWrp || xLclDtaWrp->getLoadedLanguageTag().getLanguageType() != nLang)
79         xLclDtaWrp.reset(new LocaleDataWrapper(LanguageTag( nLang )));
80     return *xLclDtaWrp;
81 }
82 
LinguLocaleToLanguage(const css::lang::Locale & rLocale)83 LanguageType LinguLocaleToLanguage( const css::lang::Locale& rLocale )
84 {
85     if ( rLocale.Language.isEmpty() )
86         return LANGUAGE_NONE;
87     return LanguageTag::convertToLanguageType( rLocale );
88 }
89 
LinguLanguageToLocale(LanguageType nLanguage)90 css::lang::Locale LinguLanguageToLocale( LanguageType nLanguage )
91 {
92     if (nLanguage == LANGUAGE_NONE)
93         return css::lang::Locale();
94     return LanguageTag::convertToLocale( nLanguage);
95 }
96 
LinguIsUnspecified(LanguageType nLanguage)97 bool LinguIsUnspecified( LanguageType nLanguage )
98 {
99     return nLanguage.anyOf(
100          LANGUAGE_NONE,
101          LANGUAGE_UNDETERMINED,
102          LANGUAGE_MULTIPLE);
103 }
104 
105 // When adding anything keep both LinguIsUnspecified() methods in sync!
106 // For mappings between language code string and LanguageType see
107 // i18nlangtag/source/isolang/isolang.cxx
108 
LinguIsUnspecified(const OUString & rBcp47)109 bool LinguIsUnspecified( const OUString & rBcp47 )
110 {
111     if (rBcp47.getLength() != 3)
112         return false;
113     return rBcp47 == "zxx" || rBcp47 == "und" || rBcp47 == "mul";
114 }
115 
Minimum(sal_Int32 n1,sal_Int32 n2,sal_Int32 n3)116 static sal_Int32 Minimum( sal_Int32 n1, sal_Int32 n2, sal_Int32 n3 )
117 {
118     return std::min(std::min(n1, n2), n3);
119 }
120 
121 namespace {
122 
123 class IntArray2D
124 {
125 private:
126     std::unique_ptr<sal_Int32[]>  pData;
127     int                           n1, n2;
128 
129 public:
130     IntArray2D( int nDim1, int nDim2 );
131 
132     sal_Int32 & Value( int i, int k  );
133 };
134 
135 }
136 
IntArray2D(int nDim1,int nDim2)137 IntArray2D::IntArray2D( int nDim1, int nDim2 )
138 {
139     n1 = nDim1;
140     n2 = nDim2;
141     pData.reset( new sal_Int32[n1 * n2] );
142 }
143 
Value(int i,int k)144 sal_Int32 & IntArray2D::Value( int i, int k  )
145 {
146     assert( (0 <= i && i < n1) && "first index out of range" );
147     assert( (0 <= k && k < n2) && "second index out of range" );
148     assert( (i * n2 + k < n1 * n2) && "index out of range" );
149     return pData[ i * n2 + k ];
150 }
151 
LevDistance(const OUString & rTxt1,const OUString & rTxt2)152 sal_Int32 LevDistance( const OUString &rTxt1, const OUString &rTxt2 )
153 {
154     sal_Int32 nLen1 = rTxt1.getLength();
155     sal_Int32 nLen2 = rTxt2.getLength();
156 
157     if (nLen1 == 0)
158         return nLen2;
159     if (nLen2 == 0)
160         return nLen1;
161 
162     IntArray2D aData( nLen1 + 1, nLen2 + 1 );
163 
164     sal_Int32 i, k;
165     for (i = 0;  i <= nLen1;  ++i)
166         aData.Value(i, 0) = i;
167     for (k = 0;  k <= nLen2;  ++k)
168         aData.Value(0, k) = k;
169     for (i = 1;  i <= nLen1;  ++i)
170     {
171         for (k = 1;  k <= nLen2;  ++k)
172         {
173             sal_Unicode c1i = rTxt1[i - 1];
174             sal_Unicode c2k = rTxt2[k - 1];
175             sal_Int32 nCost = c1i == c2k ? 0 : 1;
176             sal_Int32 nNew = Minimum( aData.Value(i-1, k  ) + 1,
177                                        aData.Value(i  , k-1) + 1,
178                                        aData.Value(i-1, k-1) + nCost );
179             // take transposition (exchange with left or right char) in account
180             if (2 < i && 2 < k)
181             {
182                 int nT = aData.Value(i-2, k-2) + 1;
183                 if (rTxt1[i - 2] != c1i)
184                     ++nT;
185                 if (rTxt2[k - 2] != c2k)
186                     ++nT;
187                 if (nT < nNew)
188                     nNew = nT;
189             }
190 
191             aData.Value(i, k) = nNew;
192         }
193     }
194     sal_Int32 nDist = aData.Value(nLen1, nLen2);
195     return nDist;
196 }
197 
IsUseDicList(const PropertyValues & rProperties,const uno::Reference<XPropertySet> & rxProp)198 bool IsUseDicList( const PropertyValues &rProperties,
199         const uno::Reference< XPropertySet > &rxProp )
200 {
201     bool bRes = true;
202 
203     const PropertyValue *pVal = std::find_if(rProperties.begin(), rProperties.end(),
204         [](const PropertyValue& rVal) { return UPH_IS_USE_DICTIONARY_LIST == rVal.Handle; });
205 
206     if (pVal != rProperties.end())
207     {
208         pVal->Value >>= bRes;
209     }
210     else  // no temporary value found in 'rProperties'
211     {
212         uno::Reference< XFastPropertySet > xFast( rxProp, UNO_QUERY );
213         if (xFast.is())
214             xFast->getFastPropertyValue( UPH_IS_USE_DICTIONARY_LIST ) >>= bRes;
215     }
216 
217     return bRes;
218 }
219 
IsIgnoreControlChars(const PropertyValues & rProperties,const uno::Reference<XPropertySet> & rxProp)220 bool IsIgnoreControlChars( const PropertyValues &rProperties,
221         const uno::Reference< XPropertySet > &rxProp )
222 {
223     bool bRes = true;
224 
225     const PropertyValue *pVal = std::find_if(rProperties.begin(), rProperties.end(),
226         [](const PropertyValue& rVal) { return UPH_IS_IGNORE_CONTROL_CHARACTERS == rVal.Handle; });
227 
228     if (pVal != rProperties.end())
229     {
230         pVal->Value >>= bRes;
231     }
232     else  // no temporary value found in 'rProperties'
233     {
234         uno::Reference< XFastPropertySet > xFast( rxProp, UNO_QUERY );
235         if (xFast.is())
236             xFast->getFastPropertyValue( UPH_IS_IGNORE_CONTROL_CHARACTERS ) >>= bRes;
237     }
238 
239     return bRes;
240 }
241 
lcl_HasHyphInfo(const uno::Reference<XDictionaryEntry> & xEntry)242 static bool lcl_HasHyphInfo( const uno::Reference<XDictionaryEntry> &xEntry )
243 {
244     bool bRes = false;
245     if (xEntry.is())
246     {
247         // there has to be (at least one) '=' or '[' denoting a hyphenation position
248         // and it must not be before any character of the word
249         sal_Int32 nIdx = xEntry->getDictionaryWord().indexOf( '=' );
250         if (nIdx == -1)
251             nIdx = xEntry->getDictionaryWord().indexOf( '[' );
252         bRes = nIdx != -1  &&  nIdx != 0;
253     }
254     return bRes;
255 }
256 
SearchDicList(const uno::Reference<XSearchableDictionaryList> & xDicList,const OUString & rWord,LanguageType nLanguage,bool bSearchPosDics,bool bSearchSpellEntry)257 uno::Reference< XDictionaryEntry > SearchDicList(
258         const uno::Reference< XSearchableDictionaryList > &xDicList,
259         const OUString &rWord, LanguageType nLanguage,
260         bool bSearchPosDics, bool bSearchSpellEntry )
261 {
262     MutexGuard  aGuard( GetLinguMutex() );
263 
264     uno::Reference< XDictionaryEntry > xEntry;
265 
266     if (!xDicList.is())
267         return xEntry;
268 
269     const uno::Sequence< uno::Reference< XDictionary > >
270             aDics( xDicList->getDictionaries() );
271     const uno::Reference< XDictionary >
272             *pDic = aDics.getConstArray();
273     sal_Int32 nDics = xDicList->getCount();
274 
275     sal_Int32 i;
276     for (i = 0;  i < nDics;  i++)
277     {
278         uno::Reference< XDictionary > axDic = pDic[i];
279 
280         DictionaryType  eType = axDic->getDictionaryType();
281         LanguageType    nLang = LinguLocaleToLanguage( axDic->getLocale() );
282 
283         if ( axDic.is() && axDic->isActive()
284             && (nLang == nLanguage  ||  LinguIsUnspecified( nLang)) )
285         {
286             // DictionaryType_MIXED is deprecated
287             SAL_WARN_IF(eType == DictionaryType_MIXED, "linguistic", "unexpected dictionary type");
288 
289             if (   (!bSearchPosDics  &&  eType == DictionaryType_NEGATIVE)
290                 || ( bSearchPosDics  &&  eType == DictionaryType_POSITIVE))
291             {
292                 xEntry = axDic->getEntry( rWord );
293                 if ( xEntry.is() && (bSearchSpellEntry || lcl_HasHyphInfo( xEntry )) )
294                     break;
295                 xEntry = nullptr;
296             }
297         }
298     }
299 
300     return xEntry;
301 }
302 
SaveDictionaries(const uno::Reference<XSearchableDictionaryList> & xDicList)303 bool SaveDictionaries( const uno::Reference< XSearchableDictionaryList > &xDicList )
304 {
305     if (!xDicList.is())
306         return true;
307 
308     bool bRet = true;
309 
310     const Sequence< uno::Reference< XDictionary >  > aDics( xDicList->getDictionaries() );
311     for (const uno::Reference<XDictionary>& rDic : aDics)
312     {
313         try
314         {
315             uno::Reference< frame::XStorable >  xStor( rDic, UNO_QUERY );
316             if (xStor.is())
317             {
318                 if (!xStor->isReadonly() && xStor->hasLocation())
319                     xStor->store();
320             }
321         }
322         catch(uno::Exception &)
323         {
324             bRet = false;
325         }
326     }
327 
328     return bRet;
329 }
330 
AddEntryToDic(uno::Reference<XDictionary> const & rxDic,const OUString & rWord,bool bIsNeg,const OUString & rRplcTxt,bool bStripDot)331 DictionaryError AddEntryToDic(
332         uno::Reference< XDictionary > const &rxDic,
333         const OUString &rWord, bool bIsNeg,
334         const OUString &rRplcTxt,
335         bool bStripDot )
336 {
337     if (!rxDic.is())
338         return DictionaryError::NOT_EXISTS;
339 
340     OUString aTmp( rWord );
341     if (bStripDot)
342     {
343         sal_Int32 nLen = rWord.getLength();
344         if (nLen > 0  &&  '.' == rWord[ nLen - 1])
345         {
346             // remove trailing '.'
347             // (this is the official way to do this :-( )
348             aTmp = aTmp.copy( 0, nLen - 1 );
349         }
350     }
351     bool bAddOk = rxDic->add( aTmp, bIsNeg, rRplcTxt );
352 
353     DictionaryError nRes = DictionaryError::NONE;
354     if (!bAddOk)
355     {
356         if (rxDic->isFull())
357             nRes = DictionaryError::FULL;
358         else
359         {
360             uno::Reference< frame::XStorable >  xStor( rxDic, UNO_QUERY );
361             if (xStor.is() && xStor->isReadonly())
362                 nRes = DictionaryError::READONLY;
363             else
364                 nRes = DictionaryError::UNKNOWN;
365         }
366     }
367 
368     return nRes;
369 }
370 
371 std::vector< LanguageType >
LocaleSeqToLangVec(uno::Sequence<Locale> const & rLocaleSeq)372     LocaleSeqToLangVec( uno::Sequence< Locale > const &rLocaleSeq )
373 {
374     std::vector< LanguageType >   aLangs;
375     aLangs.reserve(rLocaleSeq.getLength());
376 
377     std::transform(rLocaleSeq.begin(), rLocaleSeq.end(), std::back_inserter(aLangs),
378         [](const Locale& rLocale) { return LinguLocaleToLanguage(rLocale); });
379 
380     return aLangs;
381 }
382 
383 uno::Sequence< sal_Int16 >
LocaleSeqToLangSeq(uno::Sequence<Locale> const & rLocaleSeq)384      LocaleSeqToLangSeq( uno::Sequence< Locale > const &rLocaleSeq )
385 {
386     std::vector<sal_Int16> aLangs;
387     aLangs.reserve(rLocaleSeq.getLength());
388 
389     std::transform(rLocaleSeq.begin(), rLocaleSeq.end(), std::back_inserter(aLangs),
390         [](const Locale& rLocale) { return static_cast<sal_uInt16>(LinguLocaleToLanguage(rLocale)); });
391 
392     return comphelper::containerToSequence(aLangs);
393 }
IsReadOnly(const OUString & rURL,bool * pbExist)394 bool    IsReadOnly( const OUString &rURL, bool *pbExist )
395 {
396     bool bRes = false;
397     bool bExists = false;
398 
399     if (!rURL.isEmpty())
400     {
401         try
402         {
403             uno::Reference< css::ucb::XCommandEnvironment > xCmdEnv;
404             ::ucbhelper::Content aContent( rURL, xCmdEnv, comphelper::getProcessComponentContext() );
405 
406             bExists = aContent.isDocument();
407             if (bExists)
408             {
409                 Any aAny( aContent.getPropertyValue( "IsReadOnly" ) );
410                 aAny >>= bRes;
411             }
412         }
413         catch (Exception &)
414         {
415             bRes = true;
416         }
417     }
418 
419     if (pbExist)
420         *pbExist = bExists;
421     return bRes;
422 }
423 
GetAltSpelling(sal_Int16 & rnChgPos,sal_Int16 & rnChgLen,OUString & rRplc,uno::Reference<XHyphenatedWord> const & rxHyphWord)424 static bool GetAltSpelling( sal_Int16 &rnChgPos, sal_Int16 &rnChgLen, OUString &rRplc,
425         uno::Reference< XHyphenatedWord > const &rxHyphWord )
426 {
427     bool bRes = rxHyphWord->isAlternativeSpelling();
428     if (bRes)
429     {
430         OUString aWord( rxHyphWord->getWord() ),
431                  aHyphenatedWord( rxHyphWord->getHyphenatedWord() );
432         sal_Int16   nHyphenationPos     = rxHyphWord->getHyphenationPos();
433         /*sal_Int16   nHyphenPos          = rxHyphWord->getHyphenPos()*/;
434         const sal_Unicode *pWord    = aWord.getStr(),
435                           *pAltWord = aHyphenatedWord.getStr();
436 
437         // at least char changes directly left or right to the hyphen
438         // should(!) be handled properly...
439         //! nHyphenationPos and nHyphenPos differ at most by 1 (see above)
440         //! Beware: eg "Schiffahrt" in German (pre spelling reform)
441         //! proves to be a bit nasty (nChgPosLeft and nChgPosRight overlap
442         //! to an extend.)
443 
444         // find first different char from left
445         sal_Int32   nPosL    = 0,
446                     nAltPosL = 0;
447         for (sal_Int16 i = 0 ;  pWord[ nPosL ] == pAltWord[ nAltPosL ];  nPosL++, nAltPosL++, i++)
448         {
449             // restrict changes area beginning to the right to
450             // the char immediately following the hyphen.
451             //! serves to insert the additional "f" in "Schiffahrt" at
452             //! position 5 rather than position 6.
453             if (i >= nHyphenationPos + 1)
454                 break;
455         }
456 
457         // find first different char from right
458         sal_Int32   nPosR    = aWord.getLength() - 1,
459                     nAltPosR = aHyphenatedWord.getLength() - 1;
460         for ( ;  nPosR >= nPosL  &&  nAltPosR >= nAltPosL
461                     &&  pWord[ nPosR ] == pAltWord[ nAltPosR ];
462                 nPosR--, nAltPosR--)
463             ;
464 
465         rnChgPos = sal::static_int_cast< sal_Int16 >(nPosL);
466         rnChgLen = sal::static_int_cast< sal_Int16 >(nAltPosR - nPosL);
467         assert( rnChgLen >= 0 && "nChgLen < 0");
468 
469         sal_Int32 nTxtStart = nPosL;
470         sal_Int32 nTxtLen   = nAltPosR - nPosL + 1;
471         rRplc = aHyphenatedWord.copy( nTxtStart, nTxtLen );
472     }
473     return bRes;
474 }
475 
GetOrigWordPos(const OUString & rOrigWord,sal_Int16 nPos)476 static sal_Int16 GetOrigWordPos( const OUString &rOrigWord, sal_Int16 nPos )
477 {
478     sal_Int32 nLen = rOrigWord.getLength();
479     sal_Int32 i = -1;
480     while (nPos >= 0  &&  i++ < nLen)
481     {
482         sal_Unicode cChar = rOrigWord[i];
483         bool bSkip = IsHyphen( cChar ) || IsControlChar( cChar );
484         if (!bSkip)
485             --nPos;
486     }
487     return sal::static_int_cast< sal_Int16 >((0 <= i  &&  i < nLen) ? i : -1);
488 }
489 
GetPosInWordToCheck(const OUString & rTxt,sal_Int32 nPos)490 sal_Int32 GetPosInWordToCheck( const OUString &rTxt, sal_Int32 nPos )
491 {
492     sal_Int32 nRes = -1;
493     sal_Int32 nLen = rTxt.getLength();
494     if (0 <= nPos  &&  nPos < nLen)
495     {
496         nRes = 0;
497         for (sal_Int32 i = 0;  i < nPos;  ++i)
498         {
499             sal_Unicode cChar = rTxt[i];
500             bool bSkip = IsHyphen( cChar ) || IsControlChar( cChar );
501             if (!bSkip)
502                 ++nRes;
503         }
504     }
505     return nRes;
506 }
507 
RebuildHyphensAndControlChars(const OUString & rOrigWord,uno::Reference<XHyphenatedWord> const & rxHyphWord)508 uno::Reference< XHyphenatedWord > RebuildHyphensAndControlChars(
509         const OUString &rOrigWord,
510         uno::Reference< XHyphenatedWord > const &rxHyphWord )
511 {
512     uno::Reference< XHyphenatedWord > xRes;
513     if (!rOrigWord.isEmpty() && rxHyphWord.is())
514     {
515         sal_Int16    nChgPos = 0,
516                  nChgLen = 0;
517         OUString aRplc;
518         bool bAltSpelling = GetAltSpelling( nChgPos, nChgLen, aRplc, rxHyphWord );
519 
520         OUString aOrigHyphenatedWord;
521         sal_Int16 nOrigHyphenPos        = -1;
522         sal_Int16 nOrigHyphenationPos   = -1;
523         if (!bAltSpelling)
524         {
525             aOrigHyphenatedWord = rOrigWord;
526             nOrigHyphenPos      = GetOrigWordPos( rOrigWord, rxHyphWord->getHyphenPos() );
527             nOrigHyphenationPos = GetOrigWordPos( rOrigWord, rxHyphWord->getHyphenationPos() );
528         }
529         else
530         {
531             //! should at least work with the German words
532             //! B-"u-c-k-er and Sc-hif-fah-rt
533 
534             OUString aLeft, aRight;
535             sal_Int16 nPos = GetOrigWordPos( rOrigWord, nChgPos );
536 
537             // get words like Sc-hif-fah-rt to work correct
538             sal_Int16 nHyphenationPos = rxHyphWord->getHyphenationPos();
539             if (nChgPos > nHyphenationPos)
540                 --nPos;
541 
542             aLeft = rOrigWord.copy( 0, nPos );
543             aRight = rOrigWord.copy( nPos ); // FIXME: changes at the right side
544 
545             aOrigHyphenatedWord =  aLeft + aRplc + aRight;
546 
547             nOrigHyphenPos      = sal::static_int_cast< sal_Int16 >(aLeft.getLength() +
548                                   rxHyphWord->getHyphenPos() - nChgPos);
549             nOrigHyphenationPos = GetOrigWordPos( rOrigWord, nHyphenationPos );
550         }
551 
552         if (nOrigHyphenPos == -1  ||  nOrigHyphenationPos == -1)
553         {
554             SAL_WARN( "linguistic", "failed to get nOrigHyphenPos or nOrigHyphenationPos" );
555         }
556         else
557         {
558             LanguageType nLang = LinguLocaleToLanguage( rxHyphWord->getLocale() );
559             xRes = new HyphenatedWord(
560                         rOrigWord, nLang, nOrigHyphenationPos,
561                         aOrigHyphenatedWord, nOrigHyphenPos );
562         }
563 
564     }
565     return xRes;
566 }
567 
lcl_GetCharClass()568 static CharClass & lcl_GetCharClass()
569 {
570     static CharClass aCC( LanguageTag( LANGUAGE_ENGLISH_US ));
571     return aCC;
572 }
573 
lcl_GetCharClassMutex()574 static osl::Mutex & lcl_GetCharClassMutex()
575 {
576     static osl::Mutex   aMutex;
577     return aMutex;
578 }
579 
IsUpper(const OUString & rText,sal_Int32 nPos,sal_Int32 nLen,LanguageType nLanguage)580 bool IsUpper( const OUString &rText, sal_Int32 nPos, sal_Int32 nLen, LanguageType nLanguage )
581 {
582     MutexGuard  aGuard( lcl_GetCharClassMutex() );
583 
584     CharClass &rCC = lcl_GetCharClass();
585     rCC.setLanguageTag( LanguageTag( nLanguage ));
586     sal_Int32 nFlags = rCC.getStringType( rText, nPos, nLen );
587     return      (nFlags & KCharacterType::UPPER)
588             && !(nFlags & KCharacterType::LOWER);
589 }
590 
capitalType(const OUString & aTerm,CharClass const * pCC)591 CapType capitalType(const OUString& aTerm, CharClass const * pCC)
592 {
593         sal_Int32 tlen = aTerm.getLength();
594         if (pCC && tlen)
595         {
596             sal_Int32 nc = 0;
597             for (sal_Int32 tindex = 0; tindex < tlen; ++tindex)
598             {
599                 if (pCC->getCharacterType(aTerm,tindex) &
600                    css::i18n::KCharacterType::UPPER) nc++;
601             }
602 
603             if (nc == 0)
604                 return CapType::NOCAP;
605             if (nc == tlen)
606                 return CapType::ALLCAP;
607             if ((nc == 1) && (pCC->getCharacterType(aTerm,0) &
608                   css::i18n::KCharacterType::UPPER))
609                 return CapType::INITCAP;
610 
611             return CapType::MIXED;
612         }
613         return CapType::UNKNOWN;
614 }
615 
ToLower(const OUString & rText,LanguageType nLanguage)616 OUString ToLower( const OUString &rText, LanguageType nLanguage )
617 {
618     MutexGuard  aGuard( lcl_GetCharClassMutex() );
619 
620     CharClass &rCC = lcl_GetCharClass();
621     rCC.setLanguageTag( LanguageTag( nLanguage ));
622     return rCC.lowercase( rText );
623 }
624 
625 // sorted(!) array of unicode ranges for code points that are exclusively(!) used as numbers
626 // and thus may NOT not be part of names or words like the Chinese/Japanese number characters
627 const sal_uInt32 the_aDigitZeroes [] =
628 {
629     0x00000030, //0039    ; Decimal # Nd  [10] DIGIT ZERO..DIGIT NINE
630     0x00000660, //0669    ; Decimal # Nd  [10] ARABIC-INDIC DIGIT ZERO..ARABIC-INDIC DIGIT NINE
631     0x000006F0, //06F9    ; Decimal # Nd  [10] EXTENDED ARABIC-INDIC DIGIT ZERO..EXTENDED ARABIC-INDIC DIGIT NINE
632     0x000007C0, //07C9    ; Decimal # Nd  [10] NKO DIGIT ZERO..NKO DIGIT NINE
633     0x00000966, //096F    ; Decimal # Nd  [10] DEVANAGARI DIGIT ZERO..DEVANAGARI DIGIT NINE
634     0x000009E6, //09EF    ; Decimal # Nd  [10] BENGALI DIGIT ZERO..BENGALI DIGIT NINE
635     0x00000A66, //0A6F    ; Decimal # Nd  [10] GURMUKHI DIGIT ZERO..GURMUKHI DIGIT NINE
636     0x00000AE6, //0AEF    ; Decimal # Nd  [10] GUJARATI DIGIT ZERO..GUJARATI DIGIT NINE
637     0x00000B66, //0B6F    ; Decimal # Nd  [10] ODIA DIGIT ZERO..ODIA DIGIT NINE
638     0x00000BE6, //0BEF    ; Decimal # Nd  [10] TAMIL DIGIT ZERO..TAMIL DIGIT NINE
639     0x00000C66, //0C6F    ; Decimal # Nd  [10] TELUGU DIGIT ZERO..TELUGU DIGIT NINE
640     0x00000CE6, //0CEF    ; Decimal # Nd  [10] KANNADA DIGIT ZERO..KANNADA DIGIT NINE
641     0x00000D66, //0D6F    ; Decimal # Nd  [10] MALAYALAM DIGIT ZERO..MALAYALAM DIGIT NINE
642     0x00000E50, //0E59    ; Decimal # Nd  [10] THAI DIGIT ZERO..THAI DIGIT NINE
643     0x00000ED0, //0ED9    ; Decimal # Nd  [10] LAO DIGIT ZERO..LAO DIGIT NINE
644     0x00000F20, //0F29    ; Decimal # Nd  [10] TIBETAN DIGIT ZERO..TIBETAN DIGIT NINE
645     0x00001040, //1049    ; Decimal # Nd  [10] MYANMAR DIGIT ZERO..MYANMAR DIGIT NINE
646     0x00001090, //1099    ; Decimal # Nd  [10] MYANMAR SHAN DIGIT ZERO..MYANMAR SHAN DIGIT NINE
647     0x000017E0, //17E9    ; Decimal # Nd  [10] KHMER DIGIT ZERO..KHMER DIGIT NINE
648     0x00001810, //1819    ; Decimal # Nd  [10] MONGOLIAN DIGIT ZERO..MONGOLIAN DIGIT NINE
649     0x00001946, //194F    ; Decimal # Nd  [10] LIMBU DIGIT ZERO..LIMBU DIGIT NINE
650     0x000019D0, //19D9    ; Decimal # Nd  [10] NEW TAI LUE DIGIT ZERO..NEW TAI LUE DIGIT NINE
651     0x00001B50, //1B59    ; Decimal # Nd  [10] BALINESE DIGIT ZERO..BALINESE DIGIT NINE
652     0x00001BB0, //1BB9    ; Decimal # Nd  [10] SUNDANESE DIGIT ZERO..SUNDANESE DIGIT NINE
653     0x00001C40, //1C49    ; Decimal # Nd  [10] LEPCHA DIGIT ZERO..LEPCHA DIGIT NINE
654     0x00001C50, //1C59    ; Decimal # Nd  [10] OL CHIKI DIGIT ZERO..OL CHIKI DIGIT NINE
655     0x0000A620, //A629    ; Decimal # Nd  [10] VAI DIGIT ZERO..VAI DIGIT NINE
656     0x0000A8D0, //A8D9    ; Decimal # Nd  [10] SAURASHTRA DIGIT ZERO..SAURASHTRA DIGIT NINE
657     0x0000A900, //A909    ; Decimal # Nd  [10] KAYAH LI DIGIT ZERO..KAYAH LI DIGIT NINE
658     0x0000AA50, //AA59    ; Decimal # Nd  [10] CHAM DIGIT ZERO..CHAM DIGIT NINE
659     0x0000FF10, //FF19    ; Decimal # Nd  [10] FULLWIDTH DIGIT ZERO..FULLWIDTH DIGIT NINE
660     0x000104A0, //104A9   ; Decimal # Nd  [10] OSMANYA DIGIT ZERO..OSMANYA DIGIT NINE
661     0x0001D7CE  //1D7FF   ; Decimal # Nd  [50] MATHEMATICAL BOLD DIGIT ZERO..MATHEMATICAL MONOSPACE DIGIT NINE
662 };
663 
HasDigits(const OUString & rText)664 bool HasDigits( const OUString &rText )
665 {
666     const sal_Int32 nLen = rText.getLength();
667 
668     sal_Int32 i = 0;
669     while (i < nLen) // for all characters ...
670     {
671         const sal_uInt32 nCodePoint = rText.iterateCodePoints( &i );    // handle unicode surrogates correctly...
672         for (unsigned int nDigitZero : the_aDigitZeroes)   // ... check in all 0..9 ranges
673         {
674             if (nDigitZero > nCodePoint)
675                 break;
676             if (/*nDigitZero <= nCodePoint &&*/ nCodePoint <= nDigitZero + 9)
677                 return true;
678         }
679     }
680     return false;
681 }
682 
IsNumeric(const OUString & rText)683 bool IsNumeric( const OUString &rText )
684 {
685     bool bRes = false;
686     if (!rText.isEmpty())
687     {
688         sal_Int32 nLen = rText.getLength();
689         bRes = true;
690         for(sal_Int32 i = 0; i < nLen; ++i)
691         {
692             sal_Unicode cChar = rText[ i ];
693             if ( '0' > cChar  ||  cChar > '9' )
694             {
695                 bRes = false;
696                 break;
697             }
698         }
699     }
700     return bRes;
701 }
702 
GetLinguProperties()703 uno::Reference< XLinguProperties > GetLinguProperties()
704 {
705     return LinguProperties::create( comphelper::getProcessComponentContext() );
706 }
707 
GetDictionaryList()708 uno::Reference< XSearchableDictionaryList > GetDictionaryList()
709 {
710     uno::Reference< XComponentContext > xContext( comphelper::getProcessComponentContext() );
711     uno::Reference< XSearchableDictionaryList > xRef;
712     try
713     {
714         xRef = DictionaryList::create(xContext);
715     }
716     catch (const uno::Exception &)
717     {
718         SAL_WARN( "linguistic", "createInstance failed" );
719     }
720 
721     return xRef;
722 }
723 
GetIgnoreAllList()724 uno::Reference< XDictionary > GetIgnoreAllList()
725 {
726     uno::Reference< XDictionary > xRes;
727     uno::Reference< XSearchableDictionaryList > xDL( GetDictionaryList() );
728     if (xDL.is())
729     {
730         std::locale loc(Translate::Create("svt"));
731         xRes = xDL->getDictionaryByName( Translate::get(STR_DESCRIPTION_IGNOREALLLIST, loc) );
732     }
733     return xRes;
734 }
735 
AppExitListener()736 AppExitListener::AppExitListener()
737 {
738     // add object to Desktop EventListeners in order to properly call
739     // the AtExit function at application exit.
740     uno::Reference< XComponentContext > xContext( comphelper::getProcessComponentContext() );
741 
742     try
743     {
744         xDesktop = frame::Desktop::create(xContext);
745     }
746     catch (const uno::Exception &)
747     {
748         SAL_WARN( "linguistic", "createInstance failed" );
749     }
750 }
751 
~AppExitListener()752 AppExitListener::~AppExitListener()
753 {
754 }
755 
Activate()756 void AppExitListener::Activate()
757 {
758     if (xDesktop.is())
759         xDesktop->addTerminateListener( this );
760 }
761 
Deactivate()762 void AppExitListener::Deactivate()
763 {
764     if (xDesktop.is())
765         xDesktop->removeTerminateListener( this );
766 }
767 
768 void SAL_CALL
disposing(const EventObject & rEvtSource)769     AppExitListener::disposing( const EventObject& rEvtSource )
770 {
771     MutexGuard  aGuard( GetLinguMutex() );
772 
773     if (xDesktop.is()  &&  rEvtSource.Source == xDesktop)
774     {
775         xDesktop = nullptr;    //! release reference to desktop
776     }
777 }
778 
779 void SAL_CALL
queryTermination(const EventObject &)780     AppExitListener::queryTermination( const EventObject& /*rEvtSource*/ )
781 {
782 }
783 
784 void SAL_CALL
notifyTermination(const EventObject & rEvtSource)785     AppExitListener::notifyTermination( const EventObject& rEvtSource )
786 {
787     MutexGuard  aGuard( GetLinguMutex() );
788 
789     if (xDesktop.is()  &&  rEvtSource.Source == xDesktop)
790     {
791         AtExit();
792     }
793 }
794 
795 }   // namespace linguistic
796 
797 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
798