Coverage Report

Created: 2025-07-07 10:01

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