Coverage Report

Created: 2025-12-31 10:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/linguistic/source/hyphdsp.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 <algorithm>
24
#if OSL_DEBUG_LEVEL > 0
25
#include <utility>
26
#endif
27
28
#include <cppuhelper/factory.hxx>
29
#include <com/sun/star/beans/XPropertySet.hpp>
30
#include <com/sun/star/uno/XComponentContext.hpp>
31
#include <com/sun/star/linguistic2/XLinguProperties.hpp>
32
#include <com/sun/star/linguistic2/XLinguServiceEventBroadcaster.hpp>
33
#include <rtl/ustrbuf.hxx>
34
#include <i18nlangtag/lang.h>
35
#include <unotools/localedatawrapper.hxx>
36
#include <tools/debug.hxx>
37
#include <svl/lngmisc.hxx>
38
#include <comphelper/processfactory.hxx>
39
#include <comphelper/sequence.hxx>
40
#include <osl/mutex.hxx>
41
42
#include "hyphdsp.hxx"
43
#include <linguistic/hyphdta.hxx>
44
#include <linguistic/misc.hxx>
45
#include "lngsvcmgr.hxx"
46
47
using namespace osl;
48
using namespace com::sun::star;
49
using namespace com::sun::star::beans;
50
using namespace com::sun::star::lang;
51
using namespace com::sun::star::uno;
52
using namespace com::sun::star::linguistic2;
53
using namespace linguistic;
54
55
56
HyphenatorDispatcher::HyphenatorDispatcher( LngSvcMgr &rLngSvcMgr ) :
57
0
    rMgr    (rLngSvcMgr)
58
0
{
59
0
}
60
61
62
HyphenatorDispatcher::~HyphenatorDispatcher()
63
0
{
64
0
    ClearSvcList();
65
0
}
66
67
68
void HyphenatorDispatcher::ClearSvcList()
69
0
{
70
    // release memory for each table entry
71
0
    HyphSvcByLangMap_t().swap(aSvcMap);
72
0
}
73
74
75
rtl::Reference< HyphenatedWord >  HyphenatorDispatcher::buildHyphWord(
76
            const OUString& rOrigWord,
77
            const Reference<XDictionaryEntry> &xEntry,
78
            LanguageType nLang, sal_Int16 nMaxLeading )
79
0
{
80
0
    MutexGuard  aGuard( GetLinguMutex() );
81
82
0
    if (!xEntry.is())
83
0
        return nullptr;
84
85
0
    OUString aText( xEntry->getDictionaryWord() );
86
0
    sal_Int32 nTextLen = aText.getLength();
87
88
    // trailing '=' means "hyphenation should not be possible"
89
0
    if (nTextLen <= 0  ||  aText[ nTextLen - 1 ] == '=' || aText[ nTextLen - 1 ] == '[')
90
0
        return nullptr;
91
92
0
    sal_Int16 nHyphenationPos = -1;
93
0
    sal_Int16 nOrigHyphPos = -1;
94
95
0
    OUStringBuffer aTmp( nTextLen );
96
0
    bool  bSkip = false;
97
0
    bool  bSkip2 = false;
98
0
    sal_Int32 nHyphIdx = -1;
99
0
    sal_Int32 nLeading = 0;
100
0
    for (sal_Int32 i = 0;  i < nTextLen;  i++)
101
0
    {
102
0
        sal_Unicode cTmp = aText[i];
103
0
        if (cTmp == '[' || cTmp == ']')
104
0
            bSkip2 = !bSkip2;
105
0
        if (cTmp != '=' && !bSkip2 && cTmp != ']')
106
0
        {
107
0
            aTmp.append( cTmp );
108
0
            nLeading++;
109
0
            bSkip = false;
110
0
            nHyphIdx++;
111
0
        }
112
0
        else
113
0
        {
114
0
            if (!bSkip  &&  nHyphIdx >= 0)
115
0
            {
116
0
                if (nLeading <= nMaxLeading) {
117
0
                    nHyphenationPos = static_cast<sal_Int16>(nHyphIdx);
118
0
                    nOrigHyphPos = i;
119
0
                }
120
0
            }
121
0
            bSkip = true;   //! multiple '=' should count as one only
122
0
        }
123
0
    }
124
125
0
    if (nHyphenationPos <= 0)
126
0
        return nullptr;
127
128
#if OSL_DEBUG_LEVEL > 0
129
    if (std::u16string_view(aTmp) != rOrigWord)
130
    {
131
        // both words should only differ by a having a trailing '.'
132
        // character or not...
133
        std::u16string_view aShorter(aTmp), aLonger(rOrigWord);
134
        if (aTmp.getLength() > rOrigWord.getLength())
135
            std::swap(aShorter, aLonger);
136
        sal_Int32 nS = aShorter.size();
137
        sal_Int32 nL = aLonger.size();
138
        if (nS > 0 && nL > 0)
139
        {
140
            assert( ((nS + 1 == nL) && aLonger[nL-1] == '.') && "HyphenatorDispatcher::buildHyphWord: unexpected difference between words!" );
141
        }
142
    }
143
#endif
144
0
    sal_Int32 nHyphenPos = -1;
145
0
    if (aText[ nOrigHyphPos ] == '[')  // alternative hyphenation
146
0
    {
147
0
        sal_Int16 split = 0;
148
0
        sal_Unicode c = aText [ nOrigHyphPos + 1 ];
149
0
        sal_Int32 endhyphpat = aText.indexOf( ']', nOrigHyphPos );
150
0
        if ('0' <= c && c <= '9')
151
0
        {
152
0
            split = c - '0';
153
0
            nOrigHyphPos++;
154
0
        }
155
0
        if (endhyphpat > -1)
156
0
        {
157
0
            OUStringBuffer aTmp2 ( aTmp.copy(0, std::max (nHyphenationPos + 1 - split, 0) ) );
158
0
            aTmp2.append( aText.subView( nOrigHyphPos + 1, endhyphpat - nOrigHyphPos - 1) );
159
0
            nHyphenPos = aTmp2.getLength();
160
0
            aTmp2.append( aTmp.subView( nHyphenationPos + 1 ) );
161
            //! take care of #i22591#
162
0
            if (rOrigWord[ rOrigWord.getLength() - 1 ] == '.')
163
0
                aTmp2.append( '.' );
164
0
            aText = aTmp2.makeStringAndClear();
165
0
        }
166
0
    }
167
0
    if (nHyphenPos == -1)
168
0
        aText = rOrigWord;
169
170
0
    rtl::Reference< HyphenatedWord > xRes = new HyphenatedWord( rOrigWord, nLang, nHyphenationPos,
171
0
                    aText, (nHyphenPos > -1) ? nHyphenPos - 1 : nHyphenationPos);
172
173
174
0
    return xRes;
175
0
}
176
177
178
rtl::Reference<PossibleHyphens> HyphenatorDispatcher::buildPossHyphens(
179
            const Reference< XDictionaryEntry > &xEntry, LanguageType nLanguage )
