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 file
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 <i18nlangtag/lang.h>
21 #include <i18nlangtag/languagetag.hxx>
22 #include <tools/debug.hxx>
23 #include <svl/lngmisc.hxx>
24 
25 #include <com/sun/star/beans/XPropertySet.hpp>
26 #include <comphelper/processfactory.hxx>
27 #include <comphelper/sequence.hxx>
28 #include <com/sun/star/uno/XComponentContext.hpp>
29 #include <osl/mutex.hxx>
30 #include <sal/log.hxx>
31 
32 #include "thesdsp.hxx"
33 #include <linguistic/misc.hxx>
34 
35 using namespace osl;
36 using namespace com::sun::star;
37 using namespace com::sun::star::beans;
38 using namespace com::sun::star::lang;
39 using namespace com::sun::star::uno;
40 using namespace com::sun::star::linguistic2;
41 using namespace linguistic;
42 
43 
SvcListHasLanguage(const Sequence<Reference<XThesaurus>> & rRefs,const Locale & rLocale)44 static bool SvcListHasLanguage(
45         const Sequence< Reference< XThesaurus > > &rRefs,
46         const Locale &rLocale )
47 {
48     return std::any_of(rRefs.begin(), rRefs.end(),
49         [&rLocale](const Reference<XThesaurus>& rRef) {
50             return rRef.is() && rRef->hasLocale( rLocale ); });
51 }
52 
53 
ThesaurusDispatcher()54 ThesaurusDispatcher::ThesaurusDispatcher()
55 {
56 }
57 
58 
~ThesaurusDispatcher()59 ThesaurusDispatcher::~ThesaurusDispatcher()
60 {
61     ClearSvcList();
62 }
63 
64 
ClearSvcList()65 void ThesaurusDispatcher::ClearSvcList()
66 {
67     // release memory for each table entry
68     ThesSvcByLangMap_t().swap(aSvcMap);
69 }
70 
71 
72 Sequence< Locale > SAL_CALL
getLocales()73     ThesaurusDispatcher::getLocales()
74 {
75     MutexGuard  aGuard( GetLinguMutex() );
76 
77     std::vector<Locale> aLocales;
78     aLocales.reserve(aSvcMap.size());
79 
80     std::transform(aSvcMap.begin(), aSvcMap.end(), std::back_inserter(aLocales),
81         [](ThesSvcByLangMap_t::const_reference elem) { return LanguageTag::convertToLocale(elem.first); });
82 
83     return comphelper::containerToSequence(aLocales);
84 }
85 
86 
87 sal_Bool SAL_CALL
hasLocale(const Locale & rLocale)88     ThesaurusDispatcher::hasLocale( const Locale& rLocale )
89 {
90     MutexGuard  aGuard( GetLinguMutex() );
91     ThesSvcByLangMap_t::const_iterator aIt( aSvcMap.find( LinguLocaleToLanguage( rLocale ) ) );
92     return aIt != aSvcMap.end();
93 }
94 
95 
96 Sequence< Reference< XMeaning > > SAL_CALL
queryMeanings(const OUString & rTerm,const Locale & rLocale,const css::uno::Sequence<::css::beans::PropertyValue> & rProperties)97     ThesaurusDispatcher::queryMeanings(
98             const OUString& rTerm, const Locale& rLocale,
99             const css::uno::Sequence< ::css::beans::PropertyValue >& rProperties )
100 {
101     MutexGuard  aGuard( GetLinguMutex() );
102 
103     Sequence< Reference< XMeaning > >   aMeanings;
104 
105     LanguageType nLanguage = LinguLocaleToLanguage( rLocale );
106     if (LinguIsUnspecified( nLanguage) || rTerm.isEmpty())
107         return aMeanings;
108 
109     // search for entry with that language
110     ThesSvcByLangMap_t::iterator    aIt( aSvcMap.find( nLanguage ) );
111     LangSvcEntries_Thes     *pEntry = aIt != aSvcMap.end() ? aIt->second.get() : nullptr;
112 
113     if (pEntry)
114     {
115         OUString aChkWord = rTerm.replace( SVT_HARD_SPACE, ' ' );
116         RemoveHyphens( aChkWord );
117         if (IsIgnoreControlChars( rProperties, GetPropSet() ))
118             RemoveControlChars( aChkWord );
119 
120         sal_Int32 nLen = pEntry->aSvcRefs.getLength();
121         DBG_ASSERT( nLen == pEntry->aSvcImplNames.getLength(),
122                 "lng : sequence length mismatch");
123         DBG_ASSERT( pEntry->nLastTriedSvcIndex < nLen,
124                 "lng : index out of range");
125 
126         sal_Int32 i = 0;
127 
128         // try already instantiated services first
129         {
130             const Reference< XThesaurus > *pRef = pEntry->aSvcRefs.getConstArray();
131             while (i <= pEntry->nLastTriedSvcIndex
132                    &&  !aMeanings.hasElements())
133             {
134                 if (pRef[i].is()  &&  pRef[i]->hasLocale( rLocale ))
135                     aMeanings = pRef[i]->queryMeanings( aChkWord, rLocale, rProperties );
136                 ++i;
137             }
138         }
139 
140         // if still no result instantiate new services and try those
141         if (!aMeanings.hasElements()
142             &&  pEntry->nLastTriedSvcIndex < nLen - 1)
143         {
144             const OUString *pImplNames = pEntry->aSvcImplNames.getConstArray();
145             Reference< XThesaurus > *pRef = pEntry->aSvcRefs.getArray();
146 
147             Reference< XComponentContext > xContext(
148                 comphelper::getProcessComponentContext() );
149 
150             // build service initialization argument
151             Sequence< Any > aArgs(1);
152             aArgs.getArray()[0] <<= GetPropSet();
153 
154             while (i < nLen  &&  !aMeanings.hasElements())
155             {
156                 // create specific service via it's implementation name
157                 Reference< XThesaurus > xThes;
158                 try
159                 {
160                     xThes.set(  xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
161                                     pImplNames[i], aArgs, xContext ),
162                                 UNO_QUERY );
163                 }
164                 catch (uno::Exception &)
165                 {
166                     SAL_WARN( "linguistic", "createInstanceWithArguments failed" );
167                 }
168                 pRef[i] = xThes;
169 
170                 if (xThes.is()  &&  xThes->hasLocale( rLocale ))
171                     aMeanings = xThes->queryMeanings( aChkWord, rLocale, rProperties );
172 
173                 pEntry->nLastTriedSvcIndex = static_cast<sal_Int16>(i);
174                 ++i;
175             }
176 
177             // if language is not supported by any of the services
178             // remove it from the list.
179             if (i == nLen  &&  !aMeanings.hasElements())
180             {
181                 if (!SvcListHasLanguage( pEntry->aSvcRefs, rLocale ))
182                     aSvcMap.erase( nLanguage );
183             }
184         }
185     }
186 
187     return aMeanings;
188 }
189 
190 
SetServiceList(const Locale & rLocale,const Sequence<OUString> & rSvcImplNames)191 void ThesaurusDispatcher::SetServiceList( const Locale &rLocale,
192         const Sequence< OUString > &rSvcImplNames )
193 {
194     MutexGuard  aGuard( GetLinguMutex() );
195 
196     LanguageType nLanguage = LinguLocaleToLanguage( rLocale );
197 
198     sal_Int32 nLen = rSvcImplNames.getLength();
199     if (0 == nLen)
200         // remove entry
201         aSvcMap.erase( nLanguage );
202     else
203     {
204         // modify/add entry
205         LangSvcEntries_Thes *pEntry = aSvcMap[ nLanguage ].get();
206         if (pEntry)
207         {
208             pEntry->Clear();
209             pEntry->aSvcImplNames = rSvcImplNames;
210             pEntry->aSvcRefs = Sequence< Reference < XThesaurus > >( nLen );
211         }
212         else
213         {
214             auto pTmpEntry = std::make_shared<LangSvcEntries_Thes>( rSvcImplNames );
215             pTmpEntry->aSvcRefs = Sequence< Reference < XThesaurus > >( nLen );
216             aSvcMap[ nLanguage ] = pTmpEntry;
217         }
218     }
219 }
220 
221 
222 Sequence< OUString >
GetServiceList(const Locale & rLocale) const223     ThesaurusDispatcher::GetServiceList( const Locale &rLocale ) const
224 {
225     MutexGuard  aGuard( GetLinguMutex() );
226 
227     Sequence< OUString > aRes;
228 
229     // search for entry with that language and use data from that
230     LanguageType nLanguage = LinguLocaleToLanguage( rLocale );
231     const ThesSvcByLangMap_t::const_iterator  aIt( aSvcMap.find( nLanguage ) );
232     const LangSvcEntries_Thes       *pEntry = aIt != aSvcMap.end() ? aIt->second.get() : nullptr;
233     if (pEntry)
234         aRes = pEntry->aSvcImplNames;
235 
236     return aRes;
237 }
238 
239 
240 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
241