xref: /trunk/main/linguistic/source/lngsvcmgr.cxx (revision 1ecadb572e7010ff3b3382ad9bf179dbc6efadbb)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_linguistic.hxx"
30 
31 #include <com/sun/star/registry/XRegistryKey.hpp>
32 #include <com/sun/star/container/XContentEnumerationAccess.hpp>
33 #include <com/sun/star/container/XEnumeration.hpp>
34 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
35 #include <com/sun/star/linguistic2/XSupportedLocales.hpp>
36 #include <com/sun/star/linguistic2/DictionaryListEventFlags.hpp>
37 #include <com/sun/star/linguistic2/LinguServiceEventFlags.hpp>
38 
39 #include <tools/solar.h>
40 #include <unotools/lingucfg.hxx>
41 #include <unotools/processfactory.hxx>
42 #include <i18npool/lang.h>
43 #include <i18npool/mslangid.hxx>
44 #include <cppuhelper/factory.hxx>
45 #include <comphelper/extract.hxx>
46 #include <rtl/logfile.hxx>
47 
48 #include <boost/checked_delete.hpp>
49 
50 #include "lngsvcmgr.hxx"
51 #include "lngopt.hxx"
52 #include "linguistic/misc.hxx"
53 #include "spelldsp.hxx"
54 #include "hyphdsp.hxx"
55 #include "thesdsp.hxx"
56 #include "gciterator.hxx"
57 
58 
59 using namespace com::sun::star;
60 using namespace linguistic;
61 using ::rtl::OUString;
62 
63 // forward declarations
64 uno::Sequence< OUString > static GetLangSvcList( const uno::Any &rVal );
65 uno::Sequence< OUString > static GetLangSvc( const uno::Any &rVal );
66 
67 ///////////////////////////////////////////////////////////////////////////
68 
69 static sal_Bool lcl_SeqHasString( const uno::Sequence< OUString > &rSeq, const OUString &rText )
70 {
71     sal_Bool bRes = sal_False;
72 
73     sal_Int32 nLen = rSeq.getLength();
74     if (nLen == 0 || rText.getLength() == 0)
75         return bRes;
76 
77     const OUString *pSeq = rSeq.getConstArray();
78     for (sal_Int32 i = 0;  i < nLen  &&  !bRes;  ++i)
79     {
80         if (rText == pSeq[i])
81             bRes = sal_True;
82     }
83     return bRes;
84 }
85 
86 ///////////////////////////////////////////////////////////////////////////
87 
88 static uno::Sequence< lang::Locale > GetAvailLocales(
89         const uno::Sequence< OUString > &rSvcImplNames )
90 {
91     uno::Sequence< lang::Locale > aRes;
92 
93     uno::Reference< lang::XMultiServiceFactory >  xFac( utl::getProcessServiceFactory() );
94     sal_Int32 nNames = rSvcImplNames.getLength();
95     if (nNames  &&  xFac.is())
96     {
97         std::set< LanguageType > aLanguages;
98 
99         //! since we're going to create one-instance services we have to
100         //! supply their arguments even if we would not need them here...
101         uno::Sequence< uno::Any > aArgs(2);
102         aArgs.getArray()[0] <<= GetLinguProperties();
103 
104         // check all services for the supported languages and new
105         // languages to the result
106         const OUString *pImplNames = rSvcImplNames.getConstArray();
107         sal_Int32 i;
108 
109         for (i = 0;  i < nNames;  ++i)
110         {
111             uno::Reference< linguistic2::XSupportedLocales > xSuppLoc;
112             try
113             {
114                 xSuppLoc = uno::Reference< linguistic2::XSupportedLocales >(
115                         xFac->createInstanceWithArguments( pImplNames[i], aArgs ), uno::UNO_QUERY );
116             }
117             catch (uno::Exception &)
118             {
119                 DBG_ASSERT( 0, "createInstanceWithArguments failed" );
120             }
121 
122             if (xSuppLoc.is())
123             {
124                 uno::Sequence< lang::Locale > aLoc( xSuppLoc->getLocales() );
125                 sal_Int32 nLoc = aLoc.getLength();
126                 for (sal_Int32 k = 0;  k < nLoc;  ++k)
127                 {
128                     const lang::Locale *pLoc = aLoc.getConstArray();
129                     LanguageType nLang = LocaleToLanguage( pLoc[k] );
130 
131                     // language not already added?
132                     if (aLanguages.find( nLang ) == aLanguages.end())
133                         aLanguages.insert( nLang );
134                 }
135             }
136             else
137             {
138                 DBG_ASSERT( 0, "interface not supported by service" );
139             }
140         }
141 
142         // build return sequence
143         sal_Int32 nLanguages = static_cast< sal_Int32 >(aLanguages.size());
144         aRes.realloc( nLanguages );
145         lang::Locale *pRes = aRes.getArray();
146         std::set< LanguageType >::const_iterator aIt( aLanguages.begin() );
147         for (i = 0;  aIt != aLanguages.end();  ++aIt, ++i)
148         {
149             LanguageType nLang = *aIt;
150             pRes[i] = CreateLocale( nLang );
151         }
152     }
153 
154     return aRes;
155 }
156 
157 ///////////////////////////////////////////////////////////////////////////
158 
159 struct SvcInfo
160 {
161     const OUString                  aSvcImplName;
162     const uno::Sequence< sal_Int16 >    aSuppLanguages;
163 
164     SvcInfo( const OUString &rSvcImplName,
165              const uno::Sequence< sal_Int16 >  &rSuppLanguages ) :
166         aSvcImplName    (rSvcImplName),
167         aSuppLanguages  (rSuppLanguages)
168     {
169     }
170 
171     sal_Bool    HasLanguage( sal_Int16 nLanguage ) const;
172 };
173 
174 
175 sal_Bool SvcInfo::HasLanguage( sal_Int16 nLanguage ) const
176 {
177     sal_Int32 nCnt = aSuppLanguages.getLength();
178     const sal_Int16 *pLang = aSuppLanguages.getConstArray();
179     sal_Int32 i;
180 
181     for ( i = 0;  i < nCnt;  ++i)
182     {
183         if (nLanguage == pLang[i])
184             break;
185     }
186     return i < nCnt;
187 }
188 
189 
190 ///////////////////////////////////////////////////////////////////////////
191 
192 
193 void LngSvcMgr::SetAvailableCfgServiceLists( LinguDispatcher &rDispatcher,
194         const SvcInfoArray &rAvailSvcs )
195 {
196     // get list of nodenames to look at for their service list
197     const char *pEntryName = 0;
198     sal_Bool bHasLangSvcList = sal_True;
199     switch (rDispatcher.GetDspType())
200     {
201         case LinguDispatcher::DSP_SPELL     : pEntryName = "ServiceManager/SpellCheckerList";    break;
202         case LinguDispatcher::DSP_GRAMMAR   : pEntryName = "ServiceManager/GrammarCheckerList";
203                                               bHasLangSvcList = sal_False;
204                                               break;
205         case LinguDispatcher::DSP_HYPH      : pEntryName = "ServiceManager/HyphenatorList";
206                                               bHasLangSvcList = sal_False;
207                                               break;
208         case LinguDispatcher::DSP_THES      : pEntryName = "ServiceManager/ThesaurusList";  break;
209         default :
210             DBG_ASSERT( 0, "unexpected case" );
211     }
212     String  aNode( String::CreateFromAscii( pEntryName ) );
213     uno::Sequence < OUString > aNodeNames( /*aCfg.*/GetNodeNames( aNode ) );
214 
215 
216     sal_Int32 nLen = aNodeNames.getLength();
217     const OUString *pNodeNames = aNodeNames.getConstArray();
218     for (sal_Int32 i = 0;  i < nLen;  ++i)
219     {
220         uno::Sequence< OUString >   aSvcImplNames;
221 
222         uno::Sequence< OUString >    aNames( 1 );
223         OUString *pNames = aNames.getArray();
224 
225         OUString aPropName( aNode );
226         aPropName += OUString::valueOf( (sal_Unicode) '/' );
227         aPropName += pNodeNames[i];
228         pNames[0] = aPropName;
229 
230         uno::Sequence< uno::Any > aValues = /*aCfg.*/GetProperties( aNames );
231         if (aValues.getLength())
232         {
233             // get list of configured service names for the
234             // current node (language)
235             const uno::Any &rValue = aValues.getConstArray()[0];
236             if (bHasLangSvcList)
237                 aSvcImplNames = GetLangSvcList( rValue );
238             else
239                 aSvcImplNames = GetLangSvc( rValue );
240 
241             sal_Int32 nSvcs = aSvcImplNames.getLength();
242             if (nSvcs)
243             {
244                 const OUString *pImplNames = aSvcImplNames.getConstArray();
245 
246                 LanguageType nLang = MsLangId::convertIsoStringToLanguage( pNodeNames[i] );
247 
248                 // build list of available services from those
249                 sal_Int32 nCnt = 0;
250                 uno::Sequence< OUString > aAvailSvcs( nSvcs );
251                 OUString *pAvailSvcs = aAvailSvcs.getArray();
252                 for (sal_Int32 k = 0;  k < nSvcs;  ++k)
253                 {
254                     // check for availability of the service
255                     size_t nAvailSvcs = rAvailSvcs.size();
256                     for (size_t m = 0;  m < nAvailSvcs;  ++m)
257                     {
258                         const SvcInfo &rSvcInfo = *rAvailSvcs[m];
259                         if (rSvcInfo.aSvcImplName == pImplNames[k]  &&
260                             rSvcInfo.HasLanguage( nLang ))
261                         {
262                             pAvailSvcs[ nCnt++ ] = rSvcInfo.aSvcImplName;
263                             break;
264                         }
265                     }
266                 }
267 
268                 if (nCnt)
269                 {
270                     aAvailSvcs.realloc( nCnt );
271                     rDispatcher.SetServiceList( CreateLocale( nLang ), aAvailSvcs );
272                 }
273             }
274         }
275     }
276 }
277 
278 
279 ///////////////////////////////////////////////////////////////////////////
280 
281 
282 class LngSvcMgrListenerHelper :
283     public cppu::WeakImplHelper2
284     <
285         linguistic2::XLinguServiceEventListener,
286         linguistic2::XDictionaryListEventListener
287     >
288 {
289     LngSvcMgr  &rMyManager;
290 //    Timer       aLaunchTimer;
291 
292     //cppu::OMultiTypeInterfaceContainerHelper  aListeners;
293     ::cppu::OInterfaceContainerHelper           aLngSvcMgrListeners;
294     ::cppu::OInterfaceContainerHelper           aLngSvcEvtBroadcasters;
295     uno::Reference< linguistic2::XDictionaryList >               xDicList;
296     uno::Reference< uno::XInterface >                        xMyEvtObj;
297 
298     sal_Int16   nCombinedLngSvcEvt;
299 
300     // disallow copy-constructor and assignment-operator for now
301     LngSvcMgrListenerHelper(const LngSvcMgrListenerHelper &);
302     LngSvcMgrListenerHelper & operator = (const LngSvcMgrListenerHelper &);
303 
304     void    LaunchEvent( sal_Int16 nLngSvcEvtFlags );
305 
306 //  DECL_LINK( TimeOut, Timer* );
307     long Timeout();
308 
309 public:
310     LngSvcMgrListenerHelper( LngSvcMgr &rLngSvcMgr,
311             const uno::Reference< uno::XInterface > &rxSource,
312             const uno::Reference< linguistic2::XDictionaryList > &rxDicList );
313 
314     // lang::XEventListener
315     virtual void SAL_CALL
316         disposing( const lang::EventObject& rSource )
317             throw(uno::RuntimeException);
318 
319     // linguistic2::XLinguServiceEventListener
320     virtual void SAL_CALL
321         processLinguServiceEvent( const linguistic2::LinguServiceEvent& aLngSvcEvent )
322             throw(uno::RuntimeException);
323 
324     // linguistic2::XDictionaryListEventListener
325     virtual void SAL_CALL
326         processDictionaryListEvent(
327                 const linguistic2::DictionaryListEvent& rDicListEvent )
328             throw(uno::RuntimeException);
329 
330     inline  sal_Bool    AddLngSvcMgrListener(
331                         const uno::Reference< lang::XEventListener >& rxListener );
332     inline  sal_Bool    RemoveLngSvcMgrListener(
333                         const uno::Reference< lang::XEventListener >& rxListener );
334     void    DisposeAndClear( const lang::EventObject &rEvtObj );
335     sal_Bool    AddLngSvcEvtBroadcaster(
336                         const uno::Reference< linguistic2::XLinguServiceEventBroadcaster > &rxBroadcaster );
337     sal_Bool    RemoveLngSvcEvtBroadcaster(
338                         const uno::Reference< linguistic2::XLinguServiceEventBroadcaster > &rxBroadcaster );
339 
340     void    AddLngSvcEvt( sal_Int16 nLngSvcEvt );
341 };
342 
343 
344 LngSvcMgrListenerHelper::LngSvcMgrListenerHelper(
345         LngSvcMgr &rLngSvcMgr,
346         const uno::Reference< uno::XInterface > &rxSource,
347         const uno::Reference< linguistic2::XDictionaryList > &rxDicList  ) :
348     rMyManager              ( rLngSvcMgr ),
349     aLngSvcMgrListeners     ( GetLinguMutex() ),
350     aLngSvcEvtBroadcasters  ( GetLinguMutex() ),
351     xDicList                ( rxDicList ),
352     xMyEvtObj               ( rxSource )
353 {
354     if (xDicList.is())
355     {
356         xDicList->addDictionaryListEventListener(
357             (linguistic2::XDictionaryListEventListener *) this, sal_False );
358     }
359 
360     //! The timer is used to 'sum up' different events in order to reduce the
361     //! number of events forwarded.
362     //! (This may happen already if a property was changed that has several
363     //! listeners, and each of them is launching an event of it's own!)
364     //! Thus this behaviour is necessary to avoid unecessary actions of
365     //! this objects listeners!
366 //  aLaunchTimer.SetTimeout( 2000 );
367 //  aLaunchTimer.SetTimeoutHdl( LINK( this, LngSvcMgrListenerHelper, TimeOut ) );
368     nCombinedLngSvcEvt = 0;
369 }
370 
371 
372 void SAL_CALL LngSvcMgrListenerHelper::disposing( const lang::EventObject& rSource )
373         throw(uno::RuntimeException)
374 {
375     osl::MutexGuard aGuard( GetLinguMutex() );
376 
377     uno::Reference< uno::XInterface > xRef( rSource.Source );
378     if ( xRef.is() )
379     {
380         aLngSvcMgrListeners   .removeInterface( xRef );
381         aLngSvcEvtBroadcasters.removeInterface( xRef );
382         if (xDicList == xRef)
383             xDicList = 0;
384     }
385 }
386 
387 
388 //IMPL_LINK( LngSvcMgrListenerHelper, TimeOut, Timer*, pTimer )
389 long LngSvcMgrListenerHelper::Timeout()
390 {
391     osl::MutexGuard aGuard( GetLinguMutex() );
392 
393 //  if (&aLaunchTimer == pTimer)
394     {
395         // change event source to LinguServiceManager since the listeners
396         // probably do not know (and need not to know) about the specific
397         // SpellChecker's or Hyphenator's.
398         linguistic2::LinguServiceEvent aEvtObj( xMyEvtObj, nCombinedLngSvcEvt );
399         nCombinedLngSvcEvt = 0;
400 
401         if (rMyManager.pSpellDsp)
402             rMyManager.pSpellDsp->FlushSpellCache();
403 
404         // pass event on to linguistic2::XLinguServiceEventListener's
405         cppu::OInterfaceIteratorHelper aIt( aLngSvcMgrListeners );
406         while (aIt.hasMoreElements())
407         {
408             uno::Reference< linguistic2::XLinguServiceEventListener > xRef( aIt.next(), uno::UNO_QUERY );
409             if (xRef.is())
410                 xRef->processLinguServiceEvent( aEvtObj );
411         }
412     }
413     return 0;
414 }
415 
416 
417 void LngSvcMgrListenerHelper::AddLngSvcEvt( sal_Int16 nLngSvcEvt )
418 {
419     nCombinedLngSvcEvt |= nLngSvcEvt;
420 //  aLaunchTimer.Start();
421     Timeout();
422 }
423 
424 
425 void SAL_CALL
426     LngSvcMgrListenerHelper::processLinguServiceEvent(
427             const linguistic2::LinguServiceEvent& rLngSvcEvent )
428         throw(uno::RuntimeException)
429 {
430     osl::MutexGuard aGuard( GetLinguMutex() );
431     AddLngSvcEvt( rLngSvcEvent.nEvent );
432 }
433 
434 
435 void SAL_CALL
436     LngSvcMgrListenerHelper::processDictionaryListEvent(
437             const linguistic2::DictionaryListEvent& rDicListEvent )
438         throw(uno::RuntimeException)
439 {
440     osl::MutexGuard aGuard( GetLinguMutex() );
441 
442     sal_Int16 nDlEvt = rDicListEvent.nCondensedEvent;
443     if (0 == nDlEvt)
444         return;
445 
446     // we do keep the original event source here though...
447 
448     // pass event on to linguistic2::XDictionaryListEventListener's
449     cppu::OInterfaceIteratorHelper aIt( aLngSvcMgrListeners );
450     while (aIt.hasMoreElements())
451     {
452         uno::Reference< linguistic2::XDictionaryListEventListener > xRef( aIt.next(), uno::UNO_QUERY );
453         if (xRef.is())
454             xRef->processDictionaryListEvent( rDicListEvent );
455     }
456 
457     //
458     // "translate" DictionaryList event into linguistic2::LinguServiceEvent
459     //
460     sal_Int16 nLngSvcEvt = 0;
461     //
462     sal_Int16 nSpellCorrectFlags =
463             linguistic2::DictionaryListEventFlags::ADD_NEG_ENTRY        |
464             linguistic2::DictionaryListEventFlags::DEL_POS_ENTRY        |
465             linguistic2::DictionaryListEventFlags::ACTIVATE_NEG_DIC |
466             linguistic2::DictionaryListEventFlags::DEACTIVATE_POS_DIC;
467     if (0 != (nDlEvt & nSpellCorrectFlags))
468         nLngSvcEvt |= linguistic2::LinguServiceEventFlags::SPELL_CORRECT_WORDS_AGAIN;
469     //
470     sal_Int16 nSpellWrongFlags =
471             linguistic2::DictionaryListEventFlags::ADD_POS_ENTRY        |
472             linguistic2::DictionaryListEventFlags::DEL_NEG_ENTRY        |
473             linguistic2::DictionaryListEventFlags::ACTIVATE_POS_DIC |
474             linguistic2::DictionaryListEventFlags::DEACTIVATE_NEG_DIC;
475     if (0 != (nDlEvt & nSpellWrongFlags))
476         nLngSvcEvt |= linguistic2::LinguServiceEventFlags::SPELL_WRONG_WORDS_AGAIN;
477     //
478     sal_Int16 nHyphenateFlags =
479             linguistic2::DictionaryListEventFlags::ADD_POS_ENTRY        |
480             linguistic2::DictionaryListEventFlags::DEL_POS_ENTRY        |
481             linguistic2::DictionaryListEventFlags::ACTIVATE_POS_DIC |
482             linguistic2::DictionaryListEventFlags::ACTIVATE_NEG_DIC;
483     if (0 != (nDlEvt & nHyphenateFlags))
484         nLngSvcEvt |= linguistic2::LinguServiceEventFlags::HYPHENATE_AGAIN;
485 
486     if (rMyManager.pSpellDsp)
487         rMyManager.pSpellDsp->FlushSpellCache();
488     if (nLngSvcEvt)
489         LaunchEvent( nLngSvcEvt );
490 }
491 
492 
493 void LngSvcMgrListenerHelper::LaunchEvent( sal_Int16 nLngSvcEvtFlags )
494 {
495     linguistic2::LinguServiceEvent aEvt( xMyEvtObj, nLngSvcEvtFlags );
496 
497     // pass event on to linguistic2::XLinguServiceEventListener's
498     cppu::OInterfaceIteratorHelper aIt( aLngSvcMgrListeners );
499     while (aIt.hasMoreElements())
500     {
501         uno::Reference< linguistic2::XLinguServiceEventListener > xRef( aIt.next(), uno::UNO_QUERY );
502         if (xRef.is())
503             xRef->processLinguServiceEvent( aEvt );
504     }
505 }
506 
507 
508 inline sal_Bool LngSvcMgrListenerHelper::AddLngSvcMgrListener(
509         const uno::Reference< lang::XEventListener >& rxListener )
510 {
511     aLngSvcMgrListeners.addInterface( rxListener );
512     return sal_True;
513 }
514 
515 
516 inline sal_Bool LngSvcMgrListenerHelper::RemoveLngSvcMgrListener(
517         const uno::Reference< lang::XEventListener >& rxListener )
518 {
519     aLngSvcMgrListeners.removeInterface( rxListener );
520     return sal_True;
521 }
522 
523 
524 void LngSvcMgrListenerHelper::DisposeAndClear( const lang::EventObject &rEvtObj )
525 {
526     // call "disposing" for all listeners and clear list
527     aLngSvcMgrListeners   .disposeAndClear( rEvtObj );
528 
529     // remove references to this object hold by the broadcasters
530     cppu::OInterfaceIteratorHelper aIt( aLngSvcEvtBroadcasters );
531     while (aIt.hasMoreElements())
532     {
533         uno::Reference< linguistic2::XLinguServiceEventBroadcaster > xRef( aIt.next(), uno::UNO_QUERY );
534         if (xRef.is())
535             RemoveLngSvcEvtBroadcaster( xRef );
536     }
537 
538     // remove refernce to this object hold by the dictionary-list
539     if (xDicList.is())
540     {
541         xDicList->removeDictionaryListEventListener(
542             (linguistic2::XDictionaryListEventListener *) this );
543         xDicList = 0;
544     }
545 }
546 
547 
548 sal_Bool LngSvcMgrListenerHelper::AddLngSvcEvtBroadcaster(
549         const uno::Reference< linguistic2::XLinguServiceEventBroadcaster > &rxBroadcaster )
550 {
551     sal_Bool bRes = sal_False;
552     if (rxBroadcaster.is())
553     {
554         aLngSvcEvtBroadcasters.addInterface( rxBroadcaster );
555         rxBroadcaster->addLinguServiceEventListener(
556                 (linguistic2::XLinguServiceEventListener *) this );
557     }
558     return bRes;
559 }
560 
561 
562 sal_Bool LngSvcMgrListenerHelper::RemoveLngSvcEvtBroadcaster(
563         const uno::Reference< linguistic2::XLinguServiceEventBroadcaster > &rxBroadcaster )
564 {
565     sal_Bool bRes = sal_False;
566     if (rxBroadcaster.is())
567     {
568         aLngSvcEvtBroadcasters.removeInterface( rxBroadcaster );
569         rxBroadcaster->removeLinguServiceEventListener(
570                 (linguistic2::XLinguServiceEventListener *) this );
571     }
572     return bRes;
573 }
574 
575 
576 ///////////////////////////////////////////////////////////////////////////
577 
578 
579 LngSvcMgr::LngSvcMgr() :
580     utl::ConfigItem( String::CreateFromAscii( "Office.Linguistic" ) ),
581     aEvtListeners   ( GetLinguMutex() )
582 {
583     bHasAvailSpellLocales   =
584     bHasAvailGrammarLocales =
585     bHasAvailHyphLocales    =
586     bHasAvailThesLocales    =
587     bDisposing = sal_False;
588 
589     pSpellDsp   = 0;
590     pGrammarDsp = 0;
591     pHyphDsp    = 0;
592     pThesDsp    = 0;
593 
594     pAvailSpellSvcs     = 0;
595     pAvailGrammarSvcs   = 0;
596     pAvailHyphSvcs      = 0;
597     pAvailThesSvcs      = 0;
598     pListenerHelper     = 0;
599 
600     // request notify events when properties (i.e. something in the subtree) changes
601     uno::Sequence< OUString > aNames(4);
602     OUString *pNames = aNames.getArray();
603     pNames[0] = A2OU( "ServiceManager/SpellCheckerList" );
604     pNames[1] = A2OU( "ServiceManager/GrammarCheckerList" );
605     pNames[2] = A2OU( "ServiceManager/HyphenatorList" );
606     pNames[3] = A2OU( "ServiceManager/ThesaurusList" );
607     EnableNotification( aNames );
608 }
609 
610 void LngSvcMgr::clearSvcInfoArray(SvcInfoArray* pInfo)
611 {
612     if (pInfo)
613     {
614         std::for_each(pInfo->begin(), pInfo->end(), boost::checked_deleter<SvcInfo>());
615         delete pInfo;
616     }
617 }
618 
619 LngSvcMgr::~LngSvcMgr()
620 {
621     // memory for pSpellDsp, pHyphDsp, pThesDsp, pListenerHelper
622     // will be freed in the destructor of the respective Reference's
623     // xSpellDsp, xGrammarDsp, xHyphDsp, xThesDsp
624 
625     clearSvcInfoArray(pAvailSpellSvcs);
626     clearSvcInfoArray(pAvailGrammarSvcs);
627     clearSvcInfoArray(pAvailHyphSvcs);
628     clearSvcInfoArray(pAvailThesSvcs);
629 }
630 
631 
632 void LngSvcMgr::Notify( const uno::Sequence< OUString > &rPropertyNames )
633 {
634     const OUString aSpellCheckerList( A2OU("ServiceManager/SpellCheckerList") );
635     const OUString aGrammarCheckerList( A2OU("ServiceManager/GrammarCheckerList") );
636     const OUString aHyphenatorList( A2OU("ServiceManager/HyphenatorList") );
637     const OUString aThesaurusList( A2OU("ServiceManager/ThesaurusList") );
638 
639     const uno::Sequence< OUString > aSpellCheckerListEntries( GetNodeNames( aSpellCheckerList ) );
640     const uno::Sequence< OUString > aGrammarCheckerListEntries( GetNodeNames( aGrammarCheckerList ) );
641     const uno::Sequence< OUString > aHyphenatorListEntries( GetNodeNames( aHyphenatorList ) );
642     const uno::Sequence< OUString > aThesaurusListEntries( GetNodeNames( aThesaurusList ) );
643 
644     uno::Sequence< uno::Any > aValues;
645     uno::Sequence< OUString > aNames( 1 );
646     OUString *pNames = aNames.getArray();
647 
648     sal_Int32 nLen = rPropertyNames.getLength();
649     const OUString *pPropertyNames = rPropertyNames.getConstArray();
650     for (sal_Int32 i = 0;  i < nLen;  ++i)
651     {
652         // property names look like
653         // "ServiceManager/ThesaurusList/de-CH"
654 
655         const OUString &rName = pPropertyNames[i];
656         sal_Int32 nKeyStart;
657         nKeyStart = rName.lastIndexOf( '/' );
658         OUString aKeyText;
659         if (nKeyStart != -1)
660             aKeyText = rName.copy( nKeyStart + 1 );
661         DBG_ASSERT( aKeyText.getLength() != 0, "unexpected key (lang::Locale) string" );
662         if (0 == rName.compareTo( aSpellCheckerList, aSpellCheckerList.getLength() ))
663         {
664             // delete old cached data, needs to be acquired new on demand
665             clearSvcInfoArray(pAvailSpellSvcs);     pAvailSpellSvcs = 0;
666 
667             OUString aNode( aSpellCheckerList );
668             if (lcl_SeqHasString( aSpellCheckerListEntries, aKeyText ))
669             {
670                 OUString aPropName( aNode );
671                 aPropName += OUString::valueOf( (sal_Unicode) '/' );
672                 aPropName += aKeyText;
673                 pNames[0] = aPropName;
674                 aValues = /*aCfg.*/GetProperties( aNames );
675                 uno::Sequence< OUString > aSvcImplNames;
676                 if (aValues.getLength())
677                     aSvcImplNames = GetLangSvcList( aValues.getConstArray()[0] );
678 
679                 LanguageType nLang = LANGUAGE_NONE;
680                 if (0 != aKeyText.getLength())
681                     nLang = MsLangId::convertIsoStringToLanguage( aKeyText );
682 
683                 GetSpellCheckerDsp_Impl( sal_False );     // don't set service list, it will be done below
684                 pSpellDsp->SetServiceList( CreateLocale(nLang), aSvcImplNames );
685             }
686         }
687         else if (0 == rName.compareTo( aGrammarCheckerList, aGrammarCheckerList.getLength() ))
688         {
689             // delete old cached data, needs to be acquired new on demand
690             clearSvcInfoArray(pAvailGrammarSvcs);      pAvailGrammarSvcs = 0;
691 
692             OUString aNode( aGrammarCheckerList );
693             if (lcl_SeqHasString( aGrammarCheckerListEntries, aKeyText ))
694             {
695                 OUString aPropName( aNode );
696                 aPropName += OUString::valueOf( (sal_Unicode) '/' );
697                 aPropName += aKeyText;
698                 pNames[0] = aPropName;
699                 aValues = /*aCfg.*/GetProperties( aNames );
700                 uno::Sequence< OUString > aSvcImplNames;
701                 if (aValues.getLength())
702                     aSvcImplNames = GetLangSvc( aValues.getConstArray()[0] );
703 
704                 LanguageType nLang = LANGUAGE_NONE;
705                 if (0 != aKeyText.getLength())
706                     nLang = MsLangId::convertIsoStringToLanguage( aKeyText );
707 
708                 if (SvtLinguConfig().HasGrammarChecker())
709                 {
710                     GetGrammarCheckerDsp_Impl( sal_False );   // don't set service list, it will be done below
711                     pGrammarDsp->SetServiceList( CreateLocale(nLang), aSvcImplNames );
712                 }
713             }
714         }
715         else if (0 == rName.compareTo( aHyphenatorList, aHyphenatorList.getLength() ))
716         {
717             // delete old cached data, needs to be acquired new on demand
718             clearSvcInfoArray(pAvailHyphSvcs);      pAvailHyphSvcs = 0;
719 
720             OUString aNode( aHyphenatorList );
721             if (lcl_SeqHasString( aHyphenatorListEntries, aKeyText ))
722             {
723                 OUString aPropName( aNode );
724                 aPropName += OUString::valueOf( (sal_Unicode) '/' );
725                 aPropName += aKeyText;
726                 pNames[0] = aPropName;
727                 aValues = /*aCfg.*/GetProperties( aNames );
728                 uno::Sequence< OUString > aSvcImplNames;
729                 if (aValues.getLength())
730                     aSvcImplNames = GetLangSvc( aValues.getConstArray()[0] );
731 
732                 LanguageType nLang = LANGUAGE_NONE;
733                 if (0 != aKeyText.getLength())
734                     nLang = MsLangId::convertIsoStringToLanguage( aKeyText );
735 
736                 GetHyphenatorDsp_Impl( sal_False );   // don't set service list, it will be done below
737                 pHyphDsp->SetServiceList( CreateLocale(nLang), aSvcImplNames );
738             }
739         }
740         else if (0 == rName.compareTo( aThesaurusList, aThesaurusList.getLength() ))
741         {
742             // delete old cached data, needs to be acquired new on demand
743             clearSvcInfoArray(pAvailThesSvcs);      pAvailThesSvcs = 0;
744 
745             OUString aNode( aThesaurusList );
746             if (lcl_SeqHasString( aThesaurusListEntries, aKeyText ))
747             {
748                 OUString aPropName( aNode );
749                 aPropName += OUString::valueOf( (sal_Unicode) '/' );
750                 aPropName += aKeyText;
751                 pNames[0] = aPropName;
752                 aValues = /*aCfg.*/GetProperties( aNames );
753                 uno::Sequence< OUString > aSvcImplNames;
754                 if (aValues.getLength())
755                     aSvcImplNames = GetLangSvcList( aValues.getConstArray()[0] );
756 
757                 LanguageType nLang = LANGUAGE_NONE;
758                 if (0 != aKeyText.getLength())
759                     nLang = MsLangId::convertIsoStringToLanguage( aKeyText );
760 
761                 GetThesaurusDsp_Impl( sal_False );  // don't set service list, it will be done below
762                 pThesDsp->SetServiceList( CreateLocale(nLang), aSvcImplNames );
763             }
764         }
765         else
766         {
767             DBG_ASSERT( 0, "nofified for unexpected property" );
768         }
769     }
770 }
771 
772 
773 void LngSvcMgr::Commit()
774 {
775     // everything necessary should have already been done by 'SaveCfgSvcs'
776     // called from within 'setConfiguredServices'.
777     // Also this class usually exits only when the Office i sbeing shutdown.
778 }
779 
780 
781 void LngSvcMgr::GetListenerHelper_Impl()
782 {
783     if (!pListenerHelper)
784     {
785         pListenerHelper = new LngSvcMgrListenerHelper( *this,
786                 (XLinguServiceManager *) this, linguistic::GetDictionaryList() );
787         xListenerHelper = (linguistic2::XLinguServiceEventListener *) pListenerHelper;
788     }
789 }
790 
791 
792 void LngSvcMgr::GetSpellCheckerDsp_Impl( sal_Bool bSetSvcList )
793 {
794     if (!pSpellDsp)
795     {
796         pSpellDsp   = new SpellCheckerDispatcher( *this );
797         xSpellDsp   = pSpellDsp;
798         if (bSetSvcList)
799             SetCfgServiceLists( *pSpellDsp );
800     }
801 }
802 
803 
804 void LngSvcMgr::GetGrammarCheckerDsp_Impl( sal_Bool bSetSvcList  )
805 {
806     if (!pGrammarDsp && SvtLinguConfig().HasGrammarChecker())
807     {
808         //! since the grammar checking iterator needs to be a one instance service
809         //! we need to create it the correct way!
810         uno::Reference< linguistic2::XProofreadingIterator > xGCI;
811         try
812         {
813             uno::Reference< lang::XMultiServiceFactory > xMgr(
814                     utl::getProcessServiceFactory(), uno::UNO_QUERY_THROW );
815             xGCI = uno::Reference< linguistic2::XProofreadingIterator >(
816                     xMgr->createInstance( A2OU( SN_GRAMMARCHECKINGITERATOR ) ), uno::UNO_QUERY_THROW );
817         }
818         catch (uno::Exception &)
819         {
820         }
821         DBG_ASSERT( xGCI.is(), "instantiating grammar checking iterator failed" );
822 
823         if (xGCI.is())
824         {
825             pGrammarDsp    = dynamic_cast< GrammarCheckingIterator * >(xGCI.get());
826             xGrammarDsp    = xGCI;
827             DBG_ASSERT( pGrammarDsp, "failed to get implementation" );
828             if (bSetSvcList)
829                 SetCfgServiceLists( *pGrammarDsp );
830         }
831     }
832 }
833 
834 
835 void LngSvcMgr::GetHyphenatorDsp_Impl( sal_Bool bSetSvcList  )
836 {
837     if (!pHyphDsp)
838     {
839         pHyphDsp    = new HyphenatorDispatcher( *this );
840         xHyphDsp    = pHyphDsp;
841         if (bSetSvcList)
842             SetCfgServiceLists( *pHyphDsp );
843     }
844 }
845 
846 
847 void LngSvcMgr::GetThesaurusDsp_Impl( sal_Bool bSetSvcList  )
848 {
849     if (!pThesDsp)
850     {
851         pThesDsp    = new ThesaurusDispatcher;
852         xThesDsp    = pThesDsp;
853         if (bSetSvcList)
854             SetCfgServiceLists( *pThesDsp );
855     }
856 }
857 
858 
859 void LngSvcMgr::GetAvailableSpellSvcs_Impl()
860 {
861     if (!pAvailSpellSvcs)
862     {
863         pAvailSpellSvcs = new SvcInfoArray;
864 
865         uno::Reference< lang::XMultiServiceFactory >  xFac( utl::getProcessServiceFactory() );
866         if (xFac.is())
867         {
868             uno::Reference< container::XContentEnumerationAccess > xEnumAccess( xFac, uno::UNO_QUERY );
869             uno::Reference< container::XEnumeration > xEnum;
870             if (xEnumAccess.is())
871                 xEnum = xEnumAccess->createContentEnumeration(
872                         A2OU( SN_SPELLCHECKER ) );
873 
874             if (xEnum.is())
875             {
876                 while (xEnum->hasMoreElements())
877                 {
878                     uno::Any aCurrent = xEnum->nextElement();
879                     uno::Reference< lang::XSingleComponentFactory > xCompFactory;
880                     uno::Reference< lang::XSingleServiceFactory > xFactory;
881 
882                     uno::Reference< linguistic2::XSpellChecker > xSvc;
883                     if ( cppu::extractInterface( xCompFactory, aCurrent ) || ::cppu::extractInterface( xFactory, aCurrent ) )
884                     {
885                         try
886                         {
887                             uno::Reference < uno::XComponentContext > xContext;
888                             uno::Reference< beans::XPropertySet > xProps( xFac, uno::UNO_QUERY );
889 
890                             xProps->getPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "DefaultContext" ))) >>= xContext;
891                             xSvc = uno::Reference< linguistic2::XSpellChecker >( ( xCompFactory.is() ? xCompFactory->createInstanceWithContext( xContext ) : xFactory->createInstance() ), uno::UNO_QUERY );
892                         }
893                         catch (uno::Exception &rEx)
894                         {
895                             (void) rEx;
896                             DBG_ASSERT( 0, "createInstance failed" );
897                         }
898                     }
899 
900                     if (xSvc.is())
901                     {
902                         OUString            aImplName;
903                         uno::Sequence< sal_Int16 >    aLanguages;
904                         uno::Reference< XServiceInfo > xInfo( xSvc, uno::UNO_QUERY );
905                         if (xInfo.is())
906                             aImplName = xInfo->getImplementationName();
907                         DBG_ASSERT( aImplName.getLength(),
908                                 "empty implementation name" );
909                         uno::Reference< linguistic2::XSupportedLocales > xSuppLoc( xSvc, uno::UNO_QUERY );
910                         DBG_ASSERT( xSuppLoc.is(), "interfaces not supported" );
911                         if (xSuppLoc.is()) {
912                             uno::Sequence<lang::Locale> aLocaleSequence(xSuppLoc->getLocales());
913                             aLanguages = LocaleSeqToLangSeq( aLocaleSequence );
914                         }
915 
916                         pAvailSpellSvcs->push_back( new SvcInfo( aImplName, aLanguages ) );
917                     }
918                 }
919             }
920         }
921     }
922 }
923 
924 
925 void LngSvcMgr::GetAvailableGrammarSvcs_Impl()
926 {
927     if (!pAvailGrammarSvcs)
928     {
929         pAvailGrammarSvcs = new SvcInfoArray;
930 
931         uno::Reference< lang::XMultiServiceFactory >  xFac( utl::getProcessServiceFactory() );
932         if (xFac.is())
933         {
934             uno::Reference< container::XContentEnumerationAccess > xEnumAccess( xFac, uno::UNO_QUERY );
935             uno::Reference< container::XEnumeration > xEnum;
936             if (xEnumAccess.is())
937                 xEnum = xEnumAccess->createContentEnumeration(
938                         A2OU( SN_GRAMMARCHECKER ) );
939 
940             if (xEnum.is())
941             {
942                 while (xEnum->hasMoreElements())
943                 {
944                     uno::Any aCurrent = xEnum->nextElement();
945                     uno::Reference< lang::XSingleComponentFactory > xCompFactory;
946                     uno::Reference< lang::XSingleServiceFactory > xFactory;
947 
948                     uno::Reference< linguistic2::XProofreader > xSvc;
949                     if ( cppu::extractInterface( xCompFactory, aCurrent ) || ::cppu::extractInterface( xFactory, aCurrent ) )
950                     {
951                         try
952                         {
953                             uno::Reference < uno::XComponentContext > xContext;
954                             uno::Reference< beans::XPropertySet > xProps( xFac, uno::UNO_QUERY );
955 
956                             xProps->getPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "DefaultContext" ))) >>= xContext;
957                             xSvc = uno::Reference< linguistic2::XProofreader >( ( xCompFactory.is() ? xCompFactory->createInstanceWithContext( xContext ) : xFactory->createInstance() ), uno::UNO_QUERY );
958                         }
959                         catch (uno::Exception &rEx)
960                         {
961                             (void) rEx;
962                             DBG_ASSERT( 0, "createInstance failed" );
963                         }
964                     }
965 
966                     if (xSvc.is())
967                     {
968                         OUString            aImplName;
969                         uno::Sequence< sal_Int16 >   aLanguages;
970                         uno::Reference< XServiceInfo > xInfo( xSvc, uno::UNO_QUERY );
971                         if (xInfo.is())
972                             aImplName = xInfo->getImplementationName();
973                         DBG_ASSERT( aImplName.getLength(),
974                                 "empty implementation name" );
975                         uno::Reference< linguistic2::XSupportedLocales > xSuppLoc( xSvc, uno::UNO_QUERY );
976                         DBG_ASSERT( xSuppLoc.is(), "interfaces not supported" );
977                         if (xSuppLoc.is()) {
978                             uno::Sequence<lang::Locale> aLocaleSequence(xSuppLoc->getLocales());
979                             aLanguages = LocaleSeqToLangSeq( aLocaleSequence );
980                         }
981 
982                         pAvailGrammarSvcs->push_back( new SvcInfo( aImplName, aLanguages ) );
983                     }
984                 }
985             }
986         }
987     }
988 }
989 
990 
991 void LngSvcMgr::GetAvailableHyphSvcs_Impl()
992 {
993     if (!pAvailHyphSvcs)
994     {
995         pAvailHyphSvcs = new SvcInfoArray;
996         uno::Reference< lang::XMultiServiceFactory >  xFac( utl::getProcessServiceFactory() );
997         if (xFac.is())
998         {
999             uno::Reference< container::XContentEnumerationAccess > xEnumAccess( xFac, uno::UNO_QUERY );
1000             uno::Reference< container::XEnumeration > xEnum;
1001             if (xEnumAccess.is())
1002                 xEnum = xEnumAccess->createContentEnumeration( A2OU( SN_HYPHENATOR ) );
1003 
1004             if (xEnum.is())
1005             {
1006                 while (xEnum->hasMoreElements())
1007                 {
1008                     uno::Any aCurrent = xEnum->nextElement();
1009                     uno::Reference< lang::XSingleComponentFactory > xCompFactory;
1010                     uno::Reference< lang::XSingleServiceFactory > xFactory;
1011 
1012                     uno::Reference< linguistic2::XHyphenator > xSvc;
1013                     if ( cppu::extractInterface( xCompFactory, aCurrent ) || ::cppu::extractInterface( xFactory, aCurrent ) )
1014                     {
1015                         try
1016                         {
1017                             uno::Reference < uno::XComponentContext > xContext;
1018                             uno::Reference< beans::XPropertySet > xProps( xFac, uno::UNO_QUERY );
1019 
1020                             xProps->getPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "DefaultContext" ))) >>= xContext;
1021                             xSvc = uno::Reference< linguistic2::XHyphenator >( ( xCompFactory.is() ? xCompFactory->createInstanceWithContext( xContext ) : xFactory->createInstance() ), uno::UNO_QUERY );
1022 
1023                         }
1024                         catch (uno::Exception &rEx)
1025                         {
1026                             (void) rEx;
1027                             DBG_ASSERT( 0, "createInstance failed" );
1028                         }
1029                     }
1030 
1031                     if (xSvc.is())
1032                     {
1033                         OUString            aImplName;
1034                         uno::Sequence< sal_Int16 >    aLanguages;
1035                         uno::Reference< XServiceInfo > xInfo( xSvc, uno::UNO_QUERY );
1036                         if (xInfo.is())
1037                             aImplName = xInfo->getImplementationName();
1038                         DBG_ASSERT( aImplName.getLength(),
1039                                 "empty implementation name" );
1040                         uno::Reference< linguistic2::XSupportedLocales > xSuppLoc( xSvc, uno::UNO_QUERY );
1041                         DBG_ASSERT( xSuppLoc.is(), "interfaces not supported" );
1042                         if (xSuppLoc.is()) {
1043                             uno::Sequence<lang::Locale> aLocaleSequence(xSuppLoc->getLocales());
1044                             aLanguages = LocaleSeqToLangSeq( aLocaleSequence );
1045                         }
1046 
1047                         pAvailHyphSvcs->push_back( new SvcInfo( aImplName, aLanguages ) );
1048                     }
1049                 }
1050             }
1051         }
1052     }
1053 }
1054 
1055 
1056 void LngSvcMgr::GetAvailableThesSvcs_Impl()
1057 {
1058     if (!pAvailThesSvcs)
1059     {
1060         pAvailThesSvcs = new SvcInfoArray;
1061 
1062         uno::Reference< lang::XMultiServiceFactory >  xFac( utl::getProcessServiceFactory() );
1063         if (xFac.is())
1064         {
1065             uno::Reference< container::XContentEnumerationAccess > xEnumAccess( xFac, uno::UNO_QUERY );
1066             uno::Reference< container::XEnumeration > xEnum;
1067             if (xEnumAccess.is())
1068                 xEnum = xEnumAccess->createContentEnumeration(
1069                         A2OU( SN_THESAURUS ) );
1070 
1071             if (xEnum.is())
1072             {
1073                 while (xEnum->hasMoreElements())
1074                 {
1075                     uno::Any aCurrent = xEnum->nextElement();
1076 
1077                     uno::Reference< lang::XSingleComponentFactory > xCompFactory;
1078                     uno::Reference< lang::XSingleServiceFactory > xFactory;
1079 
1080                     uno::Reference< linguistic2::XThesaurus > xSvc;
1081                     if ( cppu::extractInterface( xCompFactory, aCurrent ) || ::cppu::extractInterface( xFactory, aCurrent ) )
1082                     {
1083                         try
1084                         {
1085                             uno::Reference < uno::XComponentContext > xContext;
1086                             uno::Reference< beans::XPropertySet > xProps( xFac, uno::UNO_QUERY );
1087 
1088                             xProps->getPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "DefaultContext" ))) >>= xContext;
1089                             xSvc = uno::Reference< linguistic2::XThesaurus >( ( xCompFactory.is() ? xCompFactory->createInstanceWithContext( xContext ) : xFactory->createInstance() ), uno::UNO_QUERY );
1090                         }
1091                         catch (uno::Exception &rEx)
1092                         {
1093                             (void) rEx;
1094                             DBG_ASSERT( 0, "createInstance failed" );
1095                         }
1096                     }
1097 
1098                     if (xSvc.is())
1099                     {
1100                         OUString            aImplName;
1101                         uno::Sequence< sal_Int16 >    aLanguages;
1102                         uno::Reference< XServiceInfo > xInfo( xSvc, uno::UNO_QUERY );
1103                         if (xInfo.is())
1104                             aImplName = xInfo->getImplementationName();
1105                         DBG_ASSERT( aImplName.getLength(),
1106                                 "empty implementation name" );
1107                         uno::Reference< linguistic2::XSupportedLocales > xSuppLoc( xSvc, uno::UNO_QUERY );
1108                         DBG_ASSERT( xSuppLoc.is(), "interfaces not supported" );
1109                         if (xSuppLoc.is()) {
1110                             uno::Sequence<lang::Locale> aLocaleSequence(xSuppLoc->getLocales());
1111                             aLanguages = LocaleSeqToLangSeq( aLocaleSequence );
1112                         }
1113 
1114                         pAvailThesSvcs->push_back( new SvcInfo( aImplName, aLanguages ) );
1115                     }
1116                 }
1117             }
1118         }
1119     }
1120 }
1121 
1122 
1123 void LngSvcMgr::SetCfgServiceLists( SpellCheckerDispatcher &rSpellDsp )
1124 {
1125     RTL_LOGFILE_CONTEXT( aLog, "linguistic: LngSvcMgr::SetCfgServiceLists - Spell" );
1126 
1127     String  aNode( String::CreateFromAscii( "ServiceManager/SpellCheckerList" ) );
1128     uno::Sequence< OUString > aNames( /*aCfg.*/GetNodeNames( aNode ) );
1129     OUString *pNames = aNames.getArray();
1130     sal_Int32 nLen = aNames.getLength();
1131 
1132     // append path prefix need for 'GetProperties' call below
1133     String aPrefix( aNode );
1134     aPrefix.Append( (sal_Unicode) '/' );
1135     for (int i = 0;  i < nLen;  ++i)
1136     {
1137         OUString aTmp( aPrefix );
1138         aTmp += pNames[i];
1139         pNames[i] = aTmp;
1140     }
1141 
1142     uno::Sequence< uno::Any > aValues( /*aCfg.*/GetProperties( aNames ) );
1143     if (nLen  &&  nLen == aValues.getLength())
1144     {
1145         const uno::Any *pValues = aValues.getConstArray();
1146         for (sal_Int32 i = 0;  i < nLen;  ++i)
1147         {
1148             uno::Sequence< OUString > aSvcImplNames;
1149             if (pValues[i] >>= aSvcImplNames)
1150             {
1151 #if OSL_DEBUG_LEVEL > 1
1152 //                sal_Int32 nSvcs = aSvcImplNames.getLength();
1153 //                const OUString *pSvcImplNames = aSvcImplNames.getConstArray();
1154 #endif
1155                 String aLocaleStr( pNames[i] );
1156                 xub_StrLen nSeperatorPos = aLocaleStr.SearchBackward( sal_Unicode( '/' ) );
1157                 aLocaleStr = aLocaleStr.Copy( nSeperatorPos + 1 );
1158                 lang::Locale aLocale( CreateLocale( MsLangId::convertIsoStringToLanguage(aLocaleStr) ) );
1159                 rSpellDsp.SetServiceList( aLocale, aSvcImplNames );
1160             }
1161         }
1162     }
1163 }
1164 
1165 
1166 void LngSvcMgr::SetCfgServiceLists( GrammarCheckingIterator &rGrammarDsp )
1167 {
1168     RTL_LOGFILE_CONTEXT( aLog, "linguistic: LngSvcMgr::SetCfgServiceLists - Grammar" );
1169 
1170     String  aNode( String::CreateFromAscii( "ServiceManager/GrammarCheckerList" ) );
1171     uno::Sequence< OUString > aNames( /*aCfg.*/GetNodeNames( aNode ) );
1172     OUString *pNames = aNames.getArray();
1173     sal_Int32 nLen = aNames.getLength();
1174 
1175     // append path prefix need for 'GetProperties' call below
1176     String aPrefix( aNode );
1177     aPrefix.Append( (sal_Unicode) '/' );
1178     for (int i = 0;  i < nLen;  ++i)
1179     {
1180         OUString aTmp( aPrefix );
1181         aTmp += pNames[i];
1182         pNames[i] = aTmp;
1183     }
1184 
1185     uno::Sequence< uno::Any > aValues( /*aCfg.*/GetProperties( aNames ) );
1186     if (nLen  &&  nLen == aValues.getLength())
1187     {
1188         const uno::Any *pValues = aValues.getConstArray();
1189         for (sal_Int32 i = 0;  i < nLen;  ++i)
1190         {
1191             uno::Sequence< OUString > aSvcImplNames;
1192             if (pValues[i] >>= aSvcImplNames)
1193             {
1194                 // there should only be one grammar checker in use per language...
1195                 if (aSvcImplNames.getLength() > 1)
1196                     aSvcImplNames.realloc(1);
1197 
1198 #if OSL_DEBUG_LEVEL > 1
1199 //                sal_Int32 nSvcs = aSvcImplNames.getLength();
1200 //                const OUString *pSvcImplNames = aSvcImplNames.getConstArray();
1201 #endif
1202                 String aLocaleStr( pNames[i] );
1203                 xub_StrLen nSeperatorPos = aLocaleStr.SearchBackward( sal_Unicode( '/' ) );
1204                 aLocaleStr = aLocaleStr.Copy( nSeperatorPos + 1 );
1205                 lang::Locale aLocale( CreateLocale( MsLangId::convertIsoStringToLanguage(aLocaleStr) ) );
1206                 rGrammarDsp.SetServiceList( aLocale, aSvcImplNames );
1207             }
1208         }
1209     }
1210 }
1211 
1212 
1213 void LngSvcMgr::SetCfgServiceLists( HyphenatorDispatcher &rHyphDsp )
1214 {
1215     RTL_LOGFILE_CONTEXT( aLog, "linguistic: LngSvcMgr::SetCfgServiceLists - Hyph" );
1216 
1217     String  aNode( String::CreateFromAscii( "ServiceManager/HyphenatorList" ) );
1218     uno::Sequence< OUString > aNames( /*aCfg.*/GetNodeNames( aNode ) );
1219     OUString *pNames = aNames.getArray();
1220     sal_Int32 nLen = aNames.getLength();
1221 
1222     // append path prefix need for 'GetProperties' call below
1223     String aPrefix( aNode );
1224     aPrefix.Append( (sal_Unicode) '/' );
1225     for (int i = 0;  i < nLen;  ++i)
1226     {
1227         OUString aTmp( aPrefix );
1228         aTmp += pNames[i];
1229         pNames[i] = aTmp;
1230     }
1231 
1232     uno::Sequence< uno::Any > aValues( /*aCfg.*/GetProperties( aNames ) );
1233     if (nLen  &&  nLen == aValues.getLength())
1234     {
1235         const uno::Any *pValues = aValues.getConstArray();
1236         for (sal_Int32 i = 0;  i < nLen;  ++i)
1237         {
1238             uno::Sequence< OUString > aSvcImplNames;
1239             if (pValues[i] >>= aSvcImplNames)
1240             {
1241                 // there should only be one hyphenator in use per language...
1242                 if (aSvcImplNames.getLength() > 1)
1243                     aSvcImplNames.realloc(1);
1244 
1245 #if OSL_DEBUG_LEVEL > 1
1246 //                sal_Int32 nSvcs = aSvcImplNames.getLength();
1247 //                const OUString *pSvcImplNames = aSvcImplNames.getConstArray();
1248 #endif
1249                 String aLocaleStr( pNames[i] );
1250                 xub_StrLen nSeperatorPos = aLocaleStr.SearchBackward( sal_Unicode( '/' ) );
1251                 aLocaleStr = aLocaleStr.Copy( nSeperatorPos + 1 );
1252                 lang::Locale aLocale( CreateLocale( MsLangId::convertIsoStringToLanguage(aLocaleStr) ) );
1253                 rHyphDsp.SetServiceList( aLocale, aSvcImplNames );
1254             }
1255         }
1256     }
1257 }
1258 
1259 
1260 void LngSvcMgr::SetCfgServiceLists( ThesaurusDispatcher &rThesDsp )
1261 {
1262     RTL_LOGFILE_CONTEXT( aLog, "linguistic: LngSvcMgr::SetCfgServiceLists - Thes" );
1263 
1264     String  aNode( String::CreateFromAscii( "ServiceManager/ThesaurusList" ) );
1265     uno::Sequence< OUString > aNames( /*aCfg.*/GetNodeNames( aNode ) );
1266     OUString *pNames = aNames.getArray();
1267     sal_Int32 nLen = aNames.getLength();
1268 
1269     // append path prefix need for 'GetProperties' call below
1270     String aPrefix( aNode );
1271     aPrefix.Append( (sal_Unicode) '/' );
1272     for (int i = 0;  i < nLen;  ++i)
1273     {
1274         OUString aTmp( aPrefix );
1275         aTmp += pNames[i];
1276         pNames[i] = aTmp;
1277     }
1278 
1279     uno::Sequence< uno::Any > aValues( /*aCfg.*/GetProperties( aNames ) );
1280     if (nLen  &&  nLen == aValues.getLength())
1281     {
1282         const uno::Any *pValues = aValues.getConstArray();
1283         for (sal_Int32 i = 0;  i < nLen;  ++i)
1284         {
1285             uno::Sequence< OUString > aSvcImplNames;
1286             if (pValues[i] >>= aSvcImplNames)
1287             {
1288 #if OSL_DEBUG_LEVEL > 1
1289 //                sal_Int32 nSvcs = aSvcImplNames.getLength();
1290 //                const OUString *pSvcImplNames = aSvcImplNames.getConstArray();
1291 #endif
1292                 String aLocaleStr( pNames[i] );
1293                 xub_StrLen nSeperatorPos = aLocaleStr.SearchBackward( sal_Unicode( '/' ) );
1294                 aLocaleStr = aLocaleStr.Copy( nSeperatorPos + 1 );
1295                 lang::Locale aLocale( CreateLocale( MsLangId::convertIsoStringToLanguage(aLocaleStr) ) );
1296                 rThesDsp.SetServiceList( aLocale, aSvcImplNames );
1297             }
1298         }
1299     }
1300 }
1301 
1302 
1303 uno::Reference< linguistic2::XSpellChecker > SAL_CALL
1304     LngSvcMgr::getSpellChecker()
1305         throw(uno::RuntimeException)
1306 {
1307     osl::MutexGuard aGuard( GetLinguMutex() );
1308 #if OSL_DEBUG_LEVEL > 1
1309     getAvailableLocales( A2OU( SN_SPELLCHECKER ));
1310 #endif
1311 
1312     uno::Reference< linguistic2::XSpellChecker > xRes;
1313     if (!bDisposing)
1314     {
1315         if (!xSpellDsp.is())
1316             GetSpellCheckerDsp_Impl();
1317         xRes = xSpellDsp;
1318     }
1319     return xRes;
1320 }
1321 
1322 
1323 uno::Reference< linguistic2::XHyphenator > SAL_CALL
1324     LngSvcMgr::getHyphenator()
1325         throw(uno::RuntimeException)
1326 {
1327     osl::MutexGuard aGuard( GetLinguMutex() );
1328 #if OSL_DEBUG_LEVEL > 1
1329     getAvailableLocales( A2OU( SN_HYPHENATOR ));
1330 #endif
1331 
1332     uno::Reference< linguistic2::XHyphenator >   xRes;
1333     if (!bDisposing)
1334     {
1335         if (!xHyphDsp.is())
1336             GetHyphenatorDsp_Impl();
1337         xRes = xHyphDsp;
1338     }
1339     return xRes;
1340 }
1341 
1342 
1343 uno::Reference< linguistic2::XThesaurus > SAL_CALL
1344     LngSvcMgr::getThesaurus()
1345         throw(uno::RuntimeException)
1346 {
1347     osl::MutexGuard aGuard( GetLinguMutex() );
1348 #if OSL_DEBUG_LEVEL > 1
1349     getAvailableLocales( A2OU( SN_THESAURUS ));
1350 #endif
1351 
1352     uno::Reference< linguistic2::XThesaurus >    xRes;
1353     if (!bDisposing)
1354     {
1355         if (!xThesDsp.is())
1356             GetThesaurusDsp_Impl();
1357         xRes = xThesDsp;
1358     }
1359     return xRes;
1360 }
1361 
1362 
1363 sal_Bool SAL_CALL
1364     LngSvcMgr::addLinguServiceManagerListener(
1365             const uno::Reference< lang::XEventListener >& xListener )
1366         throw(uno::RuntimeException)
1367 {
1368     osl::MutexGuard aGuard( GetLinguMutex() );
1369 
1370     sal_Bool bRes = sal_False;
1371     if (!bDisposing  &&  xListener.is())
1372     {
1373         if (!pListenerHelper)
1374             GetListenerHelper_Impl();
1375         bRes = pListenerHelper->AddLngSvcMgrListener( xListener );
1376     }
1377     return bRes;
1378 }
1379 
1380 
1381 sal_Bool SAL_CALL
1382     LngSvcMgr::removeLinguServiceManagerListener(
1383             const uno::Reference< lang::XEventListener >& xListener )
1384         throw(uno::RuntimeException)
1385 {
1386     osl::MutexGuard aGuard( GetLinguMutex() );
1387 
1388     sal_Bool bRes = sal_False;
1389     if (!bDisposing  &&  xListener.is())
1390     {
1391         DBG_ASSERT( pListenerHelper, "listener removed without being added" );
1392         if (!pListenerHelper)
1393             GetListenerHelper_Impl();
1394         bRes = pListenerHelper->RemoveLngSvcMgrListener( xListener );
1395     }
1396     return bRes;
1397 }
1398 
1399 
1400 uno::Sequence< OUString > SAL_CALL
1401     LngSvcMgr::getAvailableServices(
1402             const OUString& rServiceName,
1403             const lang::Locale& rLocale )
1404         throw(uno::RuntimeException)
1405 {
1406     osl::MutexGuard aGuard( GetLinguMutex() );
1407 
1408     uno::Sequence< OUString > aRes;
1409     const SvcInfoArray *pInfoArray = 0;
1410 
1411     if (0 == rServiceName.compareToAscii( SN_SPELLCHECKER ))
1412     {
1413         // don't used cached data here (force re-evaluation in order to have downloaded dictionaries
1414         // already found without the need to restart the office
1415         clearSvcInfoArray(pAvailSpellSvcs);  pAvailSpellSvcs = 0;
1416         GetAvailableSpellSvcs_Impl();
1417         pInfoArray = pAvailSpellSvcs;
1418     }
1419     else if (0 == rServiceName.compareToAscii( SN_GRAMMARCHECKER ))
1420     {
1421         // don't used cached data here (force re-evaluation in order to have downloaded dictionaries
1422         // already found without the need to restart the office
1423         clearSvcInfoArray(pAvailGrammarSvcs);  pAvailGrammarSvcs = 0;
1424         GetAvailableGrammarSvcs_Impl();
1425         pInfoArray = pAvailGrammarSvcs;
1426     }
1427     else if (0 == rServiceName.compareToAscii( SN_HYPHENATOR ))
1428     {
1429         // don't used cached data here (force re-evaluation in order to have downloaded dictionaries
1430         // already found without the need to restart the office
1431         clearSvcInfoArray(pAvailHyphSvcs);  pAvailHyphSvcs = 0;
1432         GetAvailableHyphSvcs_Impl();
1433         pInfoArray = pAvailHyphSvcs;
1434     }
1435     else if (0 == rServiceName.compareToAscii( SN_THESAURUS ))
1436     {
1437         // don't used cached data here (force re-evaluation in order to have downloaded dictionaries
1438         // already found without the need to restart the office
1439         clearSvcInfoArray(pAvailThesSvcs);  pAvailThesSvcs = 0;
1440         GetAvailableThesSvcs_Impl();
1441         pInfoArray = pAvailThesSvcs;
1442     }
1443 
1444     if (pInfoArray)
1445     {
1446         // resize to max number of entries
1447         size_t nMaxCnt = pInfoArray->size();
1448         aRes.realloc( nMaxCnt );
1449         OUString *pImplName = aRes.getArray();
1450 
1451         sal_uInt16 nCnt = 0;
1452         LanguageType nLanguage = LocaleToLanguage( rLocale );
1453         for (size_t i = 0;  i < nMaxCnt;  ++i)
1454         {
1455             const SvcInfo *pInfo = (*pInfoArray)[i];
1456             if (LANGUAGE_NONE == nLanguage
1457                 || (pInfo && pInfo->HasLanguage( nLanguage )))
1458             {
1459                 pImplName[ nCnt++ ] = pInfo->aSvcImplName;
1460             }
1461         }
1462 
1463         // resize to actual number of entries
1464         if (nCnt != nMaxCnt)
1465             aRes.realloc( nCnt );
1466     }
1467 
1468     return aRes;
1469 }
1470 
1471 
1472 uno::Sequence< lang::Locale > SAL_CALL
1473     LngSvcMgr::getAvailableLocales(
1474             const OUString& rServiceName )
1475         throw(uno::RuntimeException)
1476 {
1477     osl::MutexGuard aGuard( GetLinguMutex() );
1478 
1479     uno::Sequence< lang::Locale > aRes;
1480 
1481     uno::Sequence< lang::Locale >  *pAvailLocales     = NULL;
1482     sal_Bool                *pHasAvailLocales   = NULL;
1483     if (0 == rServiceName.compareToAscii( SN_SPELLCHECKER ))
1484     {
1485         pAvailLocales       = &aAvailSpellLocales;
1486         pHasAvailLocales    = &bHasAvailSpellLocales;
1487     }
1488     else if (0 == rServiceName.compareToAscii( SN_GRAMMARCHECKER ))
1489     {
1490         pAvailLocales       = &aAvailGrammarLocales;
1491         pHasAvailLocales    = &bHasAvailGrammarLocales;
1492     }
1493     else if (0 == rServiceName.compareToAscii( SN_HYPHENATOR ))
1494     {
1495         pAvailLocales       = &aAvailHyphLocales;
1496         pHasAvailLocales    = &bHasAvailHyphLocales;
1497     }
1498     else if (0 == rServiceName.compareToAscii( SN_THESAURUS ))
1499     {
1500         pAvailLocales       = &aAvailThesLocales;
1501         pHasAvailLocales    = &bHasAvailThesLocales;
1502     }
1503 
1504     // about pHasAvailLocales: nowadays (with OOo lingu in SO) we want to know immediately about
1505     // new downloaded dictionaries and have them ready right away if the Tools/Options...
1506     // is used to activate them. Thus we can not rely anymore on buffered data.
1507     if (pAvailLocales  /*&&  pHasAvailLocales */)
1508     {
1509 //      if (!*pHasAvailLocales)
1510 //      {
1511             *pAvailLocales = GetAvailLocales(
1512                     getAvailableServices( rServiceName, lang::Locale() ) );
1513 //          *pHasAvailLocales = sal_True;
1514 //      }
1515         aRes = *pAvailLocales;
1516     }
1517 
1518     return aRes;
1519 }
1520 
1521 static sal_Bool IsEqSvcList( const uno::Sequence< OUString > &rList1,
1522                         const uno::Sequence< OUString > &rList2 )
1523 {
1524     // returns sal_True iff both sequences are equal
1525 
1526     sal_Bool bRes = sal_False;
1527     sal_Int32 nLen = rList1.getLength();
1528     if (rList2.getLength() == nLen)
1529     {
1530         const OUString *pStr1 = rList1.getConstArray();
1531         const OUString *pStr2 = rList2.getConstArray();
1532         bRes = sal_True;
1533         for (sal_Int32 i = 0;  i < nLen  &&  bRes;  ++i)
1534         {
1535             if (*pStr1++ != *pStr2++)
1536                 bRes = sal_False;
1537         }
1538     }
1539     return bRes;
1540 }
1541 
1542 
1543 void SAL_CALL
1544     LngSvcMgr::setConfiguredServices(
1545             const OUString& rServiceName,
1546             const lang::Locale& rLocale,
1547             const uno::Sequence< OUString >& rServiceImplNames )
1548         throw(uno::RuntimeException)
1549 {
1550     RTL_LOGFILE_CONTEXT( aLog, "linguistic: LngSvcMgr::setConfiguredServices" );
1551 
1552     osl::MutexGuard aGuard( GetLinguMutex() );
1553 
1554 #if OSL_DEBUG_LEVEL > 1
1555 //    const OUString *pImplNames = rServiceImplNames.getConstArray();
1556 #endif
1557 
1558     LanguageType nLanguage = LocaleToLanguage( rLocale );
1559     if (LANGUAGE_NONE != nLanguage)
1560     {
1561         if (0 == rServiceName.compareToAscii( SN_SPELLCHECKER ))
1562         {
1563             if (!xSpellDsp.is())
1564                 GetSpellCheckerDsp_Impl();
1565             sal_Bool bChanged = !IsEqSvcList( rServiceImplNames,
1566                                           pSpellDsp->GetServiceList( rLocale ) );
1567             if (bChanged)
1568             {
1569                 pSpellDsp->SetServiceList( rLocale, rServiceImplNames );
1570                 SaveCfgSvcs( A2OU( SN_SPELLCHECKER ) );
1571 
1572                 if (pListenerHelper  &&  bChanged)
1573                     pListenerHelper->AddLngSvcEvt(
1574                             linguistic2::LinguServiceEventFlags::SPELL_CORRECT_WORDS_AGAIN |
1575                             linguistic2::LinguServiceEventFlags::SPELL_WRONG_WORDS_AGAIN );
1576             }
1577         }
1578         else if (0 == rServiceName.compareToAscii( SN_GRAMMARCHECKER ))
1579         {
1580             if (!xGrammarDsp.is())
1581                 GetGrammarCheckerDsp_Impl();
1582             sal_Bool bChanged = !IsEqSvcList( rServiceImplNames,
1583                                           pGrammarDsp->GetServiceList( rLocale ) );
1584             if (bChanged)
1585             {
1586                 pGrammarDsp->SetServiceList( rLocale, rServiceImplNames );
1587                 SaveCfgSvcs( A2OU( SN_GRAMMARCHECKER ) );
1588 
1589                 if (pListenerHelper  &&  bChanged)
1590                     pListenerHelper->AddLngSvcEvt(
1591                             linguistic2::LinguServiceEventFlags::PROOFREAD_AGAIN );
1592             }
1593         }
1594         else if (0 == rServiceName.compareToAscii( SN_HYPHENATOR ))
1595         {
1596             if (!xHyphDsp.is())
1597                 GetHyphenatorDsp_Impl();
1598             sal_Bool bChanged = !IsEqSvcList( rServiceImplNames,
1599                                           pHyphDsp->GetServiceList( rLocale ) );
1600             if (bChanged)
1601             {
1602                 pHyphDsp->SetServiceList( rLocale, rServiceImplNames );
1603                 SaveCfgSvcs( A2OU( SN_HYPHENATOR ) );
1604 
1605                 if (pListenerHelper  &&  bChanged)
1606                     pListenerHelper->AddLngSvcEvt(
1607                             linguistic2::LinguServiceEventFlags::HYPHENATE_AGAIN );
1608             }
1609         }
1610         else if (0 == rServiceName.compareToAscii( SN_THESAURUS ))
1611         {
1612             if (!xThesDsp.is())
1613                 GetThesaurusDsp_Impl();
1614             sal_Bool bChanged = !IsEqSvcList( rServiceImplNames,
1615                                           pThesDsp->GetServiceList( rLocale ) );
1616             if (bChanged)
1617             {
1618                 pThesDsp->SetServiceList( rLocale, rServiceImplNames );
1619                 SaveCfgSvcs( A2OU( SN_THESAURUS ) );
1620             }
1621         }
1622     }
1623 }
1624 
1625 
1626 sal_Bool LngSvcMgr::SaveCfgSvcs( const String &rServiceName )
1627 {
1628     RTL_LOGFILE_CONTEXT( aLog, "linguistic: LngSvcMgr::SaveCfgSvcs" );
1629 
1630     sal_Bool bRes = sal_False;
1631 
1632     LinguDispatcher *pDsp = 0;
1633     uno::Sequence< lang::Locale > aLocales;
1634 
1635     if (0 == rServiceName.CompareToAscii( SN_SPELLCHECKER ))
1636     {
1637         if (!pSpellDsp)
1638             GetSpellCheckerDsp_Impl();
1639         pDsp = pSpellDsp;
1640         aLocales = getAvailableLocales( A2OU( SN_SPELLCHECKER ) );
1641     }
1642     else if (0 == rServiceName.CompareToAscii( SN_GRAMMARCHECKER ))
1643     {
1644         if (!pGrammarDsp)
1645             GetGrammarCheckerDsp_Impl();
1646         pDsp = pGrammarDsp;
1647         aLocales = getAvailableLocales( A2OU( SN_GRAMMARCHECKER ) );
1648     }
1649     else if (0 == rServiceName.CompareToAscii( SN_HYPHENATOR ))
1650     {
1651         if (!pHyphDsp)
1652             GetHyphenatorDsp_Impl();
1653         pDsp = pHyphDsp;
1654         aLocales = getAvailableLocales( A2OU( SN_HYPHENATOR ) );
1655     }
1656     else if (0 == rServiceName.CompareToAscii( SN_THESAURUS ))
1657     {
1658         if (!pThesDsp)
1659             GetThesaurusDsp_Impl();
1660         pDsp = pThesDsp;
1661         aLocales = getAvailableLocales( A2OU( SN_THESAURUS ) );
1662     }
1663 
1664     if (pDsp  &&  aLocales.getLength())
1665     {
1666         sal_Int32 nLen = aLocales.getLength();
1667         const lang::Locale *pLocale = aLocales.getConstArray();
1668 
1669         uno::Sequence< beans::PropertyValue > aValues( nLen );
1670         beans::PropertyValue *pValues = aValues.getArray();
1671         beans::PropertyValue *pValue  = pValues;
1672 
1673         // get node name to be used
1674         const char *pNodeName = NULL;
1675         if (pDsp == pSpellDsp)
1676             pNodeName = "ServiceManager/SpellCheckerList";
1677         else if (pDsp == pGrammarDsp)
1678             pNodeName = "ServiceManager/GrammarCheckerList";
1679         else if (pDsp == pHyphDsp)
1680             pNodeName = "ServiceManager/HyphenatorList";
1681         else if (pDsp == pThesDsp)
1682             pNodeName = "ServiceManager/ThesaurusList";
1683         else
1684         {
1685             DBG_ASSERT( 0, "node name missing" );
1686         }
1687         OUString aNodeName( A2OU(pNodeName) );
1688 
1689         for (sal_Int32 i = 0;  i < nLen;  ++i)
1690         {
1691             uno::Sequence< OUString > aSvcImplNames;
1692             aSvcImplNames = pDsp->GetServiceList( pLocale[i] );
1693 
1694 #if OSL_DEBUG_LEVEL > 1
1695             sal_Int32 nSvcs = aSvcImplNames.getLength();
1696             const OUString *pSvcImplName = aSvcImplNames.getConstArray();
1697             for (sal_Int32 j = 0;  j < nSvcs;  ++j)
1698             {
1699                 OUString aImplName( pSvcImplName[j] );
1700             }
1701 #endif
1702             // build value to be written back to configuration
1703             uno::Any aCfgAny;
1704             if ((pDsp == pHyphDsp || pDsp == pGrammarDsp) && aSvcImplNames.getLength() > 1)
1705                 aSvcImplNames.realloc(1);   // there should be only one entry for hyphenators or grammar checkers (because they are not chained)
1706             aCfgAny <<= aSvcImplNames;
1707             DBG_ASSERT( aCfgAny.hasValue(), "missing value for 'Any' type" );
1708 
1709             OUString aCfgLocaleStr( MsLangId::convertLanguageToIsoString(
1710                                         LocaleToLanguage( pLocale[i] ) ) );
1711             pValue->Value = aCfgAny;
1712             pValue->Name  = aNodeName;
1713             pValue->Name += OUString::valueOf( (sal_Unicode) '/' );
1714             pValue->Name += aCfgLocaleStr;
1715             pValue++;
1716         }
1717         {
1718         RTL_LOGFILE_CONTEXT( aLog, "linguistic: LngSvcMgr::SaveCfgSvcs - ReplaceSetProperties" );
1719         // change, add new or replace existing entries.
1720         bRes |= /*aCfg.*/ReplaceSetProperties( aNodeName, aValues );
1721         }
1722     }
1723 
1724     return bRes;
1725 }
1726 
1727 
1728 static uno::Sequence< OUString > GetLangSvcList( const uno::Any &rVal )
1729 {
1730     uno::Sequence< OUString > aRes;
1731 
1732     if (rVal.hasValue())
1733     {
1734         rVal >>= aRes;
1735 #if OSL_DEBUG_LEVEL > 1
1736         sal_Int32 nSvcs = aRes.getLength();
1737         if (nSvcs)
1738         {
1739             const OUString *pSvcName = aRes.getConstArray();
1740             for (sal_Int32 j = 0;  j < nSvcs;  ++j)
1741             {
1742                 OUString aImplName( pSvcName[j] );
1743                 DBG_ASSERT( aImplName.getLength(), "service impl-name missing" );
1744             }
1745         }
1746 #endif
1747     }
1748 
1749     return aRes;
1750 }
1751 
1752 
1753 static uno::Sequence< OUString > GetLangSvc( const uno::Any &rVal )
1754 {
1755     uno::Sequence< OUString > aRes;
1756     if (!rVal.hasValue())
1757         return aRes;
1758 
1759     // allowing for a sequence here as well (even though it should only
1760     // be a string) makes coding easier in other places since one needs
1761     // not make a special case for writing a string only and not a
1762     // sequence of strings.
1763     if (rVal >>= aRes)
1764     {
1765         // but only the first string should be used.
1766         if (aRes.getLength() > 1)
1767             aRes.realloc(1);
1768     }
1769     else
1770     {
1771         OUString aImplName;
1772         if ((rVal >>= aImplName) && aImplName.getLength() != 0)
1773         {
1774             aRes.realloc(1);
1775             aRes.getArray()[0] = aImplName;
1776         }
1777         else
1778         {
1779             DBG_ASSERT( 0, "GetLangSvc: unexpected type encountered" );
1780         }
1781     }
1782 
1783     return aRes;
1784 }
1785 
1786 
1787 ///////////////////////////////////////////////////////////////////////////
1788 
1789 uno::Sequence< OUString > SAL_CALL
1790     LngSvcMgr::getConfiguredServices(
1791             const OUString& rServiceName,
1792             const lang::Locale& rLocale )
1793         throw(uno::RuntimeException)
1794 {
1795     osl::MutexGuard aGuard( GetLinguMutex() );
1796 
1797     uno::Sequence< OUString > aSvcImplNames;
1798 
1799     LanguageType nLanguage = LocaleToLanguage( rLocale );
1800     OUString aCfgLocale( MsLangId::convertLanguageToIsoString( nLanguage ) );
1801 
1802     uno::Sequence< uno::Any > aValues;
1803     uno::Sequence< OUString > aNames( 1 );
1804     OUString *pNames = aNames.getArray();
1805     if ( 0 == rServiceName.compareToAscii( SN_SPELLCHECKER ) )
1806     {
1807         OUString aNode( OUString::createFromAscii( "ServiceManager/SpellCheckerList" ));
1808         const uno::Sequence< OUString > aNodeEntries( GetNodeNames( aNode ) );
1809         if (lcl_SeqHasString( aNodeEntries, aCfgLocale ))
1810         {
1811             OUString aPropName( aNode );
1812             aPropName += OUString::valueOf( (sal_Unicode) '/' );
1813             aPropName += aCfgLocale;
1814             pNames[0] = aPropName;
1815             aValues = /*aCfg.*/GetProperties( aNames );
1816             if (aValues.getLength())
1817                 aSvcImplNames = GetLangSvcList( aValues.getConstArray()[0] );
1818         }
1819     }
1820     else if ( 0 == rServiceName.compareToAscii( SN_GRAMMARCHECKER ) )
1821     {
1822         OUString aNode( OUString::createFromAscii( "ServiceManager/GrammarCheckerList" ));
1823         const uno::Sequence< OUString > aNodeEntries( GetNodeNames( aNode ) );
1824         if (lcl_SeqHasString( aNodeEntries, aCfgLocale ))
1825         {
1826             OUString aPropName( aNode );
1827             aPropName += OUString::valueOf( (sal_Unicode) '/' );
1828             aPropName += aCfgLocale;
1829             pNames[0] = aPropName;
1830             aValues = /*aCfg.*/GetProperties( aNames );
1831             if (aValues.getLength())
1832                 aSvcImplNames = GetLangSvc( aValues.getConstArray()[0] );
1833         }
1834     }
1835     else if ( 0 == rServiceName.compareToAscii( SN_HYPHENATOR ) )
1836     {
1837         OUString aNode( OUString::createFromAscii( "ServiceManager/HyphenatorList" ));
1838         const uno::Sequence< OUString > aNodeEntries( GetNodeNames( aNode ) );
1839         if (lcl_SeqHasString( aNodeEntries, aCfgLocale ))
1840         {
1841             OUString aPropName( aNode );
1842             aPropName += OUString::valueOf( (sal_Unicode) '/' );
1843             aPropName += aCfgLocale;
1844             pNames[0] = aPropName;
1845             aValues = /*aCfg.*/GetProperties( aNames );
1846             if (aValues.getLength())
1847                 aSvcImplNames = GetLangSvc( aValues.getConstArray()[0] );
1848         }
1849     }
1850     else if ( 0 == rServiceName.compareToAscii( SN_THESAURUS ) )
1851     {
1852         OUString aNode( OUString::createFromAscii( "ServiceManager/ThesaurusList" ));
1853         const uno::Sequence< OUString > aNodeEntries( GetNodeNames( aNode ) );
1854         if (lcl_SeqHasString( aNodeEntries, aCfgLocale ))
1855         {
1856             OUString aPropName( aNode );
1857             aPropName += OUString::valueOf( (sal_Unicode) '/' );
1858             aPropName += aCfgLocale;
1859             pNames[0] = aPropName;
1860             aValues = /*aCfg.*/GetProperties( aNames );
1861             if (aValues.getLength())
1862                 aSvcImplNames = GetLangSvcList( aValues.getConstArray()[0] );
1863         }
1864     }
1865 
1866 #if OSL_DEBUG_LEVEL > 1
1867     const OUString *pImplNames = aSvcImplNames.getConstArray();
1868     (void) pImplNames;
1869 #endif
1870     return aSvcImplNames;
1871 }
1872 
1873 
1874 void SAL_CALL
1875     LngSvcMgr::dispose()
1876         throw(uno::RuntimeException)
1877 {
1878     osl::MutexGuard aGuard( GetLinguMutex() );
1879 
1880     if (!bDisposing)
1881     {
1882         bDisposing = sal_True;
1883 
1884         // require listeners to release this object
1885         lang::EventObject aEvtObj( (XLinguServiceManager *) this );
1886         aEvtListeners.disposeAndClear( aEvtObj );
1887 
1888         if (pListenerHelper)
1889             pListenerHelper->DisposeAndClear( aEvtObj );
1890     }
1891 }
1892 
1893 
1894 void SAL_CALL
1895     LngSvcMgr::addEventListener(
1896             const uno::Reference< lang::XEventListener >& xListener )
1897         throw(uno::RuntimeException)
1898 {
1899     osl::MutexGuard aGuard( GetLinguMutex() );
1900 
1901     if (!bDisposing  &&  xListener.is())
1902     {
1903         aEvtListeners.addInterface( xListener );
1904     }
1905 }
1906 
1907 
1908 void SAL_CALL
1909     LngSvcMgr::removeEventListener(
1910             const uno::Reference< lang::XEventListener >& xListener )
1911         throw(uno::RuntimeException)
1912 {
1913     osl::MutexGuard aGuard( GetLinguMutex() );
1914 
1915     if (xListener.is())
1916     {
1917         aEvtListeners.removeInterface( xListener );
1918     }
1919 }
1920 
1921 
1922 sal_Bool LngSvcMgr::AddLngSvcEvtBroadcaster(
1923             const uno::Reference< linguistic2::XLinguServiceEventBroadcaster > &rxBroadcaster )
1924 {
1925     sal_Bool bRes = sal_False;
1926     if (rxBroadcaster.is())
1927     {
1928         if (!pListenerHelper)
1929             GetListenerHelper_Impl();
1930         bRes = pListenerHelper->AddLngSvcEvtBroadcaster( rxBroadcaster );
1931     }
1932     return bRes;
1933 }
1934 
1935 
1936 sal_Bool LngSvcMgr::RemoveLngSvcEvtBroadcaster(
1937             const uno::Reference< linguistic2::XLinguServiceEventBroadcaster > &rxBroadcaster )
1938 {
1939     sal_Bool bRes = sal_False;
1940     if (rxBroadcaster.is())
1941     {
1942         DBG_ASSERT( pListenerHelper, "pListenerHelper non existent" );
1943         if (!pListenerHelper)
1944             GetListenerHelper_Impl();
1945         bRes = pListenerHelper->RemoveLngSvcEvtBroadcaster( rxBroadcaster );
1946     }
1947     return bRes;
1948 }
1949 
1950 
1951 OUString SAL_CALL
1952     LngSvcMgr::getImplementationName()
1953         throw(uno::RuntimeException)
1954 {
1955     osl::MutexGuard aGuard( GetLinguMutex() );
1956     return getImplementationName_Static();
1957 }
1958 
1959 
1960 sal_Bool SAL_CALL
1961     LngSvcMgr::supportsService( const OUString& ServiceName )
1962         throw(uno::RuntimeException)
1963 {
1964     osl::MutexGuard aGuard( GetLinguMutex() );
1965 
1966     uno::Sequence< OUString > aSNL = getSupportedServiceNames();
1967     const OUString * pArray = aSNL.getConstArray();
1968     for( sal_Int32 i = 0; i < aSNL.getLength(); i++ )
1969         if( pArray[i] == ServiceName )
1970             return sal_True;
1971     return sal_False;
1972 }
1973 
1974 
1975 uno::Sequence< OUString > SAL_CALL
1976     LngSvcMgr::getSupportedServiceNames()
1977         throw(uno::RuntimeException)
1978 {
1979     osl::MutexGuard aGuard( GetLinguMutex() );
1980     return getSupportedServiceNames_Static();
1981 }
1982 
1983 
1984 uno::Sequence< OUString > LngSvcMgr::getSupportedServiceNames_Static()
1985         throw()
1986 {
1987     osl::MutexGuard aGuard( GetLinguMutex() );
1988 
1989     uno::Sequence< OUString > aSNS( 1 );    // auch mehr als 1 Service moeglich
1990     aSNS.getArray()[0] = A2OU( SN_LINGU_SERVCICE_MANAGER );
1991     return aSNS;
1992 }
1993 
1994 
1995 uno::Reference< uno::XInterface > SAL_CALL LngSvcMgr_CreateInstance(
1996             const uno::Reference< lang::XMultiServiceFactory > & /*rSMgr*/ )
1997         throw(uno::Exception)
1998 {
1999     uno::Reference< uno::XInterface > xService = (cppu::OWeakObject*) new LngSvcMgr;
2000     return xService;
2001 }
2002 
2003 void * SAL_CALL LngSvcMgr_getFactory(
2004             const sal_Char * pImplName,
2005             lang::XMultiServiceFactory * pServiceManager,
2006             void * /*pRegistryKey*/ )
2007 {
2008 
2009     void * pRet = 0;
2010     if ( !LngSvcMgr::getImplementationName_Static().compareToAscii( pImplName ) )
2011     {
2012         uno::Reference< lang::XSingleServiceFactory > xFactory =
2013             cppu::createOneInstanceFactory(
2014                 pServiceManager,
2015                 LngSvcMgr::getImplementationName_Static(),
2016                 LngSvcMgr_CreateInstance,
2017                 LngSvcMgr::getSupportedServiceNames_Static());
2018         // acquire, because we return an interface pointer instead of a reference
2019         xFactory->acquire();
2020         pRet = xFactory.get();
2021     }
2022     return pRet;
2023 }
2024 
2025 
2026 ///////////////////////////////////////////////////////////////////////////
2027 
2028