180
0
{
181
0
    MutexGuard  aGuard( GetLinguMutex() );
182
183
0
    if (!xEntry.is())
184
0
        return nullptr;
185
186
    // text with hyphenation info
187
0
    OUString aText( xEntry->getDictionaryWord() );
188
0
    sal_Int32 nTextLen = aText.getLength();
189
190
    // trailing '=' means "hyphenation should not be possible"
191
0
    if (nTextLen <= 0  ||  aText[ nTextLen - 1 ] == '=' || aText[ nTextLen - 1 ] == '[')
192
0
        return nullptr;
193
194
    // sequence to hold hyphenation positions
195
0
    Sequence< sal_Int16 > aHyphPos( nTextLen );
196
0
    sal_Int16 *pPos = aHyphPos.getArray();
197
0
    sal_Int32 nHyphCount = 0;
198
199
0
    OUStringBuffer aTmp( nTextLen );
200
0
    bool  bSkip = false;
201
0
    bool  bSkip2 = false;
202
0
    sal_Int32 nHyphIdx = -1;
203
0
    for (sal_Int32 i = 0;  i < nTextLen;  i++)
204
0
    {
205
0
        sal_Unicode cTmp = aText[i];
206
0
        if (cTmp == '[' || cTmp == ']')
207
0
            bSkip2 = !bSkip2;
208
0
        if (cTmp != '=' && !bSkip2 && cTmp != ']')
209
0
        {
210
0
            aTmp.append( cTmp );
211
0
            bSkip = false;
212
0
            nHyphIdx++;
213
0
        }
214
0
        else
215
0
        {
216
0
            if (!bSkip  &&  nHyphIdx >= 0)
217
0
                pPos[ nHyphCount++ ] = static_cast<sal_Int16>(nHyphIdx);
218
0
            bSkip = true;   //! multiple '=' should count as one only
219
0
        }
220
0
    }
221
222
    // ignore (multiple) trailing '='
223
0
    if (bSkip  &&  nHyphIdx >= 0)
224
0
    {
225
0
        nHyphCount--;
226
0
    }
227
0
    DBG_ASSERT( nHyphCount >= 0, "lng : invalid hyphenation count");
228
229
0
    if (nHyphCount <= 0)
230
0
        return nullptr;
231
232
0
    aHyphPos.realloc( nHyphCount );
233
0
    rtl::Reference<PossibleHyphens> xRes = new PossibleHyphens( aTmp.makeStringAndClear(), nLanguage,
234
0
                    aText, aHyphPos );
235
236
0
    return xRes;
237
0
}
238
239
240
Sequence< Locale > SAL_CALL HyphenatorDispatcher::getLocales()
241
0
{
242
0
    MutexGuard  aGuard( GetLinguMutex() );
243
244
0
    std::vector<Locale> aLocales;
245
0
    aLocales.reserve(aSvcMap.size());
246
247
0
    std::transform(aSvcMap.begin(), aSvcMap.end(), std::back_inserter(aLocales),
248
0
        [](HyphSvcByLangMap_t::const_reference elem) { return LanguageTag::convertToLocale(elem.first); });
249
250
0
    return comphelper::containerToSequence(aLocales);
251
0
}
252
253
254
sal_Bool SAL_CALL HyphenatorDispatcher::hasLocale(const Locale& rLocale)
255
0
{
256
0
    MutexGuard  aGuard( GetLinguMutex() );
257
0
    HyphSvcByLangMap_t::const_iterator aIt( aSvcMap.find( LinguLocaleToLanguage( rLocale ) ) );
258
0
    return aIt != aSvcMap.end();
259
0
}
260
261
262
Reference< XHyphenatedWord > SAL_CALL
263
    HyphenatorDispatcher::hyphenate(
264
            const OUString& rWord, const Locale& rLocale, sal_Int16 nMaxLeading,
265
            const css::uno::Sequence< ::css::beans::PropertyValue >& rProperties )
266
0
{
267
0
    MutexGuard  aGuard( GetLinguMutex() );
268
269
0
    Reference< XHyphenatedWord >    xRes;
270
271
0
    sal_Int32 nWordLen = rWord.getLength();
272
0
    LanguageType nLanguage = LinguLocaleToLanguage( rLocale );
273
0
    if (LinguIsUnspecified(nLanguage) || !nWordLen ||
274
0
        nMaxLeading == 0 || nMaxLeading == nWordLen)
275
0
        return xRes;
276
277
    // search for entry with that language
278
0
    HyphSvcByLangMap_t::iterator    aIt( aSvcMap.find( nLanguage ) );
279
0
    LangSvcEntries_Hyph     *pEntry = aIt != aSvcMap.end() ? aIt->second.get() : nullptr;
280
281
0
    bool bWordModified = false;
282
0
    if (!pEntry || (nMaxLeading < 0 || nMaxLeading > nWordLen))
283
0
    {
284
0
        return nullptr;
285
0
    }
286
0
    else
287
0
    {
288
0
        OUString aChkWord( rWord );
289
290
        // replace typographical apostroph by ascii apostroph
291
0
        OUString aSingleQuote( GetLocaleDataWrapper( nLanguage ).getQuotationMarkEnd() );
292
0
        DBG_ASSERT( 1 == aSingleQuote.getLength(), "unexpected length of quotation mark" );
293
0
        if (!aSingleQuote.isEmpty())
294
0
            aChkWord = aChkWord.replace( aSingleQuote[0], '\'' );
295
296
0
        bWordModified |= RemoveHyphens( aChkWord );
297
0
        if (IsIgnoreControlChars( rProperties, GetPropSet() ))
298
0
            bWordModified |= RemoveControlChars( aChkWord );
299
0
        sal_Int16 nChkMaxLeading = static_cast<sal_Int16>(GetPosInWordToCheck( rWord, nMaxLeading ));
300
301
        // check for results from (positive) dictionaries which have precedence!
302
0
        Reference< XDictionaryEntry > xEntry;
303
304
0
        if (GetDicList().is()  &&  IsUseDicList( rProperties, GetPropSet() ))
305
0
        {
306
0
            xEntry = GetDicList()->queryDictionaryEntry( aChkWord, rLocale,
307
0
                        true, false );
308
0
        }
309
310
0
        if (xEntry.is())
311
0
        {
312
            //! because queryDictionaryEntry (in the end DictionaryNeo::getEntry)
313
            //! does not distinguish between "XYZ" and "XYZ." in order to avoid
314
            //! to require them as different entry we have to supply the
315
            //! original word here as well so it can be used in th result
316
            //! otherwise a strange effect may occur (see #i22591#)
317
0
            xRes = buildHyphWord( rWord, xEntry, nLanguage, nChkMaxLeading );
318
0
        }
319
0
        else
320
0
        {
321
0
            sal_Int32 nLen = pEntry->aSvcImplNames.hasElements() ? 1 : 0;
322
0
            DBG_ASSERT( pEntry->nLastTriedSvcIndex < nLen,
323
0
                    "lng : index out of range");
324
325
0
            sal_Int32 i = 0;
326
0
            Reference< XHyphenator > xHyph;
327
0
            if (pEntry->aSvcRefs.hasElements())
328
0
                xHyph = pEntry->aSvcRefs[0];
329
330
            // try already instantiated service
331
0
            if (i <= pEntry->nLastTriedSvcIndex)
332
0
            {
333
0
                if (xHyph.is()  &&  xHyph->hasLocale( rLocale ))
334
0
                    xRes = xHyph->hyphenate( aChkWord, rLocale, nChkMaxLeading,
335
0
                                            rProperties );
336
0
                ++i;
337
0
            }
338
0
            else if (pEntry->nLastTriedSvcIndex < nLen - 1)
339
            // instantiate services and try it
340
0
            {
341
0
                Reference< XHyphenator > *pRef = pEntry->aSvcRefs.getArray();
342
343
0
                const Reference< XComponentContext >& xContext(
344
0
                    comphelper::getProcessComponentContext() );
345
346
                // build service initialization argument
347
0
                Sequence< Any > aArgs(2);
348
0
                aArgs.getArray()[0] <<= GetPropSet();
349
350
                // create specific service via it's implementation name
351
0
                try
352
0
                {
353
0
                    xHyph = Reference< XHyphenator >(
354
0
                                xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
355
0
                                    pEntry->aSvcImplNames[0], aArgs, xContext ),
356
0
                                UNO_QUERY );
357
0
                }
358
0
                catch (uno::Exception &)
359
0
                {
360
0
                    SAL_WARN( "linguistic", "createInstanceWithArguments failed" );
361
0
                }
362
0
                pRef [i] = xHyph;
363
364
0
                Reference< XLinguServiceEventBroadcaster >
365
0
                        xBroadcaster( xHyph, UNO_QUERY );
366
0
                if (xBroadcaster.is())
367
0
                    rMgr.AddLngSvcEvtBroadcaster( xBroadcaster );
368
369
0
                if (xHyph.is()  &&  xHyph->hasLocale( rLocale ))
370
0
                    xRes = xHyph->hyphenate( aChkWord, rLocale, nChkMaxLeading,
371
0
                                            rProperties );
372
373
0
                pEntry->nLastTriedSvcIndex = static_cast<sal_Int16>(i);
374
0
                ++i;
375
376
                // if language is not supported by the services
377
                // remove it from the list.
378
0
                if (xHyph.is()  &&  !xHyph->hasLocale( rLocale ))
379
0
                    aSvcMap.erase( nLanguage );
380
0
            }
381
0
        }   // if (xEntry.is())
382
0
    }
383
384
0
    if (bWordModified  &&  xRes.is())
385
0
        xRes = RebuildHyphensAndControlChars( rWord, xRes );
386
387
0
    if (xRes.is()  &&  xRes->getWord() != rWord)
388
0
    {
389
0
        xRes = new HyphenatedWord( rWord, nLanguage, xRes->getHyphenationPos(),
390
0
                                   xRes->getHyphenatedWord(),
391
0
                                   xRes->getHyphenPos() );
392
0
    }
393
394
0
    return xRes;
395
0
}
396
397
398
Reference< XHyphenatedWord > SAL_CALL
399
    HyphenatorDispatcher::queryAlternativeSpelling(
400
            const OUString& rWord, const Locale& rLocale, sal_Int16 nIndex,
401
            const css::uno::Sequence< ::css::beans::PropertyValue >& rProperties )
402
0
{
403
0
    MutexGuard  aGuard( GetLinguMutex() );
404
405
0
    Reference< XHyphenatedWord >    xRes;
406
407
0
    sal_Int32 nWordLen = rWord.getLength();
408
0
    LanguageType nLanguage = LinguLocaleToLanguage( rLocale );
409
0
    if (LinguIsUnspecified(nLanguage) || !nWordLen)
410
0
        return xRes;
411
412
    // search for entry with that language
413
0
    HyphSvcByLangMap_t::iterator    aIt( aSvcMap.find( nLanguage ) );
414
0
    LangSvcEntries_Hyph     *pEntry = aIt != aSvcMap.end() ? aIt->second.get() : nullptr;
415
416
0
    bool bWordModified = false;
417
0
    if (!pEntry || 0 > nIndex || nIndex > nWordLen - 2)
418
0
    {
419
0
        return nullptr;
420
0
    }
421
0
    else
422
0
    {
423
0
        OUString aChkWord( rWord );
424
425
        // replace typographical apostroph by ascii apostroph
426
0
        OUString aSingleQuote( GetLocaleDataWrapper( nLanguage ).getQuotationMarkEnd() );
427
0
        DBG_ASSERT( 1 == aSingleQuote.getLength(), "unexpected length of quotation mark" );
428
0
        if (!aSingleQuote.isEmpty())
429
0
            aChkWord = aChkWord.replace( aSingleQuote[0], '\'' );
430
431
0
        bWordModified |= RemoveHyphens( aChkWord );
432
0
        if (IsIgnoreControlChars( rProperties, GetPropSet() ))
433
0
            bWordModified |= RemoveControlChars( aChkWord );
434
0
        sal_Int16 nChkIndex = static_cast<sal_Int16>(GetPosInWordToCheck( rWord, nIndex ));
435
436
        // check for results from (positive) dictionaries which have precedence!
437
0
        Reference< XDictionaryEntry > xEntry;
438
439
0
        if (GetDicList().is()  &&  IsUseDicList( rProperties, GetPropSet() ))
440
0
        {
441
0
            xEntry = GetDicList()->queryDictionaryEntry( aChkWord, rLocale,
442
0
                        true, false );
443
0
        }
444
445
0
        if (xEntry.is())
446
0
        {
447
0
            xRes = buildHyphWord(aChkWord, xEntry, nLanguage, nIndex + 1);
448
0
            if (xRes.is() && xRes->isAlternativeSpelling() && xRes->getHyphenationPos() == nIndex)
449
0
                    return xRes;
450
0
        }
451
0
        else
452
0
        {
453
0
            sal_Int32 nLen = pEntry->aSvcImplNames.hasElements() ? 1 : 0;
454
0
            DBG_ASSERT( pEntry->nLastTriedSvcIndex < nLen,
455
0
                    "lng : index out of range");
456
457
0
            sal_Int32 i = 0;
458
0
            Reference< XHyphenator > xHyph;
459
0
            if (pEntry->aSvcRefs.hasElements())
460
0
                xHyph = pEntry->aSvcRefs[0];
461
462
            // try already instantiated service
463
0
            if (i <= pEntry->nLastTriedSvcIndex)
464
0
            {
465
0
                if (xHyph.is()  &&  xHyph->hasLocale( rLocale ))
466
0
                    xRes = xHyph->queryAlternativeSpelling( aChkWord, rLocale,
467
0
                                nChkIndex, rProperties );
468
0
                ++i;
469
0
            }
470
0
            else if (pEntry->nLastTriedSvcIndex < nLen - 1)
471
            // instantiate services and try it
472
0
            {
473
0
                Reference< XHyphenator > *pRef = pEntry->aSvcRefs.getArray();
474
475
0
                const Reference< XComponentContext >& xContext(
476
0
                    comphelper::getProcessComponentContext() );
477
478
                // build service initialization argument
479
0
                Sequence< Any > aArgs(2);
480
0
                aArgs.getArray()[0] <<= GetPropSet();
481
482
                // create specific service via it's implementation name
483
0
                try
484
0
                {
485
0
                    xHyph = Reference< XHyphenator >(
486
0
                                xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
487
0
                                    pEntry->aSvcImplNames[0], aArgs, xContext ), UNO_QUERY );
488
0
                }
489
0
                catch (uno::Exception &)
490
0
                {
491
0
                    SAL_WARN( "linguistic", "createInstanceWithArguments failed" );
492
0
                }
493
0
                pRef [i] = xHyph;
494
495
0
                Reference< XLinguServiceEventBroadcaster >
496
0
                        xBroadcaster( xHyph, UNO_QUERY );
497
0
                if (xBroadcaster.is())
498
0
                    rMgr.AddLngSvcEvtBroadcaster( xBroadcaster );
499
500
0
                if (xHyph.is()  &&  xHyph->hasLocale( rLocale ))
501
0
                    xRes = xHyph->queryAlternativeSpelling( aChkWord, rLocale,
502
0
                                nChkIndex, rProperties );
503
504
0
                pEntry->nLastTriedSvcIndex = static_cast<sal_Int16>(i);
505
0
                ++i;
506
507
                // if language is not supported by the services
508
                // remove it from the list.
509
0
                if (xHyph.is()  &&  !xHyph->hasLocale( rLocale ))
510
0
                    aSvcMap.erase( nLanguage );
511
0
            }
512
0
        }   // if (xEntry.is())
513
0
    }
514
515
0
    if (bWordModified  &&  xRes.is())
516
0
        xRes = RebuildHyphensAndControlChars( rWord, xRes );
517
518
0
    if (xRes.is()  &&  xRes->getWord() != rWord)
519
0
    {
520
0
        xRes = new HyphenatedWord( rWord, nLanguage, xRes->getHyphenationPos(),
521
0
                                   xRes->getHyphenatedWord(),
522
0
                                   xRes->getHyphenPos() );
523
0
    }
524
525
0
    return xRes;
526
0
}
527
528
529
Reference< XPossibleHyphens > SAL_CALL
530
    HyphenatorDispatcher::createPossibleHyphens(
531
            const OUString& rWord, const Locale& rLocale,
532
            const css::uno::Sequence< ::css::beans::PropertyValue >& rProperties )
533
0
{
534
0
    MutexGuard  aGuard( GetLinguMutex() );
535
536
0
    Reference< XPossibleHyphens >   xRes;
537
538
0
    LanguageType nLanguage = LinguLocaleToLanguage( rLocale );
539
0
    if (LinguIsUnspecified(nLanguage) || rWord.isEmpty())
540
0
        return xRes;
541
542
    // search for entry with that language
543
0
    HyphSvcByLangMap_t::iterator    aIt( aSvcMap.find( nLanguage ) );
544
0
    LangSvcEntries_Hyph     *pEntry = aIt != aSvcMap.end() ? aIt->second.get() : nullptr;
545
546
0
    if (pEntry)
547
0
    {
548
0
        OUString aChkWord( rWord );
549
550
        // replace typographical apostroph by ascii apostroph
551
0
        OUString aSingleQuote( GetLocaleDataWrapper( nLanguage ).getQuotationMarkEnd() );
552
0
        DBG_ASSERT( 1 == aSingleQuote.getLength(), "unexpected length of quotation mark" );
553
0
        if (!aSingleQuote.isEmpty())
554
0
            aChkWord = aChkWord.replace( aSingleQuote[0], '\'' );
555
556
0
        RemoveHyphens( aChkWord );
557
0
        if (IsIgnoreControlChars( rProperties, GetPropSet() ))
558
0
            RemoveControlChars( aChkWord );
559
560
        // check for results from (positive) dictionaries which have precedence!
561
0
        Reference< XDictionaryEntry > xEntry;
562
563
0
        if (GetDicList().is()  &&  IsUseDicList( rProperties, GetPropSet() ))
564
0
        {
565
0
            xEntry = GetDicList()->queryDictionaryEntry( aChkWord, rLocale,
566
0
                        true, false );
567
0
        }
568
569
0
        if (xEntry.is())
570
0
        {
571
0
            xRes = buildPossHyphens( xEntry, nLanguage );
572
0
        }
573
0
        else
574
0
        {
575
0
            sal_Int32 nLen = pEntry->aSvcImplNames.hasElements() ? 1 : 0;
576
0
            DBG_ASSERT( pEntry->nLastTriedSvcIndex < nLen,
577
0
                    "lng : index out of range");
578
579
0
            sal_Int32 i = 0;
580
0
            Reference< XHyphenator > xHyph;
581
0
            if (pEntry->aSvcRefs.hasElements())
582
0
                xHyph = pEntry->aSvcRefs[0];
583
584
            // try already instantiated service
585
0
            if (i <= pEntry->nLastTriedSvcIndex)
586
0
            {
587
0
                if (xHyph.is()  &&  xHyph->hasLocale( rLocale ))
588
0
                    xRes = xHyph->createPossibleHyphens( aChkWord, rLocale,
589
0
                                rProperties );
590
0
                ++i;
591
0
            }
592
0
            else if (pEntry->nLastTriedSvcIndex < nLen - 1)
593
            // instantiate services and try it
594
0
            {
595
0
                Reference< XHyphenator > *pRef = pEntry->aSvcRefs.getArray();
596
597
0
                const Reference< XComponentContext >& xContext(
598
0
                    comphelper::getProcessComponentContext() );
599
600
                // build service initialization argument
601
0
                Sequence< Any > aArgs(2);
602
0
                aArgs.getArray()[0] <<= GetPropSet();
603
604
                // create specific service via it's implementation name
605
0
                try
606
0
                {
607
0
                    xHyph.set( xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
608
0
                                   pEntry->aSvcImplNames[0], aArgs, xContext ),
609
0
                               UNO_QUERY );
610
0
                }
611
0
                catch (uno::Exception &)
612
0
                {
613
0
                    SAL_WARN( "linguistic", "createWithArguments failed" );
614
0
                }
615
0
                pRef [i] = xHyph;
616
617
0
                Reference< XLinguServiceEventBroadcaster >
618
0
                        xBroadcaster( xHyph, UNO_QUERY );
619
0
                if (xBroadcaster.is())
620
0
                    rMgr.AddLngSvcEvtBroadcaster( xBroadcaster );
621
622
0
                if (xHyph.is()  &&  xHyph->hasLocale( rLocale ))
623
0
                    xRes = xHyph->createPossibleHyphens( aChkWord, rLocale, rProperties );
624
625
0
                pEntry->nLastTriedSvcIndex = static_cast<sal_Int16>(i);
626
0
                ++i;
627
628
                // if language is not supported by the services
629
                // remove it from the list.
630
0
                if (xHyph.is()  &&  !xHyph->hasLocale( rLocale ))
631
0
                    aSvcMap.erase( nLanguage );
632
0
            }
633
0
        }   // if (xEntry.is())
634
0
    }
635
636
0
    if (xRes.is()  &&  xRes->getWord() != rWord)
637
0
    {
638
0
        xRes = new PossibleHyphens( rWord, nLanguage,
639
0
                                    xRes->getPossibleHyphens(),
640
0
                                    xRes->getHyphenationPositions() );
641
0
    }
642
643
0
    return xRes;
644
0
}
645
646
647
void HyphenatorDispatcher::SetServiceList( const Locale &rLocale,
648
        const Sequence< OUString > &rSvcImplNames )
649
0
{
650
0
    MutexGuard  aGuard( GetLinguMutex() );
651
652
0
    LanguageType nLanguage = LinguLocaleToLanguage( rLocale );
653
654
0
    if (!rSvcImplNames.hasElements())
655
        // remove entry
656
0
        aSvcMap.erase( nLanguage );
657
0
    else
658
0
    {
659
        // modify/add entry
660
0
        LangSvcEntries_Hyph *pEntry = aSvcMap[ nLanguage ].get();
661
0
        if (pEntry)
662
0
        {
663
0
            pEntry->Clear();
664
0
            pEntry->aSvcImplNames = rSvcImplNames;
665
0
            pEntry->aSvcImplNames.realloc(1);
666
0
            pEntry->aSvcRefs  = Sequence< Reference < XHyphenator > > ( 1 );
667
0
        }
668
0
        else
669
0
        {
670
0
            auto pTmpEntry = std::make_shared<LangSvcEntries_Hyph>( rSvcImplNames[0] );
671
0
            pTmpEntry->aSvcRefs = Sequence< Reference < XHyphenator > >( 1 );
672
0
            aSvcMap[ nLanguage ] = std::move(pTmpEntry);
673
0
        }
674
0
    }
675
0
}
676
677
678
Sequence< OUString >
679
    HyphenatorDispatcher::GetServiceList( const Locale &rLocale ) const
680
0
{
681
0
    MutexGuard  aGuard( GetLinguMutex() );
682
683
0
    Sequence< OUString > aRes;
684
685
    // search for entry with that language and use data from that
686
0
    LanguageType nLanguage = LinguLocaleToLanguage( rLocale );
687
0
    const HyphSvcByLangMap_t::const_iterator  aIt( aSvcMap.find( nLanguage ) );
688
0
    const LangSvcEntries_Hyph       *pEntry = aIt != aSvcMap.end() ? aIt->second.get() : nullptr;
689
0
    if (pEntry)
690
0
    {
691
0
        aRes = pEntry->aSvcImplNames;
692
0
        if (aRes.hasElements())
693
0
            aRes.realloc(1);
694
0
    }
695
696
0
    return aRes;
697
0
}
698
699
700
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */