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/linguistic2/ConversionDictionaryType.hpp>
26 #include <com/sun/star/linguistic2/XConversionDictionary.hpp>
27 #include <com/sun/star/linguistic2/XConversionDictionaryList.hpp>
28 #include <com/sun/star/registry/XRegistryKey.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/stream.hxx>
38 #include <tools/urlobj.hxx>
39 #include <ucbhelper/content.hxx>
40 #include <unotools/localfilehelper.hxx>
41 #include <unotools/lingucfg.hxx>
42 #include <unotools/pathoptions.hxx>
43 #include <unotools/useroptions.hxx>
45 #include "convdic.hxx"
46 #include "convdiclist.hxx"
48 #include "hhconvdic.hxx"
50 #include <linguistic/misc.hxx>
53 using namespace com::sun::star
;
54 using namespace com::sun::star::lang
;
55 using namespace com::sun::star::uno
;
56 using namespace com::sun::star::container
;
57 using namespace com::sun::star::linguistic2
;
58 using namespace linguistic
;
60 #define SN_CONV_DICTIONARY_LIST "com.sun.star.linguistic2.ConversionDictionaryList"
62 OUString
GetConvDicMainURL( const OUString
&rDicName
, const OUString
&rDirectoryURL
)
64 // build URL to use for new (persistent) dictionaries
66 OUString aFullDicName
= rDicName
+ CONV_DIC_DOT_EXT
;
68 INetURLObject aURLObj
;
69 aURLObj
.SetSmartProtocol( INetProtocol::File
);
70 aURLObj
.SetSmartURL( rDirectoryURL
);
71 aURLObj
.Append( aFullDicName
, INetURLObject::EncodeMechanism::All
);
72 DBG_ASSERT(!aURLObj
.HasError(), "invalid URL");
73 if (aURLObj
.HasError())
76 return aURLObj
.GetMainURL( INetURLObject::DecodeMechanism::ToIUri
);
79 class ConvDicNameContainer
:
80 public cppu::WeakImplHelper
< css::container::XNameContainer
>
82 std::vector
< uno::Reference
< XConversionDictionary
> > aConvDics
;
84 sal_Int32
GetIndexByName_Impl( const OUString
& rName
);
87 ConvDicNameContainer();
88 ConvDicNameContainer(const ConvDicNameContainer
&) = delete;
89 ConvDicNameContainer
& operator=(const ConvDicNameContainer
&) = delete;
92 virtual css::uno::Type SAL_CALL
getElementType( ) override
;
93 virtual sal_Bool SAL_CALL
hasElements( ) override
;
96 virtual css::uno::Any SAL_CALL
getByName( const OUString
& aName
) override
;
97 virtual css::uno::Sequence
< OUString
> SAL_CALL
getElementNames( ) override
;
98 virtual sal_Bool SAL_CALL
hasByName( const OUString
& aName
) override
;
101 virtual void SAL_CALL
replaceByName( const OUString
& aName
, const css::uno::Any
& aElement
) override
;
104 virtual void SAL_CALL
insertByName( const OUString
& aName
, const css::uno::Any
& aElement
) override
;
105 virtual void SAL_CALL
removeByName( const OUString
& Name
) override
;
107 // looks for conversion dictionaries with the specified extension
108 // in the directory and adds them to the container
109 void AddConvDics( const OUString
&rSearchDirPathURL
, const OUString
&rExtension
);
111 // calls Flush for the dictionaries that support XFlushable
112 void FlushDics() const;
114 sal_Int32
GetCount() const { return aConvDics
.size(); }
115 uno::Reference
< XConversionDictionary
> GetByName( const OUString
& rName
);
117 const uno::Reference
< XConversionDictionary
>& GetByIndex( sal_Int32 nIdx
)
119 return aConvDics
[nIdx
];
123 ConvDicNameContainer::ConvDicNameContainer()
127 void ConvDicNameContainer::FlushDics() const
129 sal_Int32 nLen
= aConvDics
.size();
130 for (sal_Int32 i
= 0; i
< nLen
; ++i
)
132 uno::Reference
< util::XFlushable
> xFlush( aConvDics
[i
] , UNO_QUERY
);
141 OSL_FAIL( "flushing of conversion dictionary failed" );
147 sal_Int32
ConvDicNameContainer::GetIndexByName_Impl(
148 const OUString
& rName
)
151 sal_Int32 nLen
= aConvDics
.size();
152 for (sal_Int32 i
= 0; i
< nLen
&& nRes
== -1; ++i
)
154 if (rName
== aConvDics
[i
]->getName())
160 uno::Reference
< XConversionDictionary
> ConvDicNameContainer::GetByName(
161 const OUString
& rName
)
163 uno::Reference
< XConversionDictionary
> xRes
;
164 sal_Int32 nIdx
= GetIndexByName_Impl( rName
);
166 xRes
= aConvDics
[nIdx
];
170 uno::Type SAL_CALL
ConvDicNameContainer::getElementType( )
172 MutexGuard
aGuard( GetLinguMutex() );
173 return cppu::UnoType
<XConversionDictionary
>::get();
176 sal_Bool SAL_CALL
ConvDicNameContainer::hasElements( )
178 MutexGuard
aGuard( GetLinguMutex() );
179 return !aConvDics
.empty();
182 uno::Any SAL_CALL
ConvDicNameContainer::getByName( const OUString
& rName
)
184 MutexGuard
aGuard( GetLinguMutex() );
185 uno::Reference
< XConversionDictionary
> xRes( GetByName( rName
) );
187 throw NoSuchElementException();
188 return makeAny( xRes
);
191 uno::Sequence
< OUString
> SAL_CALL
ConvDicNameContainer::getElementNames( )
193 MutexGuard
aGuard( GetLinguMutex() );
195 sal_Int32 nLen
= aConvDics
.size();
196 uno::Sequence
< OUString
> aRes( nLen
);
197 OUString
*pName
= aRes
.getArray();
198 for (sal_Int32 i
= 0; i
< nLen
; ++i
)
199 pName
[i
] = aConvDics
[i
]->getName();
203 sal_Bool SAL_CALL
ConvDicNameContainer::hasByName( const OUString
& rName
)
205 MutexGuard
aGuard( GetLinguMutex() );
206 return GetByName( rName
).is();
209 void SAL_CALL
ConvDicNameContainer::replaceByName(
210 const OUString
& rName
,
211 const uno::Any
& rElement
)
213 MutexGuard
aGuard( GetLinguMutex() );
215 sal_Int32 nRplcIdx
= GetIndexByName_Impl( rName
);
217 throw NoSuchElementException();
218 uno::Reference
< XConversionDictionary
> xNew
;
220 if (!xNew
.is() || xNew
->getName() != rName
)
221 throw IllegalArgumentException();
222 aConvDics
[ nRplcIdx
] = xNew
;
225 void SAL_CALL
ConvDicNameContainer::insertByName(
226 const OUString
& rName
,
227 const Any
& rElement
)
229 MutexGuard
aGuard( GetLinguMutex() );
231 if (GetByName( rName
).is())
232 throw ElementExistException();
233 uno::Reference
< XConversionDictionary
> xNew
;
235 if (!xNew
.is() || xNew
->getName() != rName
)
236 throw IllegalArgumentException();
238 aConvDics
.push_back(xNew
);
241 void SAL_CALL
ConvDicNameContainer::removeByName( const OUString
& rName
)
243 MutexGuard
aGuard( GetLinguMutex() );
245 sal_Int32 nRplcIdx
= GetIndexByName_Impl( rName
);
247 throw NoSuchElementException();
249 // physically remove dictionary
250 uno::Reference
< XConversionDictionary
> xDel
= aConvDics
[nRplcIdx
];
251 OUString
aName( xDel
->getName() );
252 OUString
aDicMainURL( GetConvDicMainURL( aName
, GetDictionaryWriteablePath() ) );
253 INetURLObject
aObj( aDicMainURL
);
254 DBG_ASSERT( aObj
.GetProtocol() == INetProtocol::File
, "+HangulHanjaOptionsDialog::OkHdl(): non-file URLs cannot be deleted" );
255 if( aObj
.GetProtocol() == INetProtocol::File
)
259 ::ucbhelper::Content
aCnt( aObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
),
260 uno::Reference
< css::ucb::XCommandEnvironment
>(),
261 comphelper::getProcessComponentContext() );
262 aCnt
.executeCommand( "delete", makeAny( true ) );
264 catch( css::ucb::CommandAbortedException
& )
266 SAL_WARN( "linguistic", "HangulHanjaOptionsDialog::OkHdl(): CommandAbortedException" );
270 SAL_WARN( "linguistic", "HangulHanjaOptionsDialog::OkHdl(): Any other exception" );
274 aConvDics
.erase(aConvDics
.begin() + nRplcIdx
);
277 void ConvDicNameContainer::AddConvDics(
278 const OUString
&rSearchDirPathURL
,
279 const OUString
&rExtension
)
281 const Sequence
< OUString
> aDirCnt(
282 utl::LocalFileHelper::GetFolderContents( rSearchDirPathURL
, false ) );
283 const OUString
*pDirCnt
= aDirCnt
.getConstArray();
284 sal_Int32 nEntries
= aDirCnt
.getLength();
286 for (sal_Int32 i
= 0; i
< nEntries
; ++i
)
288 OUString
aURL( pDirCnt
[i
] );
290 sal_Int32 nPos
= aURL
.lastIndexOf('.');
291 OUString
aExt( aURL
.copy(nPos
+ 1).toAsciiLowerCase() );
292 OUString
aSearchExt( rExtension
.toAsciiLowerCase() );
293 if(aExt
!= aSearchExt
)
294 continue; // skip other files
298 if (IsConvDic( aURL
, nLang
, nConvType
))
300 // get decoded dictionary file name
301 INetURLObject
aURLObj( aURL
);
302 OUString aDicName
= aURLObj
.getBase( INetURLObject::LAST_SEGMENT
,
303 true, INetURLObject::DecodeMechanism::WithCharset
);
305 uno::Reference
< XConversionDictionary
> xDic
;
306 if (nLang
== LANGUAGE_KOREAN
&&
307 nConvType
== ConversionDictionaryType::HANGUL_HANJA
)
309 xDic
= new HHConvDic( aDicName
, aURL
);
311 else if ((nLang
== LANGUAGE_CHINESE_SIMPLIFIED
|| nLang
== LANGUAGE_CHINESE_TRADITIONAL
) &&
312 nConvType
== ConversionDictionaryType::SCHINESE_TCHINESE
)
314 xDic
= new ConvDic( aDicName
, nLang
, nConvType
, false, aURL
);
319 insertByName( xDic
->getName(), Any(xDic
) );
327 struct StaticConvDicList
: public rtl::StaticWithInit
<
328 uno::Reference
<XInterface
>, StaticConvDicList
> {
329 uno::Reference
<XInterface
> operator () () {
330 return static_cast<cppu::OWeakObject
*>(new ConvDicList
);
335 void ConvDicList::MyAppExitListener::AtExit()
337 rMyDicList
.FlushDics();
338 StaticConvDicList::get().clear();
341 ConvDicList::ConvDicList() :
342 aEvtListeners( GetLinguMutex() )
346 mxExitListener
= new MyAppExitListener( *this );
347 mxExitListener
->Activate();
350 ConvDicList::~ConvDicList()
352 if (!bDisposing
&& mxNameContainer
.is())
353 mxNameContainer
->FlushDics();
355 mxExitListener
->Deactivate();
358 void ConvDicList::FlushDics()
360 // check only pointer to avoid creating the container when
361 // the dictionaries were not accessed yet
362 if (mxNameContainer
.is())
363 mxNameContainer
->FlushDics();
366 ConvDicNameContainer
& ConvDicList::GetNameContainer()
368 if (!mxNameContainer
.is())
370 mxNameContainer
= new ConvDicNameContainer
;
371 mxNameContainer
->AddConvDics( GetDictionaryWriteablePath(), CONV_DIC_EXT
);
373 // access list of text conversion dictionaries to activate
374 SvtLinguOptions aOpt
;
375 SvtLinguConfig().GetOptions( aOpt
);
376 sal_Int32 nLen
= aOpt
.aActiveConvDics
.getLength();
377 const OUString
*pActiveConvDics
= aOpt
.aActiveConvDics
.getConstArray();
378 for (sal_Int32 i
= 0; i
< nLen
; ++i
)
380 uno::Reference
< XConversionDictionary
> xDic
=
381 mxNameContainer
->GetByName( pActiveConvDics
[i
] );
383 xDic
->setActive( true );
386 // since there is no UI to active/deactivate the dictionaries
387 // for chinese text conversion they should be activated by default
388 uno::Reference
< XConversionDictionary
> xS2TDic(
389 mxNameContainer
->GetByName( "ChineseS2T" ), UNO_QUERY
);
390 uno::Reference
< XConversionDictionary
> xT2SDic(
391 mxNameContainer
->GetByName( "ChineseT2S" ), UNO_QUERY
);
393 xS2TDic
->setActive( true );
395 xT2SDic
->setActive( true );
398 return *mxNameContainer
;
401 uno::Reference
< container::XNameContainer
> SAL_CALL
ConvDicList::getDictionaryContainer( )
403 MutexGuard
aGuard( GetLinguMutex() );
405 DBG_ASSERT( mxNameContainer
.is(), "missing name container" );
406 return mxNameContainer
.get();
409 uno::Reference
< XConversionDictionary
> SAL_CALL
ConvDicList::addNewDictionary(
410 const OUString
& rName
,
411 const Locale
& rLocale
,
412 sal_Int16 nConvDicType
)
414 MutexGuard
aGuard( GetLinguMutex() );
416 LanguageType nLang
= LinguLocaleToLanguage( rLocale
);
418 if (GetNameContainer().hasByName( rName
))
419 throw ElementExistException();
421 uno::Reference
< XConversionDictionary
> xRes
;
422 OUString
aDicMainURL( GetConvDicMainURL( rName
, GetDictionaryWriteablePath() ) );
423 if (nLang
== LANGUAGE_KOREAN
&&
424 nConvDicType
== ConversionDictionaryType::HANGUL_HANJA
)
426 xRes
= new HHConvDic( rName
, aDicMainURL
);
428 else if ((nLang
== LANGUAGE_CHINESE_SIMPLIFIED
|| nLang
== LANGUAGE_CHINESE_TRADITIONAL
) &&
429 nConvDicType
== ConversionDictionaryType::SCHINESE_TCHINESE
)
431 xRes
= new ConvDic( rName
, nLang
, nConvDicType
, false, aDicMainURL
);
435 throw NoSupportException();
437 xRes
->setActive( true );
438 GetNameContainer().insertByName( rName
, Any(xRes
) );
442 uno::Sequence
< OUString
> SAL_CALL
ConvDicList::queryConversions(
443 const OUString
& rText
,
446 const Locale
& rLocale
,
447 sal_Int16 nConversionDictionaryType
,
448 ConversionDirection eDirection
,
449 sal_Int32 nTextConversionOptions
)
451 MutexGuard
aGuard( GetLinguMutex() );
453 std::vector
< OUString
> aRes
;
455 bool bSupported
= false;
456 sal_Int32 nLen
= GetNameContainer().GetCount();
457 for (sal_Int32 i
= 0; i
< nLen
; ++i
)
459 const uno::Reference
< XConversionDictionary
> xDic( GetNameContainer().GetByIndex(i
) );
460 bool bMatch
= xDic
.is() &&
461 xDic
->getLocale() == rLocale
&&
462 xDic
->getConversionType() == nConversionDictionaryType
;
463 bSupported
|= bMatch
;
464 if (bMatch
&& xDic
->isActive())
466 Sequence
< OUString
> aNewConv( xDic
->getConversions(
467 rText
, nStartPos
, nLength
,
468 eDirection
, nTextConversionOptions
) );
469 sal_Int32 nNewLen
= aNewConv
.getLength();
472 for (sal_Int32 k
= 0; k
< nNewLen
; ++k
)
473 aRes
.push_back(aNewConv
[k
]);
479 throw NoSupportException();
481 return comphelper::containerToSequence(aRes
);
484 sal_Int16 SAL_CALL
ConvDicList::queryMaxCharCount(
485 const Locale
& rLocale
,
486 sal_Int16 nConversionDictionaryType
,
487 ConversionDirection eDirection
)
489 MutexGuard
aGuard( GetLinguMutex() );
493 sal_Int32 nLen
= GetNameContainer().GetCount();
494 for (sal_Int32 i
= 0; i
< nLen
; ++i
)
496 const uno::Reference
< XConversionDictionary
> xDic( GetNameContainer().GetByIndex(i
) );
498 xDic
->getLocale() == rLocale
&&
499 xDic
->getConversionType() == nConversionDictionaryType
)
501 sal_Int16 nC
= xDic
->getMaxCharCount( eDirection
);
509 void SAL_CALL
ConvDicList::dispose( )
511 MutexGuard
aGuard( GetLinguMutex() );
515 EventObject
aEvtObj( static_cast<XConversionDictionaryList
*>(this) );
516 aEvtListeners
.disposeAndClear( aEvtObj
);
522 void SAL_CALL
ConvDicList::addEventListener(
523 const uno::Reference
< XEventListener
>& rxListener
)
525 MutexGuard
aGuard( GetLinguMutex() );
526 if (!bDisposing
&& rxListener
.is())
527 aEvtListeners
.addInterface( rxListener
);
530 void SAL_CALL
ConvDicList::removeEventListener(
531 const uno::Reference
< XEventListener
>& rxListener
)
533 MutexGuard
aGuard( GetLinguMutex() );
534 if (!bDisposing
&& rxListener
.is())
535 aEvtListeners
.removeInterface( rxListener
);
538 OUString SAL_CALL
ConvDicList::getImplementationName()
540 return getImplementationName_Static();
543 sal_Bool SAL_CALL
ConvDicList::supportsService( const OUString
& rServiceName
)
545 return cppu::supportsService(this, rServiceName
);
548 uno::Sequence
< OUString
> SAL_CALL
ConvDicList::getSupportedServiceNames()
550 return getSupportedServiceNames_Static();
553 uno::Sequence
< OUString
> ConvDicList::getSupportedServiceNames_Static()
556 uno::Sequence
<OUString
> aSNS
{ SN_CONV_DICTIONARY_LIST
};
560 /// @throws css::uno::Exception
561 uno::Reference
< uno::XInterface
> ConvDicList_CreateInstance(
562 const uno::Reference
< XMultiServiceFactory
> & /*rSMgr*/ )
564 return StaticConvDicList::get();
567 void * ConvDicList_getFactory(
568 const sal_Char
* pImplName
,
569 XMultiServiceFactory
* pServiceManager
)
571 void * pRet
= nullptr;
572 if ( ConvDicList::getImplementationName_Static().equalsAscii( pImplName
) )
574 uno::Reference
< XSingleServiceFactory
> xFactory
=
575 cppu::createOneInstanceFactory(
577 ConvDicList::getImplementationName_Static(),
578 ConvDicList_CreateInstance
,
579 ConvDicList::getSupportedServiceNames_Static());
580 // acquire, because we return an interface pointer instead of a reference
582 pRet
= xFactory
.get();
587 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */