Coverage Report

Created: 2026-02-14 09:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/linguistic/source/convdiclist.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
22
#include <string_view>
23
24
#include <com/sun/star/container/XNameContainer.hpp>
25
#include <com/sun/star/lang/Locale.hpp>
26
#include <com/sun/star/lang/NoSupportException.hpp>
27
#include <com/sun/star/linguistic2/ConversionDictionaryType.hpp>
28
#include <com/sun/star/linguistic2/XConversionDictionaryList.hpp>
29
#include <com/sun/star/uno/Reference.h>
30
#include <com/sun/star/util/XFlushable.hpp>
31
#include <cppuhelper/factory.hxx>
32
#include <cppuhelper/implbase.hxx>
33
#include <comphelper/processfactory.hxx>
34
#include <comphelper/sequence.hxx>
35
#include <cppuhelper/supportsservice.hxx>
36
#include <tools/debug.hxx>
37
#include <tools/urlobj.hxx>
38
#include <ucbhelper/content.hxx>
39
#include <unotools/localfilehelper.hxx>
40
#include <unotools/lingucfg.hxx>
41
#include <comphelper/diagnose_ex.hxx>
42
43
#include "convdic.hxx"
44
#include "convdiclist.hxx"
45
#include "hhconvdic.hxx"
46
#include <linguistic/misc.hxx>
47
48
using namespace osl;
49
using namespace com::sun::star;
50
using namespace com::sun::star::lang;
51
using namespace com::sun::star::uno;
52
using namespace com::sun::star::container;
53
using namespace com::sun::star::linguistic2;
54
using namespace linguistic;
55
56
static OUString GetConvDicMainURL( std::u16string_view rDicName, std::u16string_view rDirectoryURL )
57
0
{
58
    // build URL to use for new (persistent) dictionaries
59
60
0
    OUString aFullDicName = OUString::Concat(rDicName) + CONV_DIC_DOT_EXT;
61
62
0
    INetURLObject aURLObj;
63
0
    aURLObj.SetSmartProtocol( INetProtocol::File );
64
0
    aURLObj.SetSmartURL( rDirectoryURL );
65
0
    aURLObj.Append( aFullDicName, INetURLObject::EncodeMechanism::All );
66
0
    DBG_ASSERT(!aURLObj.HasError(), "invalid URL");
67
0
    if (aURLObj.HasError())
68
0
        return OUString();
69
0
    else
70
0
        return aURLObj.GetMainURL( INetURLObject::DecodeMechanism::ToIUri );
71
0
}
72
73
class ConvDicNameContainer :
74
    public cppu::WeakImplHelper< css::container::XNameContainer >
75
{
76
    std::vector< uno::Reference< XConversionDictionary > >   aConvDics;
77
78
    sal_Int32 GetIndexByName_Impl( std::u16string_view rName );
79
80
public:
81
    ConvDicNameContainer();
82
    ConvDicNameContainer(const ConvDicNameContainer&) = delete;
83
    ConvDicNameContainer& operator=(const ConvDicNameContainer&) = delete;
84
85
    // XElementAccess
86
    virtual css::uno::Type SAL_CALL getElementType(  ) override;
87
    virtual sal_Bool SAL_CALL hasElements(  ) override;
88
89
    // XNameAccess
90
    virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override;
91
    virtual css::uno::Sequence< OUString > SAL_CALL getElementNames(  ) override;
92
    virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override;
93
94
    // XNameReplace
95
    virtual void SAL_CALL replaceByName( const OUString& aName, const css::uno::Any& aElement ) override;
96
97
    // XNameContainer
98
    virtual void SAL_CALL insertByName( const OUString& aName, const css::uno::Any& aElement ) override;
99
    virtual void SAL_CALL removeByName( const OUString& Name ) override;
100
101
    // looks for conversion dictionaries with the specified extension
102
    // in the directory and adds them to the container
103
    void AddConvDics( const OUString &rSearchDirPathURL, const OUString &rExtension );
104
105
    // calls Flush for the dictionaries that support XFlushable
106
    void    FlushDics() const;
107
108
0
    sal_Int32   GetCount() const    { return aConvDics.size(); }
109
    uno::Reference< XConversionDictionary > GetByName( std::u16string_view rName );
110
111
    const uno::Reference< XConversionDictionary >&  GetByIndex( sal_Int32 nIdx )
112
0
    {
113
0
        return aConvDics[nIdx];
114
0
    }
115
};
116
117
ConvDicNameContainer::ConvDicNameContainer()
118
0
{
119
0
}
120
121
void ConvDicNameContainer::FlushDics() const
122
0
{
123
0
    sal_Int32 nLen = aConvDics.size();
124
0
    for (sal_Int32 i = 0;  i < nLen;  ++i)
125
0
    {
126
0
        uno::Reference< util::XFlushable > xFlush( aConvDics[i] , UNO_QUERY );
127
0
        if (xFlush.is())
128
0
        {
129
0
            try
130
0
            {
131
0
                xFlush->flush();
132
0
            }
133
0
            catch(Exception &)
134
0
            {
135
0
                OSL_FAIL( "flushing of conversion dictionary failed" );
136
0
            }
137
0
        }
138
0
    }
139
0
}
140
141
sal_Int32 ConvDicNameContainer::GetIndexByName_Impl(
142
        std::u16string_view rName )
143
0
{
144
0
    sal_Int32 nRes = -1;
145
0
    sal_Int32 nLen = aConvDics.size();
146
0
    for (sal_Int32 i = 0;  i < nLen && nRes == -1;  ++i)
147
0
    {
148
0
        if (rName == aConvDics[i]->getName())
149
0
            nRes = i;
150
0
    }
151
0
    return nRes;
152
0
}
153
154
uno::Reference< XConversionDictionary > ConvDicNameContainer::GetByName(
155
        std::u16string_view rName )
156
0
{
157
0
    uno::Reference< XConversionDictionary > xRes;
158
0
    sal_Int32 nIdx = GetIndexByName_Impl( rName );
159
0
    if ( nIdx != -1)
160
0
        xRes = aConvDics[nIdx];
161
0
    return xRes;
162
0
}
163
164
uno::Type SAL_CALL ConvDicNameContainer::getElementType(  )
165
0
{
166
0
    return cppu::UnoType<XConversionDictionary>::get();
167
0
}
168
169
sal_Bool SAL_CALL ConvDicNameContainer::hasElements(  )
170
0
{
171
0
    MutexGuard  aGuard( GetLinguMutex() );
172
0
    return !aConvDics.empty();
173
0
}
174
175
uno::Any SAL_CALL ConvDicNameContainer::getByName( const OUString& rName )
176
0
{
177
0
    MutexGuard  aGuard( GetLinguMutex() );
178
0
    uno::Reference< XConversionDictionary > xRes( GetByName( rName ) );
179
0
    if (!xRes.is())
180
0
        throw NoSuchElementException();
181
0
    return Any( xRes );
182
0
}
183
184
uno::Sequence< OUString > SAL_CALL ConvDicNameContainer::getElementNames(  )
185
0
{
186
0
    MutexGuard  aGuard( GetLinguMutex() );
187
188
0
    std::vector<OUString> aRes;
189
0
    aRes.reserve(aConvDics.size());
190
191
0
    std::transform(aConvDics.begin(), aConvDics.end(), std::back_inserter(aRes),
192
0
        [](const uno::Reference<XConversionDictionary>& rDic) { return rDic->getName(); });
193
194
0
    return comphelper::containerToSequence(aRes);
195
0
}
196
197
sal_Bool SAL_CALL ConvDicNameContainer::hasByName( const OUString& rName )
198
0
{
199
0
    MutexGuard  aGuard( GetLinguMutex() );
200
0
    return GetByName( rName ).is();
201
0
}
202
203
void SAL_CALL ConvDicNameContainer::replaceByName(
204
        const OUString& rName,
205
        const uno::Any& rElement )
206
0
{
207
0
    MutexGuard  aGuard( GetLinguMutex() );
208
209
0
    sal_Int32 nRplcIdx = GetIndexByName_Impl( rName );
210
0
    if (nRplcIdx == -1)
211
0
        throw NoSuchElementException();
212
0
    uno::Reference< XConversionDictionary > xNew;
213
0
    rElement >>= xNew;
214
0
    if (!xNew.is() || xNew->getName() != rName)
215
0
        throw IllegalArgumentException();
216
0
    aConvDics[ nRplcIdx ] = std::move(xNew);
217
0
}
218
219
void SAL_CALL ConvDicNameContainer::insertByName(
220
        const OUString& rName,
221
        const Any& rElement )
222
0
{
223
0
    MutexGuard  aGuard( GetLinguMutex() );
224
225
0
    if (GetByName( rName ).is())
226
0
        throw ElementExistException();
227
0
    uno::Reference< XConversionDictionary > xNew;
228
0
    rElement >>= xNew;
229
0
    if (!xNew.is() || xNew->getName() != rName)
230
0
        throw IllegalArgumentException();
231
232
0
    aConvDics.push_back(xNew);
233
0
}
234
235
void SAL_CALL ConvDicNameContainer::removeByName( const OUString& rName )
236
0
{
237
0
    MutexGuard  aGuard( GetLinguMutex() );
238
239
0
    sal_Int32 nRplcIdx = GetIndexByName_Impl( rName );
240
0
    if (nRplcIdx == -1)
241
0
        throw NoSuchElementException();
242
243
    // physically remove dictionary
244
0
    uno::Reference< XConversionDictionary > xDel = aConvDics[nRplcIdx];
245
0
    OUString aName( xDel->getName() );
246
0
    OUString aDicMainURL( GetConvDicMainURL( aName, GetDictionaryWriteablePath() ) );
247
0
    INetURLObject aObj( aDicMainURL );
248
0
    DBG_ASSERT( aObj.GetProtocol() == INetProtocol::File, "+HangulHanjaOptionsDialog::OkHdl(): non-file URLs cannot be deleted" );
249
0
    if( aObj.GetProtocol() == INetProtocol::File )
250
0
    {
251
0
        try
252
0
        {
253
0
            ::ucbhelper::Content    aCnt( aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ),
254
0
                                    uno::Reference< css::ucb::XCommandEnvironment >(),
255
0
                                    comphelper::getProcessComponentContext() );
256
0
            aCnt.executeCommand( u"delete"_ustr, Any( true ) );
257
0
        }
258
0
        catch( ... )
259
0
        {
260
0
            TOOLS_WARN_EXCEPTION( "linguistic", "HangulHanjaOptionsDialog::OkHdl()" );
261
0
        }
262
0
    }
263
264
0
    aConvDics.erase(aConvDics.begin() + nRplcIdx);
265
0
}
266
267
void ConvDicNameContainer::AddConvDics(
268
        const OUString &rSearchDirPathURL,
269
        const OUString &rExtension )
270
0
{
271
0
    const Sequence< OUString > aDirCnt(
272
0
                utl::LocalFileHelper::GetFolderContents( rSearchDirPathURL, false ) );
273
274
0
    for (const OUString& aURL : aDirCnt)
275
0
    {
276
0
        sal_Int32 nPos = aURL.lastIndexOf('.');
277
0
        OUString aExt( aURL.copy(nPos + 1).toAsciiLowerCase() );
278
0
        OUString aSearchExt( rExtension.toAsciiLowerCase() );
279
0
        if(aExt != aSearchExt)
280
0
            continue;          // skip other files
281
282
0
        LanguageType nLang;
283
0
        sal_Int16 nConvType;
284
0
        if (IsConvDic( aURL, nLang, nConvType ))
285
0
        {
286
            // get decoded dictionary file name
287
0
            INetURLObject aURLObj( aURL );
288
0
            OUString aDicName = aURLObj.getBase( INetURLObject::LAST_SEGMENT,
289
0
                        true, INetURLObject::DecodeMechanism::WithCharset );
290
291
0
            uno::Reference < XConversionDictionary > xDic;
292
0
            if (nLang == LANGUAGE_KOREAN &&
293
0
                nConvType == ConversionDictionaryType::HANGUL_HANJA)
294
0
            {
295
0
                xDic = new HHConvDic( aDicName, aURL );
296
0
            }
297
0
            else if ((nLang == LANGUAGE_CHINESE_SIMPLIFIED || nLang == LANGUAGE_CHINESE_TRADITIONAL) &&
298
0
                      nConvType == ConversionDictionaryType::SCHINESE_TCHINESE)
299
0
            {
300
0
                xDic = new ConvDic( aDicName, nLang, nConvType, false, aURL );
301
0
            }
302
303
0
            if (xDic.is())
304
0
            {
305
0
                insertByName( xDic->getName(), Any(xDic) );
306
0
            }
307
0
        }
308
0
    }
309
0
}
310
311
namespace
312
{
313
    rtl::Reference<ConvDicList>& StaticConvDicList()
314
0
    {
315
0
        static rtl::Reference<ConvDicList> SINGLETON = new ConvDicList;
316
0
        return SINGLETON;
317
0
    };
318
}
319
320
void ConvDicList::MyAppExitListener::AtExit()
321
0
{
322
0
    rMyDicList.FlushDics();
323
0
    StaticConvDicList().clear();
324
0
}
325
326
ConvDicList::ConvDicList() :
327
0
    aEvtListeners( GetLinguMutex() )
328
0
{
329
0
    bDisposing = false;
330
331
0
    mxExitListener = new MyAppExitListener( *this );
332
0
    mxExitListener->Activate();
333
0
}
334
335
ConvDicList::~ConvDicList()
336
0
{
337
0
    if (!bDisposing && mxNameContainer.is())
338
0
        mxNameContainer->FlushDics();
339
340
0
    mxExitListener->Deactivate();
341
0
}
342
343
void ConvDicList::FlushDics()
344
0
{
345
    // check only pointer to avoid creating the container when
346
    // the dictionaries were not accessed yet
347
0
    if (mxNameContainer.is())
348
0
        mxNameContainer->FlushDics();
349
0
}
350
351
ConvDicNameContainer & ConvDicList::GetNameContainer()
352
0
{
353
0
    if (!mxNameContainer.is())
354
0
    {
355
0
        mxNameContainer = new ConvDicNameContainer;
356
0
        mxNameContainer->AddConvDics( GetDictionaryWriteablePath(), CONV_DIC_EXT  );
357
358
        // access list of text conversion dictionaries to activate
359
0
        SvtLinguOptions aOpt;
360
0
        SvtLinguConfig().GetOptions( aOpt );
361
0
        for (const OUString& rActiveConvDic : aOpt.aActiveConvDics)
362
0
        {
363
0
            uno::Reference< XConversionDictionary > xDic =
364
0
                    mxNameContainer->GetByName( rActiveConvDic );
365
0
            if (xDic.is())
366
0
                xDic->setActive( true );
367
0
        }
368
369
        // since there is no UI to active/deactivate the dictionaries
370
        // for chinese text conversion they should be activated by default
371
0
        uno::Reference< XConversionDictionary > xS2TDic =
372
0
                    mxNameContainer->GetByName( u"ChineseS2T" );
373
0
        uno::Reference< XConversionDictionary > xT2SDic =
374
0
                    mxNameContainer->GetByName( u"ChineseT2S" );
375
0
        if (xS2TDic.is())
376
0
            xS2TDic->setActive( true );
377
0
        if (xT2SDic.is())
378
0
            xT2SDic->setActive( true );
379
380
0
    }
381
0
    return *mxNameContainer;
382
0
}
383
384
uno::Reference< container::XNameContainer > SAL_CALL ConvDicList::getDictionaryContainer(  )
385
0
{
386
0
    MutexGuard  aGuard( GetLinguMutex() );
387
0
    GetNameContainer();
388
0
    DBG_ASSERT( mxNameContainer.is(), "missing name container" );
389
0
    return mxNameContainer;
390
0
}
391
392
uno::Reference< XConversionDictionary > SAL_CALL ConvDicList::addNewDictionary(
393
        const OUString& rName,
394
        const Locale& rLocale,
395
        sal_Int16 nConvDicType )
396
0
{
397
0
    MutexGuard  aGuard( GetLinguMutex() );
398
399
0
    LanguageType nLang = LinguLocaleToLanguage( rLocale );
400
401
0
    if (GetNameContainer().hasByName( rName ))
402
0
        throw ElementExistException();
403
404
0
    uno::Reference< XConversionDictionary > xRes;
405
0
    OUString aDicMainURL( GetConvDicMainURL( rName, GetDictionaryWriteablePath() ) );
406
0
    if (nLang == LANGUAGE_KOREAN &&
407
0
        nConvDicType == ConversionDictionaryType::HANGUL_HANJA)
408
0
    {
409
0
        xRes = new HHConvDic( rName, aDicMainURL );
410
0
    }
411
0
    else if ((nLang == LANGUAGE_CHINESE_SIMPLIFIED || nLang == LANGUAGE_CHINESE_TRADITIONAL) &&
412
0
              nConvDicType == ConversionDictionaryType::SCHINESE_TCHINESE)
413
0
    {
414
0
        xRes = new ConvDic( rName, nLang, nConvDicType, false, aDicMainURL );
415
0
    }
416
417
0
    if (!xRes.is())
418
0
        throw NoSupportException();
419
420
0
    xRes->setActive( true );
421
0
    GetNameContainer().insertByName( rName, Any(xRes) );
422
0
    return xRes;
423
0
}
424
425
uno::Sequence< OUString > SAL_CALL ConvDicList::queryConversions(
426
        const OUString& rText,
427
        sal_Int32 nStartPos,
428
        sal_Int32 nLength,
429
        const Locale& rLocale,
430
        sal_Int16 nConversionDictionaryType,
431
        ConversionDirection eDirection,
432
        sal_Int32 nTextConversionOptions )
433
0
{
434
0
    MutexGuard  aGuard( GetLinguMutex() );
435
436
0
    std::vector< OUString > aRes;
437
438
0
    bool bSupported = false;
439
0
    sal_Int32 nLen = GetNameContainer().GetCount();
440
0
    for (sal_Int32 i = 0;  i < nLen;  ++i)
441
0
    {
442
0
        const uno::Reference< XConversionDictionary > xDic( GetNameContainer().GetByIndex(i) );
443
0
        bool bMatch =   xDic.is()  &&
444
0
                            xDic->getLocale() == rLocale  &&
445
0
                            xDic->getConversionType() == nConversionDictionaryType;
446
0
        bSupported |= bMatch;
447
0
        if (bMatch  &&  xDic->isActive())
448
0
        {
449
0
            const Sequence< OUString > aNewConv( xDic->getConversions(
450
0
                                rText, nStartPos, nLength,
451
0
                                eDirection, nTextConversionOptions ) );
452
0
            aRes.insert( aRes.end(), aNewConv.begin(), aNewConv.end() );
453
0
        }
454
0
    }
455
456
0
    if (!bSupported)
457
0
        throw NoSupportException();
458
459
0
    return comphelper::containerToSequence(aRes);
460
0
}
461
462
sal_Int16 SAL_CALL ConvDicList::queryMaxCharCount(
463
        const Locale& rLocale,
464
        sal_Int16 nConversionDictionaryType,
465
        ConversionDirection eDirection )
466
0
{
467
0
    MutexGuard  aGuard( GetLinguMutex() );
468
469
0
    sal_Int16 nRes = 0;
470
0
    GetNameContainer();
471
0
    sal_Int32 nLen = GetNameContainer().GetCount();
472
0
    for (sal_Int32 i = 0;  i < nLen;  ++i)
473
0
    {
474
0
        const uno::Reference< XConversionDictionary > xDic( GetNameContainer().GetByIndex(i) );
475
0
        if (xDic.is()  &&
476
0
            xDic->getLocale() == rLocale  &&
477
0
            xDic->getConversionType() == nConversionDictionaryType)
478
0
        {
479
0
            sal_Int16 nC = xDic->getMaxCharCount( eDirection );
480
0
            if (nC > nRes)
481
0
                nRes = nC;
482
0
        }
483
0
    }
484
0
    return nRes;
485
0
}
486
487
void SAL_CALL ConvDicList::dispose(  )
488
0
{
489
0
    MutexGuard  aGuard( GetLinguMutex() );
490
0
    if (!bDisposing)
491
0
    {
492
0
        bDisposing = true;
493
0
        EventObject aEvtObj( static_cast<XConversionDictionaryList *>(this) );
494
0
        aEvtListeners.disposeAndClear( aEvtObj );
495
496
0
        FlushDics();
497
0
    }
498
0
}
499
500
void SAL_CALL ConvDicList::addEventListener(
501
        const uno::Reference< XEventListener >& rxListener )
502
0
{
503
0
    MutexGuard  aGuard( GetLinguMutex() );
504
0
    if (!bDisposing && rxListener.is())
505
0
        aEvtListeners.addInterface( rxListener );
506
0
}
507
508
void SAL_CALL ConvDicList::removeEventListener(
509
        const uno::Reference< XEventListener >& rxListener )
510
0
{
511
0
    MutexGuard  aGuard( GetLinguMutex() );
512
0
    if (!bDisposing && rxListener.is())
513
0
        aEvtListeners.removeInterface( rxListener );
514
0
}
515
516
OUString SAL_CALL ConvDicList::getImplementationName()
517
0
{
518
0
    return u"com.sun.star.lingu2.ConvDicList"_ustr;
519
0
}
520
521
sal_Bool SAL_CALL ConvDicList::supportsService( const OUString& rServiceName )
522
0
{
523
0
    return cppu::supportsService(this, rServiceName);
524
0
}
525
526
uno::Sequence< OUString > SAL_CALL ConvDicList::getSupportedServiceNames()
527
0
{
528
0
    return { u"com.sun.star.linguistic2.ConversionDictionaryList"_ustr };
529
0
}
530
531
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
532
linguistic_ConvDicList_get_implementation(
533
    css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&)
534
0
{
535
0
    return cppu::acquire(StaticConvDicList().get());
536
0
}
537
538
539
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */