Coverage Report

Created: 2025-12-08 09:28

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