1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
22 #include <com/sun/star/container/XNameContainer.hpp>
23 #include <com/sun/star/lang/Locale.hpp>
24 #include <com/sun/star/lang/NoSupportException.hpp>
25 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
26 #include <com/sun/star/linguistic2/ConversionDictionaryType.hpp>
27 #include <com/sun/star/linguistic2/XConversionDictionaryList.hpp>
28 #include <com/sun/star/uno/Reference.h>
29 #include <com/sun/star/util/XFlushable.hpp>
30 #include <cppuhelper/factory.hxx>
31 #include <cppuhelper/implbase.hxx>
32 #include <comphelper/processfactory.hxx>
33 #include <comphelper/sequence.hxx>
34 #include <cppuhelper/supportsservice.hxx>
35 #include <rtl/instance.hxx>
36 #include <tools/debug.hxx>
37 #include <tools/urlobj.hxx>
38 #include <ucbhelper/content.hxx>
39 #include <unotools/localfilehelper.hxx>
40 #include <unotools/lingucfg.hxx>
41 #include <tools/diagnose_ex.h>
43 #include "convdic.hxx"
44 #include "convdiclist.hxx"
45 #include "hhconvdic.hxx"
47 #include <linguistic/misc.hxx>
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
;
57 #define SN_CONV_DICTIONARY_LIST "com.sun.star.linguistic2.ConversionDictionaryList"
59 static OUString
GetConvDicMainURL( const OUString
&rDicName
, const OUString
&rDirectoryURL
)
61 // build URL to use for new (persistent) dictionaries
63 OUString aFullDicName
= rDicName
+ CONV_DIC_DOT_EXT
;
65 INetURLObject aURLObj
;
66 aURLObj
.SetSmartProtocol( INetProtocol::File
);
67 aURLObj
.SetSmartURL( rDirectoryURL
);
68 aURLObj
.Append( aFullDicName
, INetURLObject::EncodeMechanism::All
);
69 DBG_ASSERT(!aURLObj
.HasError(), "invalid URL");
70 if (aURLObj
.HasError())
73 return aURLObj
.GetMainURL( INetURLObject::DecodeMechanism::ToIUri
);
76 class ConvDicNameContainer
:
77 public cppu::WeakImplHelper
< css::container::XNameContainer
>
79 std::vector
< uno::Reference
< XConversionDictionary
> > aConvDics
;
81 sal_Int32
GetIndexByName_Impl( const OUString
& rName
);
84 ConvDicNameContainer();
85 ConvDicNameContainer(const ConvDicNameContainer
&) = delete;
86 ConvDicNameContainer
& operator=(const ConvDicNameContainer
&) = delete;
89 virtual css::uno::Type SAL_CALL
getElementType( ) override
;
90 virtual sal_Bool SAL_CALL
hasElements( ) override
;
93 virtual css::uno::Any SAL_CALL
getByName( const OUString
& aName
) override
;
94 virtual css::uno::Sequence
< OUString
> SAL_CALL
getElementNames( ) override
;
95 virtual sal_Bool SAL_CALL
hasByName( const OUString
& aName
) override
;
98 virtual void SAL_CALL
replaceByName( const OUString
& aName
, const css::uno::Any
& aElement
) override
;
101 virtual void SAL_CALL
insertByName( const OUString
& aName
, const css::uno::Any
& aElement
) override
;
102 virtual void SAL_CALL
removeByName( const OUString
& Name
) override
;
104 // looks for conversion dictionaries with the specified extension
105 // in the directory and adds them to the container
106 void AddConvDics( const OUString
&rSearchDirPathURL
, const OUString
&rExtension
);
108 // calls Flush for the dictionaries that support XFlushable
109 void FlushDics() const;
111 sal_Int32
GetCount() const { return aConvDics
.size(); }
112 uno::Reference
< XConversionDictionary
> GetByName( const OUString
& rName
);
114 const uno::Reference
< XConversionDictionary
>& GetByIndex( sal_Int32 nIdx
)
116 return aConvDics
[nIdx
];
120 ConvDicNameContainer::ConvDicNameContainer()
124 void ConvDicNameContainer::FlushDics() const
126 sal_Int32 nLen
= aConvDics
.size();
127 for (sal_Int32 i
= 0; i
< nLen
; ++i
)
129 uno::Reference
< util::XFlushable
> xFlush( aConvDics
[i
] , UNO_QUERY
);
138 OSL_FAIL( "flushing of conversion dictionary failed" );
144 sal_Int32
ConvDicNameContainer::GetIndexByName_Impl(
145 const OUString
& rName
)
148 sal_Int32 nLen
= aConvDics
.size();
149 for (sal_Int32 i
= 0; i
< nLen
&& nRes
== -1; ++i
)
151 if (rName
== aConvDics
[i
]->getName())
157 uno::Reference
< XConversionDictionary
> ConvDicNameContainer::GetByName(
158 const OUString
& rName
)
160 uno::Reference
< XConversionDictionary
> xRes
;
161 sal_Int32 nIdx
= GetIndexByName_Impl( rName
);
163 xRes
= aConvDics
[nIdx
];
167 uno::Type SAL_CALL
ConvDicNameContainer::getElementType( )
169 MutexGuard
aGuard( GetLinguMutex() );
170 return cppu::UnoType
<XConversionDictionary
>::get();
173 sal_Bool SAL_CALL
ConvDicNameContainer::hasElements( )
175 MutexGuard
aGuard( GetLinguMutex() );
176 return !aConvDics
.empty();
179 uno::Any SAL_CALL
ConvDicNameContainer::getByName( const OUString
& rName
)
181 MutexGuard
aGuard( GetLinguMutex() );
182 uno::Reference
< XConversionDictionary
> xRes( GetByName( rName
) );
184 throw NoSuchElementException();
185 return makeAny( xRes
);
188 uno::Sequence
< OUString
> SAL_CALL
ConvDicNameContainer::getElementNames( )
190 MutexGuard
aGuard( GetLinguMutex() );
192 std::vector
<OUString
> aRes
;
193 aRes
.reserve(aConvDics
.size());
195 std::transform(aConvDics
.begin(), aConvDics
.end(), std::back_inserter(aRes
),
196 [](const uno::Reference
<XConversionDictionary
>& rDic
) { return rDic
->getName(); });
198 return comphelper::containerToSequence(aRes
);
201 sal_Bool SAL_CALL
ConvDicNameContainer::hasByName( const OUString
& rName
)
203 MutexGuard
aGuard( GetLinguMutex() );
204 return GetByName( rName
).is();
207 void SAL_CALL
ConvDicNameContainer::replaceByName(
208 const OUString
& rName
,
209 const uno::Any
& rElement
)
211 MutexGuard
aGuard( GetLinguMutex() );
213 sal_Int32 nRplcIdx
= GetIndexByName_Impl( rName
);
215 throw NoSuchElementException();
216 uno::Reference
< XConversionDictionary
> xNew
;
218 if (!xNew
.is() || xNew
->getName() != rName
)
219 throw IllegalArgumentException();
220 aConvDics
[ nRplcIdx
] = xNew
;
223 void SAL_CALL
ConvDicNameContainer::insertByName(
224 const OUString
& rName
,
225 const Any
& rElement
)
227 MutexGuard
aGuard( GetLinguMutex() );
229 if (GetByName( rName
).is())
230 throw ElementExistException();
231 uno::Reference
< XConversionDictionary
> xNew
;
233 if (!xNew
.is() || xNew
->getName() != rName
)
234 throw IllegalArgumentException();
236 aConvDics
.push_back(xNew
);
239 void SAL_CALL
ConvDicNameContainer::removeByName( const OUString
& rName
)
241 MutexGuard
aGuard( GetLinguMutex() );
243 sal_Int32 nRplcIdx
= GetIndexByName_Impl( rName
);
245 throw NoSuchElementException();
247 // physically remove dictionary
248 uno::Reference
< XConversionDictionary
> xDel
= aConvDics
[nRplcIdx
];
249 OUString
aName( xDel
->getName() );
250 OUString
aDicMainURL( GetConvDicMainURL( aName
, GetDictionaryWriteablePath() ) );
251 INetURLObject
aObj( aDicMainURL
);
252 DBG_ASSERT( aObj
.GetProtocol() == INetProtocol::File
, "+HangulHanjaOptionsDialog::OkHdl(): non-file URLs cannot be deleted" );
253 if( aObj
.GetProtocol() == INetProtocol::File
)
257 ::ucbhelper::Content
aCnt( aObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
),
258 uno::Reference
< css::ucb::XCommandEnvironment
>(),
259 comphelper::getProcessComponentContext() );
260 aCnt
.executeCommand( "delete", makeAny( true ) );
264 TOOLS_WARN_EXCEPTION( "linguistic", "HangulHanjaOptionsDialog::OkHdl()" );
268 aConvDics
.erase(aConvDics
.begin() + nRplcIdx
);
271 void ConvDicNameContainer::AddConvDics(
272 const OUString
&rSearchDirPathURL
,
273 const OUString
&rExtension
)
275 const Sequence
< OUString
> aDirCnt(
276 utl::LocalFileHelper::GetFolderContents( rSearchDirPathURL
, false ) );
278 for (const OUString
& aURL
: aDirCnt
)
280 sal_Int32 nPos
= aURL
.lastIndexOf('.');
281 OUString
aExt( aURL
.copy(nPos
+ 1).toAsciiLowerCase() );
282 OUString
aSearchExt( rExtension
.toAsciiLowerCase() );
283 if(aExt
!= aSearchExt
)
284 continue; // skip other files
288 if (IsConvDic( aURL
, nLang
, nConvType
))
290 // get decoded dictionary file name
291 INetURLObject
aURLObj( aURL
);
292 OUString aDicName
= aURLObj
.getBase( INetURLObject::LAST_SEGMENT
,
293 true, INetURLObject::DecodeMechanism::WithCharset
);
295 uno::Reference
< XConversionDictionary
> xDic
;
296 if (nLang
== LANGUAGE_KOREAN
&&
297 nConvType
== ConversionDictionaryType::HANGUL_HANJA
)
299 xDic
= new HHConvDic( aDicName
, aURL
);
301 else if ((nLang
== LANGUAGE_CHINESE_SIMPLIFIED
|| nLang
== LANGUAGE_CHINESE_TRADITIONAL
) &&
302 nConvType
== ConversionDictionaryType::SCHINESE_TCHINESE
)
304 xDic
= new ConvDic( aDicName
, nLang
, nConvType
, false, aURL
);
309 insertByName( xDic
->getName(), Any(xDic
) );
317 struct StaticConvDicList
: public rtl::StaticWithInit
<
318 uno::Reference
<XInterface
>, StaticConvDicList
> {
319 uno::Reference
<XInterface
> operator () () {
320 return static_cast<cppu::OWeakObject
*>(new ConvDicList
);
325 void ConvDicList::MyAppExitListener::AtExit()
327 rMyDicList
.FlushDics();
328 StaticConvDicList::get().clear();
331 ConvDicList::ConvDicList() :
332 aEvtListeners( GetLinguMutex() )
336 mxExitListener
= new MyAppExitListener( *this );
337 mxExitListener
->Activate();
340 ConvDicList::~ConvDicList()
342 if (!bDisposing
&& mxNameContainer
.is())
343 mxNameContainer
->FlushDics();
345 mxExitListener
->Deactivate();
348 void ConvDicList::FlushDics()
350 // check only pointer to avoid creating the container when
351 // the dictionaries were not accessed yet
352 if (mxNameContainer
.is())
353 mxNameContainer
->FlushDics();
356 ConvDicNameContainer
& ConvDicList::GetNameContainer()
358 if (!mxNameContainer
.is())
360 mxNameContainer
= new ConvDicNameContainer
;
361 mxNameContainer
->AddConvDics( GetDictionaryWriteablePath(), CONV_DIC_EXT
);
363 // access list of text conversion dictionaries to activate
364 SvtLinguOptions aOpt
;
365 SvtLinguConfig().GetOptions( aOpt
);
366 for (const OUString
& rActiveConvDic
: std::as_const(aOpt
.aActiveConvDics
))
368 uno::Reference
< XConversionDictionary
> xDic
=
369 mxNameContainer
->GetByName( rActiveConvDic
);
371 xDic
->setActive( true );
374 // since there is no UI to active/deactivate the dictionaries
375 // for chinese text conversion they should be activated by default
376 uno::Reference
< XConversionDictionary
> xS2TDic
=
377 mxNameContainer
->GetByName( "ChineseS2T" );
378 uno::Reference
< XConversionDictionary
> xT2SDic
=
379 mxNameContainer
->GetByName( "ChineseT2S" );
381 xS2TDic
->setActive( true );
383 xT2SDic
->setActive( true );
386 return *mxNameContainer
;
389 uno::Reference
< container::XNameContainer
> SAL_CALL
ConvDicList::getDictionaryContainer( )
391 MutexGuard
aGuard( GetLinguMutex() );
393 DBG_ASSERT( mxNameContainer
.is(), "missing name container" );
394 return mxNameContainer
.get();
397 uno::Reference
< XConversionDictionary
> SAL_CALL
ConvDicList::addNewDictionary(
398 const OUString
& rName
,
399 const Locale
& rLocale
,
400 sal_Int16 nConvDicType
)
402 MutexGuard
aGuard( GetLinguMutex() );
404 LanguageType nLang
= LinguLocaleToLanguage( rLocale
);
406 if (GetNameContainer().hasByName( rName
))
407 throw ElementExistException();
409 uno::Reference
< XConversionDictionary
> xRes
;
410 OUString
aDicMainURL( GetConvDicMainURL( rName
, GetDictionaryWriteablePath() ) );
411 if (nLang
== LANGUAGE_KOREAN
&&
412 nConvDicType
== ConversionDictionaryType::HANGUL_HANJA
)
414 xRes
= new HHConvDic( rName
, aDicMainURL
);
416 else if ((nLang
== LANGUAGE_CHINESE_SIMPLIFIED
|| nLang
== LANGUAGE_CHINESE_TRADITIONAL
) &&
417 nConvDicType
== ConversionDictionaryType::SCHINESE_TCHINESE
)
419 xRes
= new ConvDic( rName
, nLang
, nConvDicType
, false, aDicMainURL
);
423 throw NoSupportException();
425 xRes
->setActive( true );
426 GetNameContainer().insertByName( rName
, Any(xRes
) );
430 uno::Sequence
< OUString
> SAL_CALL
ConvDicList::queryConversions(
431 const OUString
& rText
,
434 const Locale
& rLocale
,
435 sal_Int16 nConversionDictionaryType
,
436 ConversionDirection eDirection
,
437 sal_Int32 nTextConversionOptions
)
439 MutexGuard
aGuard( GetLinguMutex() );
441 std::vector
< OUString
> aRes
;
443 bool bSupported
= false;
444 sal_Int32 nLen
= GetNameContainer().GetCount();
445 for (sal_Int32 i
= 0; i
< nLen
; ++i
)
447 const uno::Reference
< XConversionDictionary
> xDic( GetNameContainer().GetByIndex(i
) );
448 bool bMatch
= xDic
.is() &&
449 xDic
->getLocale() == rLocale
&&
450 xDic
->getConversionType() == nConversionDictionaryType
;
451 bSupported
|= bMatch
;
452 if (bMatch
&& xDic
->isActive())
454 const Sequence
< OUString
> aNewConv( xDic
->getConversions(
455 rText
, nStartPos
, nLength
,
456 eDirection
, nTextConversionOptions
) );
457 std::copy(aNewConv
.begin(), aNewConv
.end(), std::back_inserter(aRes
));
462 throw NoSupportException();
464 return comphelper::containerToSequence(aRes
);
467 sal_Int16 SAL_CALL
ConvDicList::queryMaxCharCount(
468 const Locale
& rLocale
,
469 sal_Int16 nConversionDictionaryType
,
470 ConversionDirection eDirection
)
472 MutexGuard
aGuard( GetLinguMutex() );
476 sal_Int32 nLen
= GetNameContainer().GetCount();
477 for (sal_Int32 i
= 0; i
< nLen
; ++i
)
479 const uno::Reference
< XConversionDictionary
> xDic( GetNameContainer().GetByIndex(i
) );
481 xDic
->getLocale() == rLocale
&&
482 xDic
->getConversionType() == nConversionDictionaryType
)
484 sal_Int16 nC
= xDic
->getMaxCharCount( eDirection
);
492 void SAL_CALL
ConvDicList::dispose( )
494 MutexGuard
aGuard( GetLinguMutex() );
498 EventObject
aEvtObj( static_cast<XConversionDictionaryList
*>(this) );
499 aEvtListeners
.disposeAndClear( aEvtObj
);
505 void SAL_CALL
ConvDicList::addEventListener(
506 const uno::Reference
< XEventListener
>& rxListener
)
508 MutexGuard
aGuard( GetLinguMutex() );
509 if (!bDisposing
&& rxListener
.is())
510 aEvtListeners
.addInterface( rxListener
);
513 void SAL_CALL
ConvDicList::removeEventListener(
514 const uno::Reference
< XEventListener
>& rxListener
)
516 MutexGuard
aGuard( GetLinguMutex() );
517 if (!bDisposing
&& rxListener
.is())
518 aEvtListeners
.removeInterface( rxListener
);
521 OUString SAL_CALL
ConvDicList::getImplementationName()
523 return getImplementationName_Static();
526 sal_Bool SAL_CALL
ConvDicList::supportsService( const OUString
& rServiceName
)
528 return cppu::supportsService(this, rServiceName
);
531 uno::Sequence
< OUString
> SAL_CALL
ConvDicList::getSupportedServiceNames()
533 return getSupportedServiceNames_Static();
536 uno::Sequence
< OUString
> ConvDicList::getSupportedServiceNames_Static()
539 uno::Sequence
<OUString
> aSNS
{ SN_CONV_DICTIONARY_LIST
};
543 /// @throws css::uno::Exception
544 static uno::Reference
< uno::XInterface
> ConvDicList_CreateInstance(
545 const uno::Reference
< XMultiServiceFactory
> & /*rSMgr*/ )
547 return StaticConvDicList::get();
550 void * ConvDicList_getFactory(
551 const sal_Char
* pImplName
,
552 XMultiServiceFactory
* pServiceManager
)
554 void * pRet
= nullptr;
555 if ( ConvDicList::getImplementationName_Static().equalsAscii( pImplName
) )
557 uno::Reference
< XSingleServiceFactory
> xFactory
=
558 cppu::createOneInstanceFactory(
560 ConvDicList::getImplementationName_Static(),
561 ConvDicList_CreateInstance
,
562 ConvDicList::getSupportedServiceNames_Static());
563 // acquire, because we return an interface pointer instead of a reference
565 pRet
= xFactory
.get();
570 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */