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 <sal/config.h>
21 #include <sal/log.hxx>
22 
23 #include <com/sun/star/deployment/DeploymentException.hpp>
24 #include <com/sun/star/deployment/ExtensionManager.hpp>
25 #include <com/sun/star/container/XContentEnumerationAccess.hpp>
26 #include <com/sun/star/container/XEnumeration.hpp>
27 #include <com/sun/star/lang/XSingleComponentFactory.hpp>
28 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
29 #include <com/sun/star/linguistic2/XSupportedLocales.hpp>
30 #include <com/sun/star/linguistic2/DictionaryListEventFlags.hpp>
31 #include <com/sun/star/linguistic2/LinguServiceEventFlags.hpp>
32 #include <com/sun/star/linguistic2/ProofreadingIterator.hpp>
33 
34 #include <tools/debug.hxx>
35 #include <unotools/lingucfg.hxx>
36 #include <vcl/svapp.hxx>
37 #include <comphelper/processfactory.hxx>
38 #include <comphelper/sequence.hxx>
39 #include <i18nlangtag/lang.h>
40 #include <i18nlangtag/languagetag.hxx>
41 #include <cppuhelper/factory.hxx>
42 #include <cppuhelper/implbase.hxx>
43 #include <cppuhelper/supportsservice.hxx>
44 #include <cppuhelper/weak.hxx>
45 
46 #include "lngsvcmgr.hxx"
47 #include <linguistic/misc.hxx>
48 #include "spelldsp.hxx"
49 #include "hyphdsp.hxx"
50 #include "thesdsp.hxx"
51 #include "gciterator.hxx"
52 
53 using namespace com::sun::star;
54 using namespace linguistic;
55 
56 uno::Sequence< OUString > static GetLangSvcList( const uno::Any &rVal );
57 uno::Sequence< OUString > static GetLangSvc( const uno::Any &rVal );
58 
lcl_SeqHasString(const uno::Sequence<OUString> & rSeq,const OUString & rText)59 static bool lcl_SeqHasString( const uno::Sequence< OUString > &rSeq, const OUString &rText )
60 {
61     return !rText.isEmpty()
62         && comphelper::findValue(rSeq, rText) != -1;
63 }
64 
65 
GetAvailLocales(const uno::Sequence<OUString> & rSvcImplNames)66 static uno::Sequence< lang::Locale > GetAvailLocales(
67         const uno::Sequence< OUString > &rSvcImplNames )
68 {
69     uno::Sequence< lang::Locale > aRes;
70 
71     uno::Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
72     if( rSvcImplNames.hasElements() )
73     {
74         std::set< LanguageType > aLanguages;
75 
76         // All of these services only use one arg, but need two args for compat reasons
77         uno::Sequence< uno::Any > aArgs(2);
78         aArgs.getArray()[0] <<= GetLinguProperties();
79 
80         // check all services for the supported languages and new
81         // languages to the result
82 
83         for (const OUString& rImplName : rSvcImplNames)
84         {
85             uno::Reference< linguistic2::XSupportedLocales > xSuppLoc;
86             try
87             {
88                 xSuppLoc.set( xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
89                                  rImplName, aArgs, xContext ),
90                               uno::UNO_QUERY );
91             }
92             catch (uno::Exception &)
93             {
94                 SAL_WARN( "linguistic", "createInstanceWithArguments failed" );
95             }
96 
97             if (xSuppLoc.is())
98             {
99                 const uno::Sequence< lang::Locale > aLoc( xSuppLoc->getLocales() );
100                 for (const lang::Locale& rLoc : aLoc)
101                 {
102                     LanguageType nLang = LinguLocaleToLanguage( rLoc );
103 
104                     // It's a set, so insertion fails if language was already added.
105                     aLanguages.insert( nLang );
106                 }
107             }
108             else
109             {
110                 SAL_WARN( "linguistic", "interface not supported by service" );
111             }
112         }
113 
114         // build return sequence
115         std::vector<lang::Locale> aVec;
116         aVec.reserve(aLanguages.size());
117 
118         std::transform(aLanguages.begin(), aLanguages.end(), std::back_inserter(aVec),
119             [](const LanguageType& rLang) -> lang::Locale { return LanguageTag::convertToLocale(rLang); });
120 
121         aRes = comphelper::containerToSequence(aVec);
122     }
123 
124     return aRes;
125 }
126 
127 
128 struct SvcInfo
129 {
130     const OUString                  aSvcImplName;
131     const std::vector< LanguageType >    aSuppLanguages;
132 
SvcInfoSvcInfo133     SvcInfo( const OUString &rSvcImplName,
134              const std::vector< LanguageType >  &rSuppLanguages ) :
135         aSvcImplName    (rSvcImplName),
136         aSuppLanguages  (rSuppLanguages)
137     {
138     }
139 
140     bool    HasLanguage( LanguageType nLanguage ) const;
141 };
142 
143 
HasLanguage(LanguageType nLanguage) const144 bool SvcInfo::HasLanguage( LanguageType nLanguage ) const
145 {
146     for ( auto const & i : aSuppLanguages)
147     {
148         if (nLanguage == i)
149             return true;
150     }
151     return false;
152 }
153 
154 class LngSvcMgrListenerHelper :
155     public cppu::WeakImplHelper
156     <
157         linguistic2::XLinguServiceEventListener,
158         linguistic2::XDictionaryListEventListener
159     >
160 {
161     LngSvcMgr  &rMyManager;
162 
163     ::comphelper::OInterfaceContainerHelper2           aLngSvcMgrListeners;
164     ::comphelper::OInterfaceContainerHelper2           aLngSvcEvtBroadcasters;
165     uno::Reference< linguistic2::XSearchableDictionaryList >           xDicList;
166 
167     sal_Int16   nCombinedLngSvcEvt;
168 
169     void    LaunchEvent( sal_Int16 nLngSvcEvtFlags );
170 
171     void Timeout();
172 
173 public:
174     LngSvcMgrListenerHelper( LngSvcMgr &rLngSvcMgr,
175         const uno::Reference< linguistic2::XSearchableDictionaryList > &rxDicList );
176 
177     LngSvcMgrListenerHelper(const LngSvcMgrListenerHelper&) = delete;
178     LngSvcMgrListenerHelper& operator=(const LngSvcMgrListenerHelper&) = delete;
179 
180     // lang::XEventListener
181     virtual void SAL_CALL
182         disposing( const lang::EventObject& rSource ) override;
183 
184     // linguistic2::XLinguServiceEventListener
185     virtual void SAL_CALL
186         processLinguServiceEvent( const linguistic2::LinguServiceEvent& aLngSvcEvent ) override;
187 
188     // linguistic2::XDictionaryListEventListener
189     virtual void SAL_CALL
190         processDictionaryListEvent(
191                 const linguistic2::DictionaryListEvent& rDicListEvent ) override;
192 
193     inline  void    AddLngSvcMgrListener(
194                         const uno::Reference< lang::XEventListener >& rxListener );
195     inline  void    RemoveLngSvcMgrListener(
196                         const uno::Reference< lang::XEventListener >& rxListener );
197     void    DisposeAndClear( const lang::EventObject &rEvtObj );
198     void    AddLngSvcEvtBroadcaster(
199                         const uno::Reference< linguistic2::XLinguServiceEventBroadcaster > &rxBroadcaster );
200     void    RemoveLngSvcEvtBroadcaster(
201                         const uno::Reference< linguistic2::XLinguServiceEventBroadcaster > &rxBroadcaster );
202 
203     void    AddLngSvcEvt( sal_Int16 nLngSvcEvt );
204 };
205 
206 
LngSvcMgrListenerHelper(LngSvcMgr & rLngSvcMgr,const uno::Reference<linguistic2::XSearchableDictionaryList> & rxDicList)207 LngSvcMgrListenerHelper::LngSvcMgrListenerHelper(
208         LngSvcMgr &rLngSvcMgr,
209         const uno::Reference< linguistic2::XSearchableDictionaryList > &rxDicList  ) :
210     rMyManager              ( rLngSvcMgr ),
211     aLngSvcMgrListeners     ( GetLinguMutex() ),
212     aLngSvcEvtBroadcasters  ( GetLinguMutex() ),
213     xDicList                ( rxDicList )
214 {
215     if (xDicList.is())
216     {
217         xDicList->addDictionaryListEventListener(
218             static_cast<linguistic2::XDictionaryListEventListener *>(this), false );
219     }
220 
221     nCombinedLngSvcEvt = 0;
222 }
223 
224 
disposing(const lang::EventObject & rSource)225 void SAL_CALL LngSvcMgrListenerHelper::disposing( const lang::EventObject& rSource )
226 {
227     osl::MutexGuard aGuard( GetLinguMutex() );
228 
229     uno::Reference< uno::XInterface > xRef( rSource.Source );
230     if ( xRef.is() )
231     {
232         aLngSvcMgrListeners   .removeInterface( xRef );
233         aLngSvcEvtBroadcasters.removeInterface( xRef );
234         if (xDicList == xRef)
235             xDicList = nullptr;
236     }
237 }
238 
Timeout()239 void LngSvcMgrListenerHelper::Timeout()
240 {
241     osl::MutexGuard aGuard( GetLinguMutex() );
242 
243     {
244         // change event source to LinguServiceManager since the listeners
245         // probably do not know (and need not to know) about the specific
246         // SpellChecker's or Hyphenator's.
247         linguistic2::LinguServiceEvent aEvtObj(
248             static_cast<css::linguistic2::XLinguServiceManager*>(&rMyManager), nCombinedLngSvcEvt );
249         nCombinedLngSvcEvt = 0;
250 
251         if (rMyManager.mxSpellDsp.is())
252             rMyManager.mxSpellDsp->FlushSpellCache();
253 
254         // pass event on to linguistic2::XLinguServiceEventListener's
255         aLngSvcMgrListeners.notifyEach( &linguistic2::XLinguServiceEventListener::processLinguServiceEvent, aEvtObj );
256     }
257 }
258 
259 
AddLngSvcEvt(sal_Int16 nLngSvcEvt)260 void LngSvcMgrListenerHelper::AddLngSvcEvt( sal_Int16 nLngSvcEvt )
261 {
262     nCombinedLngSvcEvt |= nLngSvcEvt;
263     Timeout();
264 }
265 
266 
267 void SAL_CALL
processLinguServiceEvent(const linguistic2::LinguServiceEvent & rLngSvcEvent)268     LngSvcMgrListenerHelper::processLinguServiceEvent(
269             const linguistic2::LinguServiceEvent& rLngSvcEvent )
270 {
271     osl::MutexGuard aGuard( GetLinguMutex() );
272     AddLngSvcEvt( rLngSvcEvent.nEvent );
273 }
274 
275 
276 void SAL_CALL
processDictionaryListEvent(const linguistic2::DictionaryListEvent & rDicListEvent)277     LngSvcMgrListenerHelper::processDictionaryListEvent(
278             const linguistic2::DictionaryListEvent& rDicListEvent )
279 {
280     osl::MutexGuard aGuard( GetLinguMutex() );
281 
282     sal_Int16 nDlEvt = rDicListEvent.nCondensedEvent;
283     if (0 == nDlEvt)
284         return;
285 
286     // we do keep the original event source here though...
287 
288     // pass event on to linguistic2::XDictionaryListEventListener's
289     aLngSvcMgrListeners.notifyEach( &linguistic2::XDictionaryListEventListener::processDictionaryListEvent, rDicListEvent );
290 
291     // "translate" DictionaryList event into linguistic2::LinguServiceEvent
292     sal_Int16 nLngSvcEvt = 0;
293     sal_Int16 const nSpellCorrectFlags =
294             linguistic2::DictionaryListEventFlags::ADD_NEG_ENTRY        |
295             linguistic2::DictionaryListEventFlags::DEL_POS_ENTRY        |
296             linguistic2::DictionaryListEventFlags::ACTIVATE_NEG_DIC |
297             linguistic2::DictionaryListEventFlags::DEACTIVATE_POS_DIC;
298     if (0 != (nDlEvt & nSpellCorrectFlags))
299         nLngSvcEvt |= linguistic2::LinguServiceEventFlags::SPELL_CORRECT_WORDS_AGAIN;
300 
301     sal_Int16 const nSpellWrongFlags =
302             linguistic2::DictionaryListEventFlags::ADD_POS_ENTRY        |
303             linguistic2::DictionaryListEventFlags::DEL_NEG_ENTRY        |
304             linguistic2::DictionaryListEventFlags::ACTIVATE_POS_DIC |
305             linguistic2::DictionaryListEventFlags::DEACTIVATE_NEG_DIC;
306     if (0 != (nDlEvt & nSpellWrongFlags))
307         nLngSvcEvt |= linguistic2::LinguServiceEventFlags::SPELL_WRONG_WORDS_AGAIN;
308 
309     sal_Int16 const nHyphenateFlags =
310             linguistic2::DictionaryListEventFlags::ADD_POS_ENTRY        |
311             linguistic2::DictionaryListEventFlags::DEL_POS_ENTRY        |
312             linguistic2::DictionaryListEventFlags::ACTIVATE_POS_DIC |
313             linguistic2::DictionaryListEventFlags::ACTIVATE_NEG_DIC;
314     if (0 != (nDlEvt & nHyphenateFlags))
315         nLngSvcEvt |= linguistic2::LinguServiceEventFlags::HYPHENATE_AGAIN;
316 
317     if (rMyManager.mxSpellDsp.is())
318         rMyManager.mxSpellDsp->FlushSpellCache();
319     if (nLngSvcEvt)
320         LaunchEvent( nLngSvcEvt );
321 }
322 
323 
LaunchEvent(sal_Int16 nLngSvcEvtFlags)324 void LngSvcMgrListenerHelper::LaunchEvent( sal_Int16 nLngSvcEvtFlags )
325 {
326     linguistic2::LinguServiceEvent aEvt(
327         static_cast<css::linguistic2::XLinguServiceManager*>(&rMyManager), nLngSvcEvtFlags );
328 
329     // pass event on to linguistic2::XLinguServiceEventListener's
330     aLngSvcMgrListeners.notifyEach( &linguistic2::XLinguServiceEventListener::processLinguServiceEvent, aEvt );
331 }
332 
333 
AddLngSvcMgrListener(const uno::Reference<lang::XEventListener> & rxListener)334 inline void LngSvcMgrListenerHelper::AddLngSvcMgrListener(
335         const uno::Reference< lang::XEventListener >& rxListener )
336 {
337     aLngSvcMgrListeners.addInterface( rxListener );
338 }
339 
340 
RemoveLngSvcMgrListener(const uno::Reference<lang::XEventListener> & rxListener)341 inline void LngSvcMgrListenerHelper::RemoveLngSvcMgrListener(
342         const uno::Reference< lang::XEventListener >& rxListener )
343 {
344     aLngSvcMgrListeners.removeInterface( rxListener );
345 }
346 
347 
DisposeAndClear(const lang::EventObject & rEvtObj)348 void LngSvcMgrListenerHelper::DisposeAndClear( const lang::EventObject &rEvtObj )
349 {
350     // call "disposing" for all listeners and clear list
351     aLngSvcMgrListeners   .disposeAndClear( rEvtObj );
352 
353     // remove references to this object hold by the broadcasters
354     comphelper::OInterfaceIteratorHelper2 aIt( aLngSvcEvtBroadcasters );
355     while (aIt.hasMoreElements())
356     {
357         uno::Reference< linguistic2::XLinguServiceEventBroadcaster > xRef( aIt.next(), uno::UNO_QUERY );
358         if (xRef.is())
359             RemoveLngSvcEvtBroadcaster( xRef );
360     }
361 
362     // remove reference to this object hold by the dictionary-list
363     if (xDicList.is())
364     {
365         xDicList->removeDictionaryListEventListener(
366             static_cast<linguistic2::XDictionaryListEventListener *>(this) );
367         xDicList = nullptr;
368     }
369 }
370 
371 
AddLngSvcEvtBroadcaster(const uno::Reference<linguistic2::XLinguServiceEventBroadcaster> & rxBroadcaster)372 void LngSvcMgrListenerHelper::AddLngSvcEvtBroadcaster(
373         const uno::Reference< linguistic2::XLinguServiceEventBroadcaster > &rxBroadcaster )
374 {
375     if (rxBroadcaster.is())
376     {
377         aLngSvcEvtBroadcasters.addInterface( rxBroadcaster );
378         rxBroadcaster->addLinguServiceEventListener(
379                 static_cast<linguistic2::XLinguServiceEventListener *>(this) );
380     }
381 }
382 
383 
RemoveLngSvcEvtBroadcaster(const uno::Reference<linguistic2::XLinguServiceEventBroadcaster> & rxBroadcaster)384 void LngSvcMgrListenerHelper::RemoveLngSvcEvtBroadcaster(
385         const uno::Reference< linguistic2::XLinguServiceEventBroadcaster > &rxBroadcaster )
386 {
387     if (rxBroadcaster.is())
388     {
389         aLngSvcEvtBroadcasters.removeInterface( rxBroadcaster );
390         rxBroadcaster->removeLinguServiceEventListener(
391                 static_cast<linguistic2::XLinguServiceEventListener *>(this) );
392     }
393 }
394 
395 
LngSvcMgr()396 LngSvcMgr::LngSvcMgr()
397     : utl::ConfigItem("Office.Linguistic")
398     , aEvtListeners(GetLinguMutex())
399 {
400     bDisposing = false;
401 
402     // request notify events when properties (i.e. something in the subtree) changes
403     uno::Sequence< OUString > aNames{
404         "ServiceManager/SpellCheckerList",
405         "ServiceManager/GrammarCheckerList",
406         "ServiceManager/HyphenatorList",
407         "ServiceManager/ThesaurusList"
408     };
409     EnableNotification( aNames );
410 
411     UpdateAll();
412 
413     aUpdateIdle.SetPriority(TaskPriority::LOWEST);
414     aUpdateIdle.SetInvokeHandler(LINK(this, LngSvcMgr, updateAndBroadcast));
415 
416     // request to be notified if an extension has been added/removed
417     uno::Reference<uno::XComponentContext> xContext(comphelper::getProcessComponentContext());
418 
419     uno::Reference<deployment::XExtensionManager> xExtensionManager;
420     try {
421         xExtensionManager = deployment::ExtensionManager::get(xContext);
422     } catch ( const uno::DeploymentException & ) {
423         SAL_WARN( "linguistic", "no extension manager - should fire on mobile only" );
424     } catch ( const deployment::DeploymentException & ) {
425         SAL_WARN( "linguistic", "no extension manager - should fire on mobile only" );
426     }
427     if (xExtensionManager.is())
428     {
429         xMB.set(xExtensionManager, uno::UNO_QUERY_THROW);
430 
431         uno::Reference<util::XModifyListener> xListener(this);
432         xMB->addModifyListener( xListener );
433     }
434 }
435 
436 // css::util::XModifyListener
modified(const lang::EventObject &)437 void LngSvcMgr::modified(const lang::EventObject&)
438 {
439     {
440         osl::MutexGuard aGuard(GetLinguMutex());
441         //assume that if an extension has been added/removed that
442         //it might be a dictionary extension, so drop our cache
443 
444         pAvailSpellSvcs.reset();
445         pAvailGrammarSvcs.reset();
446         pAvailHyphSvcs.reset();
447         pAvailThesSvcs.reset();
448     }
449 
450     {
451         SolarMutexGuard aGuard;
452         //schedule in an update to execute in the main thread
453         aUpdateIdle.Start();
454     }
455 }
456 
457 //run update, and inform everyone that dictionaries (may) have changed, this
458 //needs to be run in the main thread because
459 //utl::ConfigChangeListener_Impl::changesOccurred grabs the SolarMutex and we
460 //get notified that an extension was added from an extension manager thread
IMPL_LINK_NOARG(LngSvcMgr,updateAndBroadcast,Timer *,void)461 IMPL_LINK_NOARG(LngSvcMgr, updateAndBroadcast, Timer *, void)
462 {
463     osl::MutexGuard aGuard( GetLinguMutex() );
464 
465     UpdateAll();
466 
467     if (mxListenerHelper.is())
468     {
469         mxListenerHelper->AddLngSvcEvt(
470                 linguistic2::LinguServiceEventFlags::SPELL_CORRECT_WORDS_AGAIN |
471                 linguistic2::LinguServiceEventFlags::SPELL_WRONG_WORDS_AGAIN |
472                 linguistic2::LinguServiceEventFlags::PROOFREAD_AGAIN |
473                 linguistic2::LinguServiceEventFlags::HYPHENATE_AGAIN );
474     }
475 }
476 
stopListening()477 void LngSvcMgr::stopListening()
478 {
479     osl::MutexGuard aGuard(GetLinguMutex());
480 
481     if (!xMB.is())
482         return;
483 
484     try
485     {
486             uno::Reference<util::XModifyListener>  xListener(this);
487             xMB->removeModifyListener(xListener);
488     }
489     catch (const uno::Exception&)
490     {
491     }
492 
493     xMB.clear();
494 }
495 
disposing(const lang::EventObject &)496 void LngSvcMgr::disposing(const lang::EventObject&)
497 {
498     stopListening();
499 }
500 
~LngSvcMgr()501 LngSvcMgr::~LngSvcMgr()
502 {
503     stopListening();
504 
505     // memory for pSpellDsp, pHyphDsp, pThesDsp, pListenerHelper
506     // will be freed in the destructor of the respective Reference's
507     // xSpellDsp, xGrammarDsp, xHyphDsp, xThesDsp
508 
509     pAvailSpellSvcs.reset();
510     pAvailGrammarSvcs.reset();
511     pAvailHyphSvcs.reset();
512     pAvailThesSvcs.reset();
513 }
514 
515 namespace
516 {
517     using lang::Locale;
518     using uno::Any;
519     using uno::Sequence;
520 
lcl_FindEntry(const OUString & rEntry,const Sequence<OUString> & rCfgSvcs)521     bool lcl_FindEntry( const OUString &rEntry, const Sequence< OUString > &rCfgSvcs )
522     {
523         return comphelper::findValue(rCfgSvcs, rEntry) != -1;
524     }
525 
lcl_FindEntry(const OUString & rEntry,const std::vector<OUString> & rCfgSvcs)526     bool lcl_FindEntry( const OUString &rEntry, const std::vector< OUString > &rCfgSvcs )
527     {
528         return std::find(rCfgSvcs.begin(), rCfgSvcs.end(), rEntry) != rCfgSvcs.end();
529     }
530 
lcl_GetLastFoundSvcs(SvtLinguConfig const & rCfg,const OUString & rLastFoundList,const OUString & rCfgLocaleStr)531     Sequence< OUString > lcl_GetLastFoundSvcs(
532             SvtLinguConfig const &rCfg,
533             const OUString &rLastFoundList ,
534             const OUString& rCfgLocaleStr )
535     {
536         Sequence< OUString > aRes;
537 
538         Sequence< OUString > aNodeNames( rCfg.GetNodeNames(rLastFoundList) );
539         bool bFound = lcl_FindEntry( rCfgLocaleStr, aNodeNames);
540 
541         if (bFound)
542         {
543             Sequence< OUString > aNames { rLastFoundList + "/" + rCfgLocaleStr };
544             Sequence< Any > aValues( rCfg.GetProperties( aNames ) );
545             if (aValues.hasElements())
546             {
547                 SAL_WARN_IF( aValues.getLength() != 1, "linguistic", "unexpected length of sequence" );
548                 Sequence< OUString > aSvcImplNames;
549                 if (aValues.getConstArray()[0] >>= aSvcImplNames)
550                     aRes = aSvcImplNames;
551                 else
552                 {
553                     SAL_WARN( "linguistic", "type mismatch" );
554                 }
555             }
556         }
557 
558         return aRes;
559     }
560 
lcl_RemoveMissingEntries(const Sequence<OUString> & rCfgSvcs,const Sequence<OUString> & rAvailSvcs)561     Sequence< OUString > lcl_RemoveMissingEntries(
562             const Sequence< OUString > &rCfgSvcs,
563             const Sequence< OUString > &rAvailSvcs )
564     {
565         std::vector<OUString> aRes;
566         aRes.reserve(rCfgSvcs.getLength());
567 
568         std::copy_if(rCfgSvcs.begin(), rCfgSvcs.end(), std::back_inserter(aRes),
569             [&rAvailSvcs](const OUString& entry) { return lcl_SeqHasString(rAvailSvcs, entry); });
570 
571         return comphelper::containerToSequence(aRes);
572     }
573 
lcl_GetNewEntries(const Sequence<OUString> & rLastFoundSvcs,const Sequence<OUString> & rAvailSvcs)574     Sequence< OUString > lcl_GetNewEntries(
575             const Sequence< OUString > &rLastFoundSvcs,
576             const Sequence< OUString > &rAvailSvcs )
577     {
578         std::vector<OUString> aRes;
579         aRes.reserve(rAvailSvcs.getLength());
580 
581         std::copy_if(rAvailSvcs.begin(), rAvailSvcs.end(), std::back_inserter(aRes),
582             [&rLastFoundSvcs](const OUString& rEntry) {
583                 return !rEntry.isEmpty() && !lcl_FindEntry( rEntry, rLastFoundSvcs ); });
584 
585         return comphelper::containerToSequence(aRes);
586     }
587 
lcl_MergeSeq(const Sequence<OUString> & rCfgSvcs,const Sequence<OUString> & rNewSvcs)588     Sequence< OUString > lcl_MergeSeq(
589             const Sequence< OUString > &rCfgSvcs,
590             const Sequence< OUString > &rNewSvcs )
591     {
592         std::vector<OUString> aRes;
593         aRes.reserve(rCfgSvcs.getLength() + rNewSvcs.getLength());
594 
595         auto lVecNotHasString = [&aRes](const OUString& rEntry)
596             { return !rEntry.isEmpty() && !lcl_FindEntry(rEntry, aRes); };
597 
598         // add previously configured service first and append
599         // new found services at the end
600         for (const Sequence< OUString > &rSeq : { rCfgSvcs, rNewSvcs })
601         {
602             std::copy_if(rSeq.begin(), rSeq.end(), std::back_inserter(aRes), lVecNotHasString);
603         }
604 
605         return comphelper::containerToSequence(aRes);
606     }
607 }
608 
UpdateAll()609 void LngSvcMgr::UpdateAll()
610 {
611     using beans::PropertyValue;
612     using lang::Locale;
613     using uno::Sequence;
614 
615     typedef std::map< OUString, Sequence< OUString > > list_entry_map_t;
616 
617     SvtLinguConfig aCfg;
618 
619     const int nNumServices = 4;
620     const sal_Unicode * const apServices[nNumServices]       =  { SN_SPELLCHECKER, SN_GRAMMARCHECKER, SN_HYPHENATOR, SN_THESAURUS };
621     const char * const apCurLists[nNumServices]       =  { "ServiceManager/SpellCheckerList",       "ServiceManager/GrammarCheckerList",       "ServiceManager/HyphenatorList",       "ServiceManager/ThesaurusList" };
622     const char * const apLastFoundLists[nNumServices] =  { "ServiceManager/LastFoundSpellCheckers", "ServiceManager/LastFoundGrammarCheckers", "ServiceManager/LastFoundHyphenators", "ServiceManager/LastFoundThesauri" };
623 
624     // usage of indices as above: 0 = spell checker, 1 = grammar checker, 2 = hyphenator, 3 = thesaurus
625     std::vector< list_entry_map_t > aLastFoundSvcs(nNumServices);
626     std::vector< list_entry_map_t > aCurSvcs(nNumServices);
627 
628     for (int k = 0;  k < nNumServices;  ++k)
629     {
630         OUString aService( apServices[k] );
631         OUString aActiveList( OUString::createFromAscii( apCurLists[k] ) );
632         OUString aLastFoundList( OUString::createFromAscii( apLastFoundLists[k] ) );
633 
634 
635         // remove configured but not available language/services entries
636 
637         const Sequence< OUString > aNodeNames( aCfg.GetNodeNames( aActiveList ) );   // list of configured locales
638         for (const OUString& rNodeName : aNodeNames)
639         {
640             Locale aLocale( LanguageTag::convertToLocale( rNodeName));
641             Sequence< OUString > aCfgSvcs( getConfiguredServices( aService, aLocale ));
642             Sequence< OUString > aAvailSvcs( getAvailableServices( aService, aLocale ));
643 
644             aCfgSvcs = lcl_RemoveMissingEntries( aCfgSvcs, aAvailSvcs );
645 
646             aCurSvcs[k][ rNodeName ] = aCfgSvcs;
647         }
648 
649 
650         // add new available language/service entries
651         // and
652         // set last found services to currently available ones
653 
654         const Sequence< Locale > aAvailLocales( getAvailableLocales(aService) );
655         for (const Locale& rAvailLocale : aAvailLocales)
656         {
657             OUString aCfgLocaleStr( LanguageTag::convertToBcp47( rAvailLocale));
658 
659             Sequence< OUString > aAvailSvcs( getAvailableServices( aService, rAvailLocale ));
660 
661             aLastFoundSvcs[k][ aCfgLocaleStr ] = aAvailSvcs;
662 
663             Sequence< OUString > aLastSvcs(
664                     lcl_GetLastFoundSvcs( aCfg, aLastFoundList , aCfgLocaleStr ));
665             Sequence< OUString > aNewSvcs =
666                     lcl_GetNewEntries( aLastSvcs, aAvailSvcs );
667 
668             Sequence< OUString > aCfgSvcs( aCurSvcs[k][ aCfgLocaleStr ] );
669 
670             // merge services list (previously configured to be listed first).
671             aCfgSvcs = lcl_MergeSeq( aCfgSvcs, aNewSvcs );
672 
673             aCurSvcs[k][ aCfgLocaleStr ] = aCfgSvcs;
674         }
675     }
676 
677 
678     // write new data back to configuration
679 
680     for (int k = 0;  k < nNumServices;  ++k)
681     {
682         for (int i = 0;  i < 2;  ++i)
683         {
684             const char *pSubNodeName = (i == 0) ? apCurLists[k] : apLastFoundLists[k];
685             OUString aSubNodeName( OUString::createFromAscii(pSubNodeName) );
686 
687             list_entry_map_t &rCurMap = (i == 0) ? aCurSvcs[k] : aLastFoundSvcs[k];
688             sal_Int32 nVals = static_cast< sal_Int32 >( rCurMap.size() );
689             Sequence< PropertyValue > aNewValues( nVals );
690             PropertyValue *pNewValue = aNewValues.getArray();
691             for (auto const& elem : rCurMap)
692             {
693                 pNewValue->Name = aSubNodeName + "/" + elem.first;
694                 pNewValue->Value <<= elem.second;
695                 ++pNewValue;
696             }
697             OSL_ENSURE( pNewValue - aNewValues.getConstArray() == nVals,
698                     "possible mismatch of sequence size and property number" );
699 
700             {
701                 // add new or replace existing entries.
702                 bool bRes = aCfg.ReplaceSetProperties( aSubNodeName, aNewValues );
703                 SAL_WARN_IF(!bRes, "linguistic", "failed to set new configuration values");
704             }
705         }
706     }
707 
708     //The new settings in the configuration get applied ! because we are
709     //listening to the configuration for changes of the relevant ! properties
710     //and Notify applies the new settings.
711 }
712 
Notify(const uno::Sequence<OUString> & rPropertyNames)713 void LngSvcMgr::Notify( const uno::Sequence< OUString > &rPropertyNames )
714 {
715     static const OUStringLiteral aSpellCheckerList( u"ServiceManager/SpellCheckerList" );
716     static const OUStringLiteral aGrammarCheckerList( u"ServiceManager/GrammarCheckerList" );
717     static const OUStringLiteral aHyphenatorList( u"ServiceManager/HyphenatorList" );
718     static const OUStringLiteral aThesaurusList( u"ServiceManager/ThesaurusList" );
719 
720     const uno::Sequence< OUString > aSpellCheckerListEntries( GetNodeNames( aSpellCheckerList ) );
721     const uno::Sequence< OUString > aGrammarCheckerListEntries( GetNodeNames( aGrammarCheckerList ) );
722     const uno::Sequence< OUString > aHyphenatorListEntries( GetNodeNames( aHyphenatorList ) );
723     const uno::Sequence< OUString > aThesaurusListEntries( GetNodeNames( aThesaurusList ) );
724 
725     uno::Sequence< uno::Any > aValues;
726     uno::Sequence< OUString > aNames( 1 );
727     OUString *pNames = aNames.getArray();
728 
729     for (const OUString& rName : rPropertyNames)
730     {
731         // property names look like
732         // "ServiceManager/ThesaurusList/de-CH"
733 
734         sal_Int32 nKeyStart;
735         nKeyStart = rName.lastIndexOf( '/' );
736         OUString aKeyText;
737         if (nKeyStart != -1)
738             aKeyText = rName.copy( nKeyStart + 1 );
739         SAL_WARN_IF( aKeyText.isEmpty(), "linguistic", "unexpected key (lang::Locale) string" );
740         if (rName.startsWith( aSpellCheckerList ))
741         {
742             osl::MutexGuard aGuard(GetLinguMutex());
743 
744             // delete old cached data, needs to be acquired new on demand
745             pAvailSpellSvcs.reset();
746 
747             if (lcl_SeqHasString( aSpellCheckerListEntries, aKeyText ))
748             {
749                 pNames[0] = aSpellCheckerList + "/" + aKeyText;
750                 aValues = /*aCfg.*/GetProperties( aNames );
751                 uno::Sequence< OUString > aSvcImplNames;
752                 if (aValues.hasElements())
753                     aSvcImplNames = GetLangSvcList( aValues.getConstArray()[0] );
754 
755                 LanguageType nLang = LANGUAGE_NONE;
756                 if (!aKeyText.isEmpty())
757                     nLang = LanguageTag::convertToLanguageType( aKeyText );
758 
759                 GetSpellCheckerDsp_Impl( false );     // don't set service list, it will be done below
760                 mxSpellDsp->SetServiceList( LanguageTag::convertToLocale(nLang), aSvcImplNames );
761             }
762         }
763         else if (rName.startsWith( aGrammarCheckerList ))
764         {
765             osl::MutexGuard aGuard(GetLinguMutex());
766 
767             // delete old cached data, needs to be acquired new on demand
768             pAvailGrammarSvcs.reset();
769 
770             if (lcl_SeqHasString( aGrammarCheckerListEntries, aKeyText ))
771             {
772                 pNames[0] = aGrammarCheckerList + "/" + aKeyText;
773                 aValues = /*aCfg.*/GetProperties( aNames );
774                 uno::Sequence< OUString > aSvcImplNames;
775                 if (aValues.hasElements())
776                     aSvcImplNames = GetLangSvc( aValues.getConstArray()[0] );
777 
778                 LanguageType nLang = LANGUAGE_NONE;
779                 if (!aKeyText.isEmpty())
780                     nLang = LanguageTag::convertToLanguageType( aKeyText );
781 
782                 if (SvtLinguConfig().HasGrammarChecker())
783                 {
784                     GetGrammarCheckerDsp_Impl( false );   // don't set service list, it will be done below
785                     mxGrammarDsp->SetServiceList( LanguageTag::convertToLocale(nLang), aSvcImplNames );
786                 }
787             }
788         }
789         else if (rName.startsWith( aHyphenatorList ))
790         {
791             osl::MutexGuard aGuard(GetLinguMutex());
792 
793             // delete old cached data, needs to be acquired new on demand
794             pAvailHyphSvcs.reset();
795 
796             if (lcl_SeqHasString( aHyphenatorListEntries, aKeyText ))
797             {
798                 pNames[0] = aHyphenatorList + "/" + aKeyText;
799                 aValues = /*aCfg.*/GetProperties( aNames );
800                 uno::Sequence< OUString > aSvcImplNames;
801                 if (aValues.hasElements())
802                     aSvcImplNames = GetLangSvc( aValues.getConstArray()[0] );
803 
804                 LanguageType nLang = LANGUAGE_NONE;
805                 if (!aKeyText.isEmpty())
806                     nLang = LanguageTag::convertToLanguageType( aKeyText );
807 
808                 GetHyphenatorDsp_Impl( false );   // don't set service list, it will be done below
809                 mxHyphDsp->SetServiceList( LanguageTag::convertToLocale(nLang), aSvcImplNames );
810             }
811         }
812         else if (rName.startsWith( aThesaurusList ))
813         {
814             osl::MutexGuard aGuard(GetLinguMutex());
815 
816             // delete old cached data, needs to be acquired new on demand
817             pAvailThesSvcs.reset();
818 
819             if (lcl_SeqHasString( aThesaurusListEntries, aKeyText ))
820             {
821                 pNames[0] = aThesaurusList + "/" + aKeyText;
822                 aValues = /*aCfg.*/GetProperties( aNames );
823                 uno::Sequence< OUString > aSvcImplNames;
824                 if (aValues.hasElements())
825                     aSvcImplNames = GetLangSvcList( aValues.getConstArray()[0] );
826 
827                 LanguageType nLang = LANGUAGE_NONE;
828                 if (!aKeyText.isEmpty())
829                     nLang = LanguageTag::convertToLanguageType( aKeyText );
830 
831                 GetThesaurusDsp_Impl( false );  // don't set service list, it will be done below
832                 mxThesDsp->SetServiceList( LanguageTag::convertToLocale(nLang), aSvcImplNames );
833             }
834         }
835         else
836         {
837             SAL_WARN( "linguistic", "notified for unexpected property" );
838         }
839     }
840 }
841 
842 
ImplCommit()843 void LngSvcMgr::ImplCommit()
844 {
845     // everything necessary should have already been done by 'SaveCfgSvcs'
846     // called from within 'setConfiguredServices'.
847     // Also this class usually exits only when the Office is being shutdown.
848 }
849 
850 
GetListenerHelper_Impl()851 void LngSvcMgr::GetListenerHelper_Impl()
852 {
853     if (!mxListenerHelper.is())
854     {
855         mxListenerHelper = new LngSvcMgrListenerHelper( *this, linguistic::GetDictionaryList() );
856     }
857 }
858 
859 
GetSpellCheckerDsp_Impl(bool bSetSvcList)860 void LngSvcMgr::GetSpellCheckerDsp_Impl( bool bSetSvcList )
861 {
862     if (!mxSpellDsp.is())
863     {
864         mxSpellDsp = new SpellCheckerDispatcher( *this );
865         if (bSetSvcList)
866             SetCfgServiceLists( *mxSpellDsp );
867     }
868 }
869 
870 
GetGrammarCheckerDsp_Impl(bool bSetSvcList)871 void LngSvcMgr::GetGrammarCheckerDsp_Impl( bool bSetSvcList  )
872 {
873     if (mxGrammarDsp.is() || !SvtLinguConfig().HasGrammarChecker())
874         return;
875 
876     //! since the grammar checking iterator needs to be a one instance service
877     //! we need to create it the correct way!
878     uno::Reference< linguistic2::XProofreadingIterator > xGCI;
879     try
880     {
881         xGCI = linguistic2::ProofreadingIterator::create( comphelper::getProcessComponentContext() );
882     }
883     catch (const uno::Exception &)
884     {
885     }
886     SAL_WARN_IF( !xGCI.is(), "linguistic", "instantiating grammar checking iterator failed" );
887 
888     if (xGCI.is())
889     {
890         mxGrammarDsp = dynamic_cast< GrammarCheckingIterator * >(xGCI.get());
891         SAL_WARN_IF( mxGrammarDsp == nullptr, "linguistic", "failed to get implementation" );
892         if (bSetSvcList && mxGrammarDsp.is())
893             SetCfgServiceLists( *mxGrammarDsp );
894     }
895 }
896 
897 
GetHyphenatorDsp_Impl(bool bSetSvcList)898 void LngSvcMgr::GetHyphenatorDsp_Impl( bool bSetSvcList  )
899 {
900     if (!mxHyphDsp.is())
901     {
902         mxHyphDsp = new HyphenatorDispatcher( *this );
903         if (bSetSvcList)
904             SetCfgServiceLists( *mxHyphDsp );
905     }
906 }
907 
908 
GetThesaurusDsp_Impl(bool bSetSvcList)909 void LngSvcMgr::GetThesaurusDsp_Impl( bool bSetSvcList  )
910 {
911     if (!mxThesDsp.is())
912     {
913         mxThesDsp = new ThesaurusDispatcher;
914         if (bSetSvcList)
915             SetCfgServiceLists( *mxThesDsp );
916     }
917 }
918 
919 
GetAvailableSpellSvcs_Impl()920 void LngSvcMgr::GetAvailableSpellSvcs_Impl()
921 {
922     if (pAvailSpellSvcs)
923         return;
924 
925     pAvailSpellSvcs.reset(new SvcInfoArray);
926 
927     uno::Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
928 
929     uno::Reference< container::XContentEnumerationAccess > xEnumAccess( xContext->getServiceManager(), uno::UNO_QUERY );
930     uno::Reference< container::XEnumeration > xEnum;
931     if (xEnumAccess.is())
932         xEnum = xEnumAccess->createContentEnumeration( SN_SPELLCHECKER );
933 
934     if (!xEnum.is())
935         return;
936 
937     while (xEnum->hasMoreElements())
938     {
939         uno::Any aCurrent = xEnum->nextElement();
940         uno::Reference< lang::XSingleComponentFactory > xCompFactory;
941         uno::Reference< lang::XSingleServiceFactory > xFactory;
942 
943         uno::Reference< linguistic2::XSpellChecker > xSvc;
944         xCompFactory.set(aCurrent, css::uno::UNO_QUERY);
945         if (!xCompFactory.is())
946         {
947             xFactory.set(aCurrent, css::uno::UNO_QUERY);
948         }
949         if ( xCompFactory.is() || xFactory.is() )
950         {
951             try
952             {
953                 xSvc.set( ( xCompFactory.is() ? xCompFactory->createInstanceWithContext( xContext ) : xFactory->createInstance() ), uno::UNO_QUERY );
954             }
955             catch (const uno::Exception &)
956             {
957                 SAL_WARN( "linguistic", "createInstance failed" );
958             }
959         }
960 
961         if (xSvc.is())
962         {
963             OUString            aImplName;
964             std::vector< LanguageType >   aLanguages;
965             uno::Reference< XServiceInfo > xInfo( xSvc, uno::UNO_QUERY );
966             if (xInfo.is())
967                 aImplName = xInfo->getImplementationName();
968             SAL_WARN_IF( aImplName.isEmpty(), "linguistic", "empty implementation name" );
969             uno::Sequence<lang::Locale> aLocaleSequence(xSvc->getLocales());
970             aLanguages = LocaleSeqToLangVec( aLocaleSequence );
971 
972             pAvailSpellSvcs->push_back( std::make_unique<SvcInfo>( aImplName, aLanguages ) );
973         }
974     }
975 }
976 
977 
GetAvailableGrammarSvcs_Impl()978 void LngSvcMgr::GetAvailableGrammarSvcs_Impl()
979 {
980     if (pAvailGrammarSvcs)
981         return;
982 
983     pAvailGrammarSvcs.reset(new SvcInfoArray);
984 
985     uno::Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
986 
987     uno::Reference< container::XContentEnumerationAccess > xEnumAccess( xContext->getServiceManager(), uno::UNO_QUERY );
988     uno::Reference< container::XEnumeration > xEnum;
989     if (xEnumAccess.is())
990         xEnum = xEnumAccess->createContentEnumeration( SN_GRAMMARCHECKER );
991 
992     if (!xEnum.is())
993         return;
994 
995     while (xEnum->hasMoreElements())
996     {
997         uno::Any aCurrent = xEnum->nextElement();
998         uno::Reference< lang::XSingleComponentFactory > xCompFactory;
999         uno::Reference< lang::XSingleServiceFactory > xFactory;
1000 
1001         uno::Reference< linguistic2::XProofreader > xSvc;
1002         xCompFactory.set(aCurrent, css::uno::UNO_QUERY);
1003         if (!xCompFactory.is())
1004         {
1005             xFactory.set(aCurrent, css::uno::UNO_QUERY);
1006         }
1007         if ( xCompFactory.is() || xFactory.is() )
1008         {
1009             try
1010             {
1011                 if (xCompFactory.is())
1012                 {
1013                     xSvc.set(xCompFactory->createInstanceWithContext(xContext), uno::UNO_QUERY);
1014                 }
1015                 else
1016                 {
1017                     xSvc.set(xFactory->createInstance(), uno::UNO_QUERY);
1018                 }
1019             }
1020             catch (const uno::Exception &)
1021             {
1022                 SAL_WARN( "linguistic", "createInstance failed" );
1023             }
1024         }
1025 
1026         if (xSvc.is() && pAvailGrammarSvcs)
1027         {
1028             OUString            aImplName;
1029             std::vector< LanguageType >    aLanguages;
1030             uno::Reference< XServiceInfo > xInfo( xSvc, uno::UNO_QUERY );
1031             if (xInfo.is())
1032                 aImplName = xInfo->getImplementationName();
1033             SAL_WARN_IF( aImplName.isEmpty(), "linguistic", "empty implementation name" );
1034             uno::Sequence<lang::Locale> aLocaleSequence(xSvc->getLocales());
1035             aLanguages = LocaleSeqToLangVec( aLocaleSequence );
1036 
1037             pAvailGrammarSvcs->push_back( std::make_unique<SvcInfo>( aImplName, aLanguages ) );
1038         }
1039     }
1040 }
1041 
1042 
GetAvailableHyphSvcs_Impl()1043 void LngSvcMgr::GetAvailableHyphSvcs_Impl()
1044 {
1045     if (pAvailHyphSvcs)
1046         return;
1047 
1048     pAvailHyphSvcs.reset(new SvcInfoArray);
1049     uno::Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
1050 
1051     uno::Reference< container::XContentEnumerationAccess > xEnumAccess( xContext->getServiceManager(), uno::UNO_QUERY );
1052     uno::Reference< container::XEnumeration > xEnum;
1053     if (xEnumAccess.is())
1054         xEnum = xEnumAccess->createContentEnumeration( SN_HYPHENATOR );
1055 
1056     if (!xEnum.is())
1057         return;
1058 
1059     while (xEnum->hasMoreElements())
1060     {
1061         uno::Any aCurrent = xEnum->nextElement();
1062         uno::Reference< lang::XSingleComponentFactory > xCompFactory;
1063         uno::Reference< lang::XSingleServiceFactory > xFactory;
1064 
1065         uno::Reference< linguistic2::XHyphenator > xSvc;
1066         xCompFactory.set(aCurrent, css::uno::UNO_QUERY);
1067         if (!xCompFactory.is())
1068         {
1069             xFactory.set(aCurrent, css::uno::UNO_QUERY);
1070         }
1071         if ( xCompFactory.is() || xFactory.is() )
1072         {
1073             try
1074             {
1075                 xSvc.set( ( xCompFactory.is() ? xCompFactory->createInstanceWithContext( xContext ) : xFactory->createInstance() ), uno::UNO_QUERY );
1076             }
1077             catch (const uno::Exception &)
1078             {
1079                 SAL_WARN( "linguistic", "createInstance failed" );
1080             }
1081         }
1082         if (xSvc.is())
1083         {
1084             OUString            aImplName;
1085             std::vector< LanguageType >    aLanguages;
1086             uno::Reference< XServiceInfo > xInfo( xSvc, uno::UNO_QUERY );
1087             if (xInfo.is())
1088                 aImplName = xInfo->getImplementationName();
1089             SAL_WARN_IF( aImplName.isEmpty(), "linguistic", "empty implementation name" );
1090             uno::Sequence<lang::Locale> aLocaleSequence(xSvc->getLocales());
1091             aLanguages = LocaleSeqToLangVec( aLocaleSequence );
1092             pAvailHyphSvcs->push_back( std::make_unique<SvcInfo>( aImplName, aLanguages ) );
1093         }
1094     }
1095 }
1096 
1097 
GetAvailableThesSvcs_Impl()1098 void LngSvcMgr::GetAvailableThesSvcs_Impl()
1099 {
1100     if (pAvailThesSvcs)
1101         return;
1102 
1103     pAvailThesSvcs.reset(new SvcInfoArray);
1104 
1105     uno::Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
1106 
1107     uno::Reference< container::XContentEnumerationAccess > xEnumAccess( xContext->getServiceManager(), uno::UNO_QUERY );
1108     uno::Reference< container::XEnumeration > xEnum;
1109     if (xEnumAccess.is())
1110         xEnum = xEnumAccess->createContentEnumeration( SN_THESAURUS );
1111 
1112     if (!xEnum.is())
1113         return;
1114 
1115     while (xEnum->hasMoreElements())
1116     {
1117         uno::Any aCurrent = xEnum->nextElement();
1118         uno::Reference< lang::XSingleComponentFactory > xCompFactory;
1119         uno::Reference< lang::XSingleServiceFactory > xFactory;
1120 
1121         uno::Reference< linguistic2::XThesaurus > xSvc;
1122         xCompFactory.set(aCurrent, css::uno::UNO_QUERY);
1123         if (!xCompFactory.is())
1124         {
1125             xFactory.set(aCurrent, css::uno::UNO_QUERY);
1126         }
1127         if ( xCompFactory.is() || xFactory.is() )
1128         {
1129             try
1130             {
1131                 xSvc.set( ( xCompFactory.is() ? xCompFactory->createInstanceWithContext( xContext ) : xFactory->createInstance() ), uno::UNO_QUERY );
1132             }
1133             catch (const uno::Exception &)
1134             {
1135                SAL_WARN( "linguistic", "createInstance failed" );
1136             }
1137         }
1138         if (xSvc.is())
1139         {
1140             OUString            aImplName;
1141             std::vector< LanguageType >    aLanguages;
1142             uno::Reference< XServiceInfo > xInfo( xSvc, uno::UNO_QUERY );
1143             if (xInfo.is())
1144                 aImplName = xInfo->getImplementationName();
1145             SAL_WARN_IF( aImplName.isEmpty(), "linguistic", "empty implementation name" );
1146             uno::Sequence<lang::Locale> aLocaleSequence(xSvc->getLocales());
1147             aLanguages = LocaleSeqToLangVec( aLocaleSequence );
1148 
1149             pAvailThesSvcs->push_back( std::make_unique<SvcInfo>( aImplName, aLanguages ) );
1150         }
1151     }
1152 }
1153 
1154 
SetCfgServiceLists(SpellCheckerDispatcher & rSpellDsp)1155 void LngSvcMgr::SetCfgServiceLists( SpellCheckerDispatcher &rSpellDsp )
1156 {
1157     SAL_INFO( "linguistic", "linguistic: LngSvcMgr::SetCfgServiceLists - Spell" );
1158 
1159     OUString aNode("ServiceManager/SpellCheckerList");
1160     uno::Sequence< OUString > aNames( /*aCfg.*/GetNodeNames( aNode ) );
1161 
1162     // append path prefix need for 'GetProperties' call below
1163     OUString aPrefix = aNode + "/";
1164     for (OUString & name : aNames)
1165     {
1166         name = aPrefix + name;
1167     }
1168 
1169     const uno::Sequence< uno::Any > aValues( /*aCfg.*/GetProperties( aNames ) );
1170     if (!(aNames.hasElements()  &&  aNames.getLength() == aValues.getLength()))
1171         return;
1172 
1173     const OUString *pNames = aNames.getConstArray();
1174     for (const uno::Any& rValue : aValues)
1175     {
1176         uno::Sequence< OUString > aSvcImplNames;
1177         if (rValue >>= aSvcImplNames)
1178         {
1179             OUString aLocaleStr( *pNames++ );
1180             sal_Int32 nSeparatorPos = aLocaleStr.lastIndexOf( '/' );
1181             aLocaleStr = aLocaleStr.copy( nSeparatorPos + 1 );
1182             rSpellDsp.SetServiceList( LanguageTag::convertToLocale(aLocaleStr), aSvcImplNames );
1183         }
1184     }
1185 }
1186 
1187 
SetCfgServiceLists(GrammarCheckingIterator & rGrammarDsp)1188 void LngSvcMgr::SetCfgServiceLists( GrammarCheckingIterator &rGrammarDsp )
1189 {
1190     SAL_INFO( "linguistic", "linguistic: LngSvcMgr::SetCfgServiceLists - Grammar" );
1191 
1192     OUString aNode("ServiceManager/GrammarCheckerList");
1193     uno::Sequence< OUString > aNames( /*aCfg.*/GetNodeNames( aNode ) );
1194 
1195     // append path prefix need for 'GetProperties' call below
1196     OUString aPrefix = aNode  + "/";
1197     for (OUString & name : aNames)
1198     {
1199         name = aPrefix + name;
1200     }
1201 
1202     const uno::Sequence< uno::Any > aValues( /*aCfg.*/GetProperties( aNames ) );
1203     if (!(aNames.hasElements()  &&  aNames.getLength() == aValues.getLength()))
1204         return;
1205 
1206     const OUString *pNames = aNames.getConstArray();
1207     for (const uno::Any& rValue : aValues)
1208     {
1209         uno::Sequence< OUString > aSvcImplNames;
1210         if (rValue >>= aSvcImplNames)
1211         {
1212             // there should only be one grammar checker in use per language...
1213             if (aSvcImplNames.getLength() > 1)
1214                 aSvcImplNames.realloc(1);
1215 
1216             OUString aLocaleStr( *pNames++ );
1217             sal_Int32 nSeparatorPos = aLocaleStr.lastIndexOf( '/' );
1218             aLocaleStr = aLocaleStr.copy( nSeparatorPos + 1 );
1219             rGrammarDsp.SetServiceList( LanguageTag::convertToLocale(aLocaleStr), aSvcImplNames );
1220         }
1221     }
1222 }
1223 
1224 
SetCfgServiceLists(HyphenatorDispatcher & rHyphDsp)1225 void LngSvcMgr::SetCfgServiceLists( HyphenatorDispatcher &rHyphDsp )
1226 {
1227     SAL_INFO( "linguistic", "linguistic: LngSvcMgr::SetCfgServiceLists - Hyph" );
1228 
1229     OUString aNode("ServiceManager/HyphenatorList");
1230     uno::Sequence< OUString > aNames( /*aCfg.*/GetNodeNames( aNode ) );
1231 
1232     // append path prefix need for 'GetProperties' call below
1233     OUString aPrefix = aNode + "/";
1234     for (OUString & name : aNames)
1235     {
1236         name = aPrefix + name;
1237     }
1238 
1239     const uno::Sequence< uno::Any > aValues( /*aCfg.*/GetProperties( aNames ) );
1240     if (!(aNames.hasElements()  &&  aNames.getLength() == aValues.getLength()))
1241         return;
1242 
1243     const OUString *pNames = aNames.getConstArray();
1244     for (const uno::Any& rValue : aValues)
1245     {
1246         uno::Sequence< OUString > aSvcImplNames;
1247         if (rValue >>= aSvcImplNames)
1248         {
1249             // there should only be one hyphenator in use per language...
1250             if (aSvcImplNames.getLength() > 1)
1251                 aSvcImplNames.realloc(1);
1252 
1253             OUString aLocaleStr( *pNames++ );
1254             sal_Int32 nSeparatorPos = aLocaleStr.lastIndexOf( '/' );
1255             aLocaleStr = aLocaleStr.copy( nSeparatorPos + 1 );
1256             rHyphDsp.SetServiceList( LanguageTag::convertToLocale(aLocaleStr), aSvcImplNames );
1257         }
1258     }
1259 }
1260 
1261 
SetCfgServiceLists(ThesaurusDispatcher & rThesDsp)1262 void LngSvcMgr::SetCfgServiceLists( ThesaurusDispatcher &rThesDsp )
1263 {
1264     SAL_INFO( "linguistic", "linguistic: LngSvcMgr::SetCfgServiceLists - Thes" );
1265 
1266     OUString aNode("ServiceManager/ThesaurusList");
1267     uno::Sequence< OUString > aNames( /*aCfg.*/GetNodeNames( aNode ) );
1268 
1269     // append path prefix need for 'GetProperties' call below
1270     OUString aPrefix = aNode + "/";
1271     for (OUString & name : aNames)
1272     {
1273         name = aPrefix + name;
1274     }
1275 
1276     const uno::Sequence< uno::Any > aValues( /*aCfg.*/GetProperties( aNames ) );
1277     if (!(aNames.hasElements()  &&  aNames.getLength() == aValues.getLength()))
1278         return;
1279 
1280     const OUString *pNames = aNames.getConstArray();
1281     for (const uno::Any& rValue : aValues)
1282     {
1283         uno::Sequence< OUString > aSvcImplNames;
1284         if (rValue >>= aSvcImplNames)
1285         {
1286             OUString aLocaleStr( *pNames++ );
1287             sal_Int32 nSeparatorPos = aLocaleStr.lastIndexOf( '/' );
1288             aLocaleStr = aLocaleStr.copy( nSeparatorPos + 1 );
1289             rThesDsp.SetServiceList( LanguageTag::convertToLocale(aLocaleStr), aSvcImplNames );
1290         }
1291     }
1292 }
1293 
1294 
1295 uno::Reference< linguistic2::XSpellChecker > SAL_CALL
getSpellChecker()1296     LngSvcMgr::getSpellChecker()
1297 {
1298     osl::MutexGuard aGuard( GetLinguMutex() );
1299 #if OSL_DEBUG_LEVEL > 0
1300     getAvailableLocales(SN_SPELLCHECKER);
1301 #endif
1302 
1303     uno::Reference< linguistic2::XSpellChecker > xRes;
1304     if (!bDisposing)
1305     {
1306         if (!mxSpellDsp.is())
1307             GetSpellCheckerDsp_Impl();
1308         xRes = mxSpellDsp.get();
1309     }
1310     return xRes;
1311 }
1312 
1313 
1314 uno::Reference< linguistic2::XHyphenator > SAL_CALL
getHyphenator()1315     LngSvcMgr::getHyphenator()
1316 {
1317     osl::MutexGuard aGuard( GetLinguMutex() );
1318 #if OSL_DEBUG_LEVEL > 0
1319     getAvailableLocales(SN_HYPHENATOR);
1320 #endif
1321     uno::Reference< linguistic2::XHyphenator >   xRes;
1322     if (!bDisposing)
1323     {
1324         if (!mxHyphDsp.is())
1325             GetHyphenatorDsp_Impl();
1326         xRes = mxHyphDsp.get();
1327     }
1328     return xRes;
1329 }
1330 
1331 
1332 uno::Reference< linguistic2::XThesaurus > SAL_CALL
getThesaurus()1333     LngSvcMgr::getThesaurus()
1334 {
1335     osl::MutexGuard aGuard( GetLinguMutex() );
1336 #if OSL_DEBUG_LEVEL > 0
1337     getAvailableLocales(SN_THESAURUS);
1338 #endif
1339     uno::Reference< linguistic2::XThesaurus >    xRes;
1340     if (!bDisposing)
1341     {
1342         if (!mxThesDsp.is())
1343             GetThesaurusDsp_Impl();
1344         xRes = mxThesDsp.get();
1345     }
1346     return xRes;
1347 }
1348 
1349 
1350 sal_Bool SAL_CALL
addLinguServiceManagerListener(const uno::Reference<lang::XEventListener> & xListener)1351     LngSvcMgr::addLinguServiceManagerListener(
1352             const uno::Reference< lang::XEventListener >& xListener )
1353 {
1354     osl::MutexGuard aGuard( GetLinguMutex() );
1355 
1356     if (bDisposing || !xListener.is())
1357         return false;
1358 
1359     if (!mxListenerHelper.is())
1360         GetListenerHelper_Impl();
1361     mxListenerHelper->AddLngSvcMgrListener( xListener );
1362     return true;
1363 }
1364 
1365 
1366 sal_Bool SAL_CALL
removeLinguServiceManagerListener(const uno::Reference<lang::XEventListener> & xListener)1367     LngSvcMgr::removeLinguServiceManagerListener(
1368             const uno::Reference< lang::XEventListener >& xListener )
1369 {
1370     osl::MutexGuard aGuard( GetLinguMutex() );
1371 
1372     if (bDisposing || !xListener.is())
1373         return false;
1374 
1375     DBG_ASSERT( mxListenerHelper.is(), "listener removed without being added" );
1376     if (!mxListenerHelper.is())
1377         GetListenerHelper_Impl();
1378     mxListenerHelper->RemoveLngSvcMgrListener( xListener );
1379     return true;
1380 }
1381 
1382 
1383 uno::Sequence< OUString > SAL_CALL
getAvailableServices(const OUString & rServiceName,const lang::Locale & rLocale)1384     LngSvcMgr::getAvailableServices(
1385             const OUString& rServiceName,
1386             const lang::Locale& rLocale )
1387 {
1388     osl::MutexGuard aGuard( GetLinguMutex() );
1389 
1390     uno::Sequence< OUString > aRes;
1391     const SvcInfoArray *pInfoArray = nullptr;
1392 
1393     if (rServiceName == SN_SPELLCHECKER)
1394     {
1395         GetAvailableSpellSvcs_Impl();
1396         pInfoArray = pAvailSpellSvcs.get();
1397     }
1398     else if (rServiceName == SN_GRAMMARCHECKER)
1399     {
1400         GetAvailableGrammarSvcs_Impl();
1401         pInfoArray = pAvailGrammarSvcs.get();
1402     }
1403     else if (rServiceName == SN_HYPHENATOR)
1404     {
1405         GetAvailableHyphSvcs_Impl();
1406         pInfoArray = pAvailHyphSvcs.get();
1407     }
1408     else if (rServiceName == SN_THESAURUS)
1409     {
1410         GetAvailableThesSvcs_Impl();
1411         pInfoArray = pAvailThesSvcs.get();
1412     }
1413 
1414     if (pInfoArray)
1415     {
1416         std::vector<OUString> aVec;
1417         aVec.reserve(pInfoArray->size());
1418 
1419         LanguageType nLanguage = LinguLocaleToLanguage( rLocale );
1420         for (const auto& pInfo : *pInfoArray)
1421         {
1422             if (LinguIsUnspecified( nLanguage )
1423                 || pInfo->HasLanguage( nLanguage ))
1424             {
1425                 aVec.push_back(pInfo->aSvcImplName);
1426             }
1427         }
1428 
1429         aRes = comphelper::containerToSequence(aVec);
1430     }
1431 
1432     return aRes;
1433 }
1434 
1435 
1436 uno::Sequence< lang::Locale > SAL_CALL
getAvailableLocales(const OUString & rServiceName)1437     LngSvcMgr::getAvailableLocales(
1438             const OUString& rServiceName )
1439 {
1440     osl::MutexGuard aGuard( GetLinguMutex() );
1441 
1442     uno::Sequence< lang::Locale > aRes;
1443 
1444     uno::Sequence< lang::Locale >  *pAvailLocales     = nullptr;
1445     if (rServiceName == SN_SPELLCHECKER)
1446         pAvailLocales       = &aAvailSpellLocales;
1447     else if (rServiceName == SN_GRAMMARCHECKER)
1448         pAvailLocales       = &aAvailGrammarLocales;
1449     else if (rServiceName == SN_HYPHENATOR)
1450         pAvailLocales       = &aAvailHyphLocales;
1451     else if (rServiceName == SN_THESAURUS)
1452         pAvailLocales       = &aAvailThesLocales;
1453 
1454     // Nowadays (with OOo lingu in SO) we want to know immediately about
1455     // new downloaded dictionaries and have them ready right away if the Tools/Options...
1456     // is used to activate them. Thus we can not rely anymore on buffered data.
1457     if (pAvailLocales)
1458     {
1459         *pAvailLocales = GetAvailLocales(getAvailableServices(rServiceName, lang::Locale()));
1460         aRes = *pAvailLocales;
1461     }
1462 
1463     return aRes;
1464 }
1465 
IsEqSvcList(const uno::Sequence<OUString> & rList1,const uno::Sequence<OUString> & rList2)1466 static bool IsEqSvcList( const uno::Sequence< OUString > &rList1,
1467                          const uno::Sequence< OUString > &rList2 )
1468 {
1469     // returns true if both sequences are equal
1470     return rList1.getLength() == rList2.getLength()
1471         && std::equal(rList1.begin(), rList1.end(), rList2.begin(), rList2.end());
1472 }
1473 
1474 
1475 void SAL_CALL
setConfiguredServices(const OUString & rServiceName,const lang::Locale & rLocale,const uno::Sequence<OUString> & rServiceImplNames)1476     LngSvcMgr::setConfiguredServices(
1477             const OUString& rServiceName,
1478             const lang::Locale& rLocale,
1479             const uno::Sequence< OUString >& rServiceImplNames )
1480 {
1481     SAL_INFO( "linguistic", "linguistic: LngSvcMgr::setConfiguredServices" );
1482 
1483     osl::MutexGuard aGuard( GetLinguMutex() );
1484 
1485     LanguageType nLanguage = LinguLocaleToLanguage( rLocale );
1486     if (LinguIsUnspecified( nLanguage))
1487         return;
1488 
1489     if (rServiceName == SN_SPELLCHECKER)
1490     {
1491         if (!mxSpellDsp.is())
1492             GetSpellCheckerDsp_Impl();
1493         bool bChanged = !IsEqSvcList( rServiceImplNames,
1494                                       mxSpellDsp->GetServiceList( rLocale ) );
1495         if (bChanged)
1496         {
1497             mxSpellDsp->SetServiceList( rLocale, rServiceImplNames );
1498             SaveCfgSvcs( SN_SPELLCHECKER );
1499 
1500             if (mxListenerHelper)
1501                 mxListenerHelper->AddLngSvcEvt(
1502                         linguistic2::LinguServiceEventFlags::SPELL_CORRECT_WORDS_AGAIN |
1503                         linguistic2::LinguServiceEventFlags::SPELL_WRONG_WORDS_AGAIN );
1504         }
1505     }
1506     else if (rServiceName == SN_GRAMMARCHECKER)
1507     {
1508         if (!mxGrammarDsp.is())
1509             GetGrammarCheckerDsp_Impl();
1510         bool bChanged = !IsEqSvcList( rServiceImplNames,
1511                                       mxGrammarDsp->GetServiceList( rLocale ) );
1512         if (bChanged)
1513         {
1514             mxGrammarDsp->SetServiceList( rLocale, rServiceImplNames );
1515             SaveCfgSvcs( SN_GRAMMARCHECKER );
1516 
1517             if (mxListenerHelper)
1518                 mxListenerHelper->AddLngSvcEvt(
1519                         linguistic2::LinguServiceEventFlags::PROOFREAD_AGAIN );
1520         }
1521     }
1522     else if (rServiceName == SN_HYPHENATOR)
1523     {
1524         if (!mxHyphDsp.is())
1525             GetHyphenatorDsp_Impl();
1526         bool bChanged = !IsEqSvcList( rServiceImplNames,
1527                                       mxHyphDsp->GetServiceList( rLocale ) );
1528         if (bChanged)
1529         {
1530             mxHyphDsp->SetServiceList( rLocale, rServiceImplNames );
1531             SaveCfgSvcs( SN_HYPHENATOR );
1532 
1533             if (mxListenerHelper)
1534                 mxListenerHelper->AddLngSvcEvt(
1535                         linguistic2::LinguServiceEventFlags::HYPHENATE_AGAIN );
1536         }
1537     }
1538     else if (rServiceName == SN_THESAURUS)
1539     {
1540         if (!mxThesDsp.is())
1541             GetThesaurusDsp_Impl();
1542         bool bChanged = !IsEqSvcList( rServiceImplNames,
1543                                       mxThesDsp->GetServiceList( rLocale ) );
1544         if (bChanged)
1545         {
1546             mxThesDsp->SetServiceList( rLocale, rServiceImplNames );
1547             SaveCfgSvcs( SN_THESAURUS );
1548         }
1549     }
1550 }
1551 
1552 
SaveCfgSvcs(std::u16string_view rServiceName)1553 bool LngSvcMgr::SaveCfgSvcs( std::u16string_view rServiceName )
1554 {
1555     SAL_INFO( "linguistic", "linguistic: LngSvcMgr::SaveCfgSvcs" );
1556 
1557     bool bRes = false;
1558 
1559     LinguDispatcher *pDsp = nullptr;
1560     uno::Sequence< lang::Locale > aLocales;
1561 
1562     if (rServiceName == SN_SPELLCHECKER)
1563     {
1564         if (!mxSpellDsp)
1565             GetSpellCheckerDsp_Impl();
1566         pDsp = mxSpellDsp.get();
1567         aLocales = getAvailableLocales( SN_SPELLCHECKER );
1568     }
1569     else if (rServiceName == SN_GRAMMARCHECKER)
1570     {
1571         if (!mxGrammarDsp.is())
1572             GetGrammarCheckerDsp_Impl();
1573         pDsp = mxGrammarDsp.get();
1574         aLocales = getAvailableLocales( SN_GRAMMARCHECKER );
1575     }
1576     else if (rServiceName == SN_HYPHENATOR)
1577     {
1578         if (!mxHyphDsp.is())
1579             GetHyphenatorDsp_Impl();
1580         pDsp = mxHyphDsp.get();
1581         aLocales = getAvailableLocales( SN_HYPHENATOR );
1582     }
1583     else if (rServiceName == SN_THESAURUS)
1584     {
1585         if (!mxThesDsp.is())
1586             GetThesaurusDsp_Impl();
1587         pDsp = mxThesDsp.get();
1588         aLocales = getAvailableLocales( SN_THESAURUS );
1589     }
1590 
1591     if (pDsp  &&  aLocales.hasElements())
1592     {
1593         uno::Sequence< beans::PropertyValue > aValues( aLocales.getLength() );
1594         beans::PropertyValue *pValue = aValues.getArray();
1595 
1596         // get node name to be used
1597         const char *pNodeName = nullptr;
1598         if (pDsp == mxSpellDsp.get())
1599             pNodeName = "ServiceManager/SpellCheckerList";
1600         else if (pDsp == mxGrammarDsp.get())
1601             pNodeName = "ServiceManager/GrammarCheckerList";
1602         else if (pDsp == mxHyphDsp.get())
1603             pNodeName = "ServiceManager/HyphenatorList";
1604         else if (pDsp == mxThesDsp.get())
1605             pNodeName = "ServiceManager/ThesaurusList";
1606         else
1607         {
1608             SAL_WARN( "linguistic", "node name missing" );
1609         }
1610         OUString aNodeName( OUString::createFromAscii(pNodeName) );
1611 
1612         for (const lang::Locale& rLocale : std::as_const(aLocales))
1613         {
1614             uno::Sequence< OUString > aSvcImplNames = pDsp->GetServiceList( rLocale );
1615 
1616             // build value to be written back to configuration
1617             uno::Any aCfgAny;
1618             if ((pDsp == mxHyphDsp.get() || pDsp == mxGrammarDsp.get()) && aSvcImplNames.getLength() > 1)
1619                 aSvcImplNames.realloc(1);   // there should be only one entry for hyphenators or grammar checkers (because they are not chained)
1620             aCfgAny <<= aSvcImplNames;
1621             DBG_ASSERT( aCfgAny.hasValue(), "missing value for 'Any' type" );
1622 
1623             OUString aCfgLocaleStr( LanguageTag::convertToBcp47( rLocale));
1624             pValue->Value = aCfgAny;
1625             pValue->Name  = aNodeName + "/" + aCfgLocaleStr;
1626             pValue++;
1627         }
1628         {
1629         SAL_INFO( "linguistic", "linguistic: LngSvcMgr::SaveCfgSvcs - ReplaceSetProperties" );
1630         // change, add new or replace existing entries.
1631         bRes |= /*aCfg.*/ReplaceSetProperties( aNodeName, aValues );
1632         }
1633     }
1634 
1635     return bRes;
1636 }
1637 
1638 
GetLangSvcList(const uno::Any & rVal)1639 static uno::Sequence< OUString > GetLangSvcList( const uno::Any &rVal )
1640 {
1641     uno::Sequence< OUString > aRes;
1642 
1643     if (rVal.hasValue())
1644     {
1645         rVal >>= aRes;
1646 #if OSL_DEBUG_LEVEL > 0
1647         for (const OUString& rSvcName : std::as_const(aRes))
1648         {
1649             SAL_WARN_IF( rSvcName.isEmpty(), "linguistic", "service impl-name missing" );
1650         }
1651 #endif
1652     }
1653 
1654     return aRes;
1655 }
1656 
1657 
GetLangSvc(const uno::Any & rVal)1658 static uno::Sequence< OUString > GetLangSvc( const uno::Any &rVal )
1659 {
1660     uno::Sequence< OUString > aRes;
1661     if (!rVal.hasValue())
1662         return aRes;
1663 
1664     // allowing for a sequence here as well (even though it should only
1665     // be a string) makes coding easier in other places since one needs
1666     // not make a special case for writing a string only and not a
1667     // sequence of strings.
1668     if (rVal >>= aRes)
1669     {
1670         // but only the first string should be used.
1671         if (aRes.getLength() > 1)
1672             aRes.realloc(1);
1673     }
1674     else
1675     {
1676         OUString aImplName;
1677         if ((rVal >>= aImplName) && !aImplName.isEmpty())
1678         {
1679             aRes.realloc(1);
1680             aRes.getArray()[0] = aImplName;
1681         }
1682         else
1683         {
1684             SAL_WARN( "linguistic", "GetLangSvc: unexpected type encountered" );
1685         }
1686     }
1687 
1688     return aRes;
1689 }
1690 
1691 
1692 uno::Sequence< OUString > SAL_CALL
getConfiguredServices(const OUString & rServiceName,const lang::Locale & rLocale)1693     LngSvcMgr::getConfiguredServices(
1694             const OUString& rServiceName,
1695             const lang::Locale& rLocale )
1696 {
1697     osl::MutexGuard aGuard( GetLinguMutex() );
1698 
1699     uno::Sequence< OUString > aSvcImplNames;
1700 
1701     OUString aCfgLocale( LanguageTag::convertToBcp47( rLocale) );
1702 
1703     uno::Sequence< uno::Any > aValues;
1704     uno::Sequence< OUString > aNames( 1 );
1705     OUString *pNames = aNames.getArray();
1706     if ( rServiceName == SN_SPELLCHECKER )
1707     {
1708         OUString aNode( "ServiceManager/SpellCheckerList");
1709         const uno::Sequence< OUString > aNodeEntries( GetNodeNames( aNode ) );
1710         if (lcl_SeqHasString( aNodeEntries, aCfgLocale ))
1711         {
1712             pNames[0] = aNode + "/" + aCfgLocale;
1713             aValues = /*aCfg.*/GetProperties( aNames );
1714             if (aValues.hasElements())
1715                 aSvcImplNames = GetLangSvcList( aValues.getConstArray()[0] );
1716         }
1717     }
1718     else if ( rServiceName == SN_GRAMMARCHECKER )
1719     {
1720         OUString aNode( "ServiceManager/GrammarCheckerList");
1721         const uno::Sequence< OUString > aNodeEntries( GetNodeNames( aNode ) );
1722         if (lcl_SeqHasString( aNodeEntries, aCfgLocale ))
1723         {
1724             pNames[0] = aNode + "/" + aCfgLocale;
1725             aValues = /*aCfg.*/GetProperties( aNames );
1726             if (aValues.hasElements())
1727                 aSvcImplNames = GetLangSvc( aValues.getConstArray()[0] );
1728         }
1729     }
1730     else if ( rServiceName == SN_HYPHENATOR )
1731     {
1732         OUString aNode( "ServiceManager/HyphenatorList");
1733         const uno::Sequence< OUString > aNodeEntries( GetNodeNames( aNode ) );
1734         if (lcl_SeqHasString( aNodeEntries, aCfgLocale ))
1735         {
1736             pNames[0] = aNode + "/" + aCfgLocale;
1737             aValues = /*aCfg.*/GetProperties( aNames );
1738             if (aValues.hasElements())
1739                 aSvcImplNames = GetLangSvc( aValues.getConstArray()[0] );
1740         }
1741     }
1742     else if ( rServiceName == SN_THESAURUS )
1743     {
1744         OUString aNode( "ServiceManager/ThesaurusList");
1745         const uno::Sequence< OUString > aNodeEntries( GetNodeNames( aNode ) );
1746         if (lcl_SeqHasString( aNodeEntries, aCfgLocale ))
1747         {
1748             pNames[0] = aNode + "/" + aCfgLocale;
1749             aValues = /*aCfg.*/GetProperties( aNames );
1750             if (aValues.hasElements())
1751                 aSvcImplNames = GetLangSvcList( aValues.getConstArray()[0] );
1752         }
1753     }
1754 
1755     return aSvcImplNames;
1756 }
1757 
1758 
1759 void SAL_CALL
dispose()1760     LngSvcMgr::dispose()
1761 {
1762     osl::MutexGuard aGuard( GetLinguMutex() );
1763 
1764     if (!bDisposing)
1765     {
1766         bDisposing = true;
1767 
1768         // require listeners to release this object
1769         lang::EventObject aEvtObj( static_cast<XLinguServiceManager*>(this) );
1770         aEvtListeners.disposeAndClear( aEvtObj );
1771 
1772         if (mxListenerHelper.is())
1773             mxListenerHelper->DisposeAndClear( aEvtObj );
1774     }
1775 }
1776 
1777 
1778 void SAL_CALL
addEventListener(const uno::Reference<lang::XEventListener> & xListener)1779     LngSvcMgr::addEventListener(
1780             const uno::Reference< lang::XEventListener >& xListener )
1781 {
1782     osl::MutexGuard aGuard( GetLinguMutex() );
1783 
1784     if (!bDisposing  &&  xListener.is())
1785     {
1786         aEvtListeners.addInterface( xListener );
1787     }
1788 }
1789 
1790 
1791 void SAL_CALL
removeEventListener(const uno::Reference<lang::XEventListener> & xListener)1792     LngSvcMgr::removeEventListener(
1793             const uno::Reference< lang::XEventListener >& xListener )
1794 {
1795     osl::MutexGuard aGuard( GetLinguMutex() );
1796 
1797     if (xListener.is())
1798     {
1799         aEvtListeners.removeInterface( xListener );
1800     }
1801 }
1802 
1803 
AddLngSvcEvtBroadcaster(const uno::Reference<linguistic2::XLinguServiceEventBroadcaster> & rxBroadcaster)1804 bool LngSvcMgr::AddLngSvcEvtBroadcaster(
1805             const uno::Reference< linguistic2::XLinguServiceEventBroadcaster > &rxBroadcaster )
1806 {
1807     if (!rxBroadcaster.is())
1808         return false;
1809     if (!mxListenerHelper.is())
1810         GetListenerHelper_Impl();
1811     mxListenerHelper->AddLngSvcEvtBroadcaster( rxBroadcaster );
1812     return true;
1813 }
1814 
1815 
1816 OUString SAL_CALL
getImplementationName()1817     LngSvcMgr::getImplementationName()
1818 {
1819     return "com.sun.star.lingu2.LngSvcMgr";
1820 }
1821 
1822 
1823 sal_Bool SAL_CALL
supportsService(const OUString & ServiceName)1824     LngSvcMgr::supportsService( const OUString& ServiceName )
1825 {
1826     return cppu::supportsService(this, ServiceName);
1827 }
1828 
1829 
1830 uno::Sequence< OUString > SAL_CALL
getSupportedServiceNames()1831     LngSvcMgr::getSupportedServiceNames()
1832 {
1833     return { "com.sun.star.linguistic2.LinguServiceManager" };
1834 }
1835 
1836 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
linguistic_LngSvcMgr_get_implementation(css::uno::XComponentContext *,css::uno::Sequence<css::uno::Any> const &)1837 linguistic_LngSvcMgr_get_implementation(
1838     css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&)
1839 {
1840     return cppu::acquire(new LngSvcMgr());
1841 }
1842 
1843 
1844 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1845