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