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 <string_view>
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>
44 #include "convdic.hxx"
45 #include "convdiclist.hxx"
46 #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 static OUString
GetConvDicMainURL( std::u16string_view rDicName
, const OUString
&rDirectoryURL
)
59 // build URL to use for new (persistent) dictionaries
61 OUString aFullDicName
= OUString::Concat(rDicName
) + CONV_DIC_DOT_EXT
;
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())
71 return aURLObj
.GetMainURL( INetURLObject::DecodeMechanism::ToIUri
);
74 class ConvDicNameContainer
:
75 public cppu::WeakImplHelper
< css::container::XNameContainer
>
77 std::vector
< uno::Reference
< XConversionDictionary
> > aConvDics
;
79 sal_Int32
GetIndexByName_Impl( std::u16string_view rName
);
82 ConvDicNameContainer();
83 ConvDicNameContainer(const ConvDicNameContainer
&) = delete;
84 ConvDicNameContainer
& operator=(const ConvDicNameContainer
&) = delete;
87 virtual css::uno::Type SAL_CALL
getElementType( ) override
;
88 virtual sal_Bool SAL_CALL
hasElements( ) override
;
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
;
96 virtual void SAL_CALL
replaceByName( const OUString
& aName
, const css::uno::Any
& aElement
) override
;
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
;
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
);
106 // calls Flush for the dictionaries that support XFlushable
107 void FlushDics() const;
109 sal_Int32
GetCount() const { return aConvDics
.size(); }
110 uno::Reference
< XConversionDictionary
> GetByName( std::u16string_view rName
);
112 const uno::Reference
< XConversionDictionary
>& GetByIndex( sal_Int32 nIdx
)
114 return aConvDics
[nIdx
];
118 ConvDicNameContainer::ConvDicNameContainer()
122 void ConvDicNameContainer::FlushDics() const
124 sal_Int32 nLen
= aConvDics
.size();
125 for (sal_Int32 i
= 0; i
< nLen
; ++i
)
127 uno::Reference
< util::XFlushable
> xFlush( aConvDics
[i
] , UNO_QUERY
);
136 OSL_FAIL( "flushing of conversion dictionary failed" );
142 sal_Int32
ConvDicNameContainer::GetIndexByName_Impl(
143 std::u16string_view rName
)
146 sal_Int32 nLen
= aConvDics
.size();
147 for (sal_Int32 i
= 0; i
< nLen
&& nRes
== -1; ++i
)
149 if (rName
== aConvDics
[i
]->getName())
155 uno::Reference
< XConversionDictionary
> ConvDicNameContainer::GetByName(
156 std::u16string_view rName
)
158 uno::Reference
< XConversionDictionary
> xRes
;
159 sal_Int32 nIdx
= GetIndexByName_Impl( rName
);
161 xRes
= aConvDics
[nIdx
];
165 uno::Type SAL_CALL
ConvDicNameContainer::getElementType( )
167 MutexGuard
aGuard( GetLinguMutex() );
168 return cppu::UnoType
<XConversionDictionary
>::get();
171 sal_Bool SAL_CALL
ConvDicNameContainer::hasElements( )
173 MutexGuard
aGuard( GetLinguMutex() );
174 return !aConvDics
.empty();
177 uno::Any SAL_CALL
ConvDicNameContainer::getByName( const OUString
& rName
)
179 MutexGuard
aGuard( GetLinguMutex() );
180 uno::Reference
< XConversionDictionary
> xRes( GetByName( rName
) );
182 throw NoSuchElementException();
183 return makeAny( xRes
);
186 uno::Sequence
< OUString
> SAL_CALL
ConvDicNameContainer::getElementNames( )
188 MutexGuard
aGuard( GetLinguMutex() );
190 std::vector
<OUString
> aRes
;
191 aRes
.reserve(aConvDics
.size());
193 std::transform(aConvDics
.begin(), aConvDics
.end(), std::back_inserter(aRes
),
194 [](const uno::Reference
<XConversionDictionary
>& rDic
) { return rDic
->getName(); });
196 return comphelper::containerToSequence(aRes
);
199 sal_Bool SAL_CALL
ConvDicNameContainer::hasByName( const OUString
& rName
)
201 MutexGuard
aGuard( GetLinguMutex() );
202 return GetByName( rName
).is();
205 void SAL_CALL
ConvDicNameContainer::replaceByName(
206 const OUString
& rName
,
207 const uno::Any
& rElement
)
209 MutexGuard
aGuard( GetLinguMutex() );
211 sal_Int32 nRplcIdx
= GetIndexByName_Impl( rName
);
213 throw NoSuchElementException();
214 uno::Reference
< XConversionDictionary
> xNew
;
216 if (!xNew
.is() || xNew
->getName() != rName
)
217 throw IllegalArgumentException();
218 aConvDics
[ nRplcIdx
] = xNew
;
221 void SAL_CALL
ConvDicNameContainer::insertByName(
222 const OUString
& rName
,
223 const Any
& rElement
)
225 MutexGuard
aGuard( GetLinguMutex() );
227 if (GetByName( rName
).is())
228 throw ElementExistException();
229 uno::Reference
< XConversionDictionary
> xNew
;
231 if (!xNew
.is() || xNew
->getName() != rName
)
232 throw IllegalArgumentException();
234 aConvDics
.push_back(xNew
);
237 void SAL_CALL
ConvDicNameContainer::removeByName( const OUString
& rName
)
239 MutexGuard
aGuard( GetLinguMutex() );
241 sal_Int32 nRplcIdx
= GetIndexByName_Impl( rName
);
243 throw NoSuchElementException();
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
)
255 ::ucbhelper::Content
aCnt( aObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
),
256 uno::Reference
< css::ucb::XCommandEnvironment
>(),
257 comphelper::getProcessComponentContext() );
258 aCnt
.executeCommand( "delete", makeAny( true ) );
262 TOOLS_WARN_EXCEPTION( "linguistic", "HangulHanjaOptionsDialog::OkHdl()" );
266 aConvDics
.erase(aConvDics
.begin() + nRplcIdx
);
269 void ConvDicNameContainer::AddConvDics(
270 const OUString
&rSearchDirPathURL
,
271 const OUString
&rExtension
)
273 const Sequence
< OUString
> aDirCnt(
274 utl::LocalFileHelper::GetFolderContents( rSearchDirPathURL
, false ) );
276 for (const OUString
& aURL
: aDirCnt
)
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
286 if (IsConvDic( aURL
, nLang
, nConvType
))
288 // get decoded dictionary file name
289 INetURLObject
aURLObj( aURL
);
290 OUString aDicName
= aURLObj
.getBase( INetURLObject::LAST_SEGMENT
,
291 true, INetURLObject::DecodeMechanism::WithCharset
);
293 uno::Reference
< XConversionDictionary
> xDic
;
294 if (nLang
== LANGUAGE_KOREAN
&&
295 nConvType
== ConversionDictionaryType::HANGUL_HANJA
)
297 xDic
= new HHConvDic( aDicName
, aURL
);
299 else if ((nLang
== LANGUAGE_CHINESE_SIMPLIFIED
|| nLang
== LANGUAGE_CHINESE_TRADITIONAL
) &&
300 nConvType
== ConversionDictionaryType::SCHINESE_TCHINESE
)
302 xDic
= new ConvDic( aDicName
, nLang
, nConvType
, false, aURL
);
307 insertByName( xDic
->getName(), Any(xDic
) );
315 struct StaticConvDicList
: public rtl::StaticWithInit
<
316 rtl::Reference
<ConvDicList
>, StaticConvDicList
> {
317 rtl::Reference
<ConvDicList
> operator () () {
318 return new ConvDicList
;
323 void ConvDicList::MyAppExitListener::AtExit()
325 rMyDicList
.FlushDics();
326 StaticConvDicList::get().clear();
329 ConvDicList::ConvDicList() :
330 aEvtListeners( GetLinguMutex() )
334 mxExitListener
= new MyAppExitListener( *this );
335 mxExitListener
->Activate();
338 ConvDicList::~ConvDicList()
340 if (!bDisposing
&& mxNameContainer
.is())
341 mxNameContainer
->FlushDics();
343 mxExitListener
->Deactivate();
346 void ConvDicList::FlushDics()
348 // check only pointer to avoid creating the container when
349 // the dictionaries were not accessed yet
350 if (mxNameContainer
.is())
351 mxNameContainer
->FlushDics();
354 ConvDicNameContainer
& ConvDicList::GetNameContainer()
356 if (!mxNameContainer
.is())
358 mxNameContainer
= new ConvDicNameContainer
;
359 mxNameContainer
->AddConvDics( GetDictionaryWriteablePath(), CONV_DIC_EXT
);
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
))
366 uno::Reference
< XConversionDictionary
> xDic
=
367 mxNameContainer
->GetByName( rActiveConvDic
);
369 xDic
->setActive( true );
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" );
379 xS2TDic
->setActive( true );
381 xT2SDic
->setActive( true );
384 return *mxNameContainer
;
387 uno::Reference
< container::XNameContainer
> SAL_CALL
ConvDicList::getDictionaryContainer( )
389 MutexGuard
aGuard( GetLinguMutex() );
391 DBG_ASSERT( mxNameContainer
.is(), "missing name container" );
392 return mxNameContainer
;
395 uno::Reference
< XConversionDictionary
> SAL_CALL
ConvDicList::addNewDictionary(
396 const OUString
& rName
,
397 const Locale
& rLocale
,
398 sal_Int16 nConvDicType
)
400 MutexGuard
aGuard( GetLinguMutex() );
402 LanguageType nLang
= LinguLocaleToLanguage( rLocale
);
404 if (GetNameContainer().hasByName( rName
))
405 throw ElementExistException();
407 uno::Reference
< XConversionDictionary
> xRes
;
408 OUString
aDicMainURL( GetConvDicMainURL( rName
, GetDictionaryWriteablePath() ) );
409 if (nLang
== LANGUAGE_KOREAN
&&
410 nConvDicType
== ConversionDictionaryType::HANGUL_HANJA
)
412 xRes
= new HHConvDic( rName
, aDicMainURL
);
414 else if ((nLang
== LANGUAGE_CHINESE_SIMPLIFIED
|| nLang
== LANGUAGE_CHINESE_TRADITIONAL
) &&
415 nConvDicType
== ConversionDictionaryType::SCHINESE_TCHINESE
)
417 xRes
= new ConvDic( rName
, nLang
, nConvDicType
, false, aDicMainURL
);
421 throw NoSupportException();
423 xRes
->setActive( true );
424 GetNameContainer().insertByName( rName
, Any(xRes
) );
428 uno::Sequence
< OUString
> SAL_CALL
ConvDicList::queryConversions(
429 const OUString
& rText
,
432 const Locale
& rLocale
,
433 sal_Int16 nConversionDictionaryType
,
434 ConversionDirection eDirection
,
435 sal_Int32 nTextConversionOptions
)
437 MutexGuard
aGuard( GetLinguMutex() );
439 std::vector
< OUString
> aRes
;
441 bool bSupported
= false;
442 sal_Int32 nLen
= GetNameContainer().GetCount();
443 for (sal_Int32 i
= 0; i
< nLen
; ++i
)
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())
452 const Sequence
< OUString
> aNewConv( xDic
->getConversions(
453 rText
, nStartPos
, nLength
,
454 eDirection
, nTextConversionOptions
) );
455 aRes
.insert( aRes
.end(), aNewConv
.begin(), aNewConv
.end() );
460 throw NoSupportException();
462 return comphelper::containerToSequence(aRes
);
465 sal_Int16 SAL_CALL
ConvDicList::queryMaxCharCount(
466 const Locale
& rLocale
,
467 sal_Int16 nConversionDictionaryType
,
468 ConversionDirection eDirection
)
470 MutexGuard
aGuard( GetLinguMutex() );
474 sal_Int32 nLen
= GetNameContainer().GetCount();
475 for (sal_Int32 i
= 0; i
< nLen
; ++i
)
477 const uno::Reference
< XConversionDictionary
> xDic( GetNameContainer().GetByIndex(i
) );
479 xDic
->getLocale() == rLocale
&&
480 xDic
->getConversionType() == nConversionDictionaryType
)
482 sal_Int16 nC
= xDic
->getMaxCharCount( eDirection
);
490 void SAL_CALL
ConvDicList::dispose( )
492 MutexGuard
aGuard( GetLinguMutex() );
496 EventObject
aEvtObj( static_cast<XConversionDictionaryList
*>(this) );
497 aEvtListeners
.disposeAndClear( aEvtObj
);
503 void SAL_CALL
ConvDicList::addEventListener(
504 const uno::Reference
< XEventListener
>& rxListener
)
506 MutexGuard
aGuard( GetLinguMutex() );
507 if (!bDisposing
&& rxListener
.is())
508 aEvtListeners
.addInterface( rxListener
);
511 void SAL_CALL
ConvDicList::removeEventListener(
512 const uno::Reference
< XEventListener
>& rxListener
)
514 MutexGuard
aGuard( GetLinguMutex() );
515 if (!bDisposing
&& rxListener
.is())
516 aEvtListeners
.removeInterface( rxListener
);
519 OUString SAL_CALL
ConvDicList::getImplementationName()
521 return "com.sun.star.lingu2.ConvDicList";
524 sal_Bool SAL_CALL
ConvDicList::supportsService( const OUString
& rServiceName
)
526 return cppu::supportsService(this, rServiceName
);
529 uno::Sequence
< OUString
> SAL_CALL
ConvDicList::getSupportedServiceNames()
531 return { "com.sun.star.linguistic2.ConversionDictionaryList" };
534 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
535 linguistic_ConvDicList_get_implementation(
536 css::uno::XComponentContext
* , css::uno::Sequence
<css::uno::Any
> const&)
538 return cppu::acquire(StaticConvDicList::get().get());
542 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */