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