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