Coverage Report

Created: 2025-12-08 09:28

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