summaryrefslogtreecommitdiff
path: root/linguistic
diff options
context:
space:
mode:
authorCaolán McNamara <caolanm@redhat.com>2012-07-11 09:50:49 +0100
committerCaolán McNamara <caolanm@redhat.com>2012-07-11 09:59:10 +0100
commit464f69b8bb5f64f9f4660ba2e2095cdc1c65952b (patch)
tree91ba961a4827a03874629bd78ff700e8d3f02fef /linguistic
parent7fe61368cead27eef5d6175abd1cc254e99e8a05 (diff)
Resolves: rhbz#836937 insanely slow with Zemberek installed
Zemberek is a java spellchecker extension. With it installed the collecting of spellchecker information on first activation of spellchecking is insanely slow as the cache of spellcheckers is thrown away on each iteration through each language known to LibreOffice. So... move the config updating stuff from editeng down to the linguistic layer. Let the linguistic layer keep its spellchecker cache and listen to the extension manager to know if an extension which might be a spellchecker one has been modified and only throw away the cache on that event or if (existing implementation) the config data for linguistics changes. The polling of changed linguistic data in SvxLinguConfigUpdate::IsNeedUpdateAll can be removed and leave it up to LngSvcMgr to load everything in its ctor and keep itself up to date with its config and extension listeners. Change-Id: I9c93d998928e2e7f5128c36771b3e450a8057cd6
Diffstat (limited to 'linguistic')
-rw-r--r--linguistic/Library_lng.mk1
-rw-r--r--linguistic/prj/build.lst2
-rw-r--r--linguistic/source/lngsvcmgr.cxx347
-rw-r--r--linguistic/source/lngsvcmgr.hxx26
4 files changed, 358 insertions, 18 deletions
diff --git a/linguistic/Library_lng.mk b/linguistic/Library_lng.mk
index 4459c458405d..d451348f460b 100644
--- a/linguistic/Library_lng.mk
+++ b/linguistic/Library_lng.mk
@@ -53,6 +53,7 @@ $(eval $(call gb_Library_use_libraries,lng,\
tl \
ucbhelper \
utl \
+ vcl \
xo \
$(gb_STDLIBS) \
))
diff --git a/linguistic/prj/build.lst b/linguistic/prj/build.lst
index 3ef507e2e115..fe7d24d3db78 100644
--- a/linguistic/prj/build.lst
+++ b/linguistic/prj/build.lst
@@ -1,2 +1,2 @@
-lg linguistic : svl xmloff ucbhelper comphelper ICU:icu LIBXSLT:libxslt NULL
+lg linguistic : svl vcl xmloff ucbhelper comphelper ICU:icu LIBXSLT:libxslt NULL
lg linguistic\prj nmake - all lg_prj NULL
diff --git a/linguistic/source/lngsvcmgr.cxx b/linguistic/source/lngsvcmgr.cxx
index adf9da3a9b8c..6dc2d69a34ae 100644
--- a/linguistic/source/lngsvcmgr.cxx
+++ b/linguistic/source/lngsvcmgr.cxx
@@ -27,6 +27,7 @@
************************************************************************/
+#include <com/sun/star/deployment/ExtensionManager.hpp>
#include <com/sun/star/registry/XRegistryKey.hpp>
#include <com/sun/star/container/XContentEnumerationAccess.hpp>
#include <com/sun/star/container/XEnumeration.hpp>
@@ -270,8 +271,6 @@ void SAL_CALL LngSvcMgrListenerHelper::disposing( const lang::EventObject& rSour
}
}
-
-//IMPL_LINK( LngSvcMgrListenerHelper, TimeOut, Timer*, pTimer )
long LngSvcMgrListenerHelper::Timeout()
{
osl::MutexGuard aGuard( GetLinguMutex() );
@@ -483,11 +482,98 @@ LngSvcMgr::LngSvcMgr()
pNames[2] = "ServiceManager/HyphenatorList";
pNames[3] = "ServiceManager/ThesaurusList";
EnableNotification( aNames );
+
+ UpdateAll();
+
+ aUpdateTimer.SetTimeout(500);
+ aUpdateTimer.SetTimeoutHdl(LINK(this, LngSvcMgr, updateAndBroadcast));
+
+ // request to be notified if an extension has been added/removed
+ uno::Reference<uno::XComponentContext> xContext(comphelper::getProcessComponentContext());
+
+ uno::Reference<deployment::XExtensionManager> xExtensionManager(
+ deployment::ExtensionManager::get(xContext));
+ if (xExtensionManager.is())
+ {
+ xMB = uno::Reference<util::XModifyBroadcaster>(xExtensionManager, uno::UNO_QUERY_THROW);
+
+ uno::Reference<util::XModifyListener> xListener(this);
+ xMB->addModifyListener( xListener );
+ }
+}
+
+// ::com::sun::star::util::XModifyListener
+void LngSvcMgr::modified(const lang::EventObject&)
+ throw(uno::RuntimeException)
+{
+ osl::MutexGuard aGuard(GetLinguMutex());
+ //assume that if an extension has been added/removed that
+ //it might be a dictionary extension, so drop our cache
+
+ delete pAvailSpellSvcs;
+ pAvailSpellSvcs = NULL;
+ delete pAvailGrammarSvcs;
+ pAvailGrammarSvcs = NULL;
+ delete pAvailHyphSvcs;
+ pAvailHyphSvcs = NULL;
+ delete pAvailThesSvcs;
+ pAvailThesSvcs = NULL;
+
+ //schedule in an update to execute in the main thread
+ aUpdateTimer.Start();
}
+//run update, and inform everyone that dictionaries (may) have changed, this
+//needs to be run in the main thread because
+//utl::ConfigChangeListener_Impl::changesOccurred grabs the SolarMutex and we
+//get notified that an extension was added from an extension manager thread
+IMPL_LINK_NOARG(LngSvcMgr, updateAndBroadcast)
+{
+ osl::MutexGuard aGuard( GetLinguMutex() );
+
+ UpdateAll();
+
+ if (pListenerHelper)
+ {
+ pListenerHelper->AddLngSvcEvt(
+ linguistic2::LinguServiceEventFlags::SPELL_CORRECT_WORDS_AGAIN |
+ linguistic2::LinguServiceEventFlags::SPELL_WRONG_WORDS_AGAIN |
+ linguistic2::LinguServiceEventFlags::PROOFREAD_AGAIN |
+ linguistic2::LinguServiceEventFlags::HYPHENATE_AGAIN );
+ }
+
+ return 0;
+}
+
+void LngSvcMgr::stopListening()
+{
+ osl::MutexGuard aGuard(GetLinguMutex());
+
+ if (xMB.is())
+ {
+ try
+ {
+ uno::Reference<util::XModifyListener> xListener(this);
+ xMB->removeModifyListener(xListener);
+ }
+ catch (const uno::Exception&)
+ {
+ }
+
+ xMB.clear();
+ }
+}
+
+void LngSvcMgr::disposing(const lang::EventObject&)
+ throw (uno::RuntimeException)
+{
+ stopListening();
+}
LngSvcMgr::~LngSvcMgr()
{
+ stopListening();
+
// memory for pSpellDsp, pHyphDsp, pThesDsp, pListenerHelper
// will be freed in the destructor of the respective Reference's
// xSpellDsp, xGrammarDsp, xHyphDsp, xThesDsp
@@ -498,6 +584,252 @@ LngSvcMgr::~LngSvcMgr()
delete pAvailThesSvcs;
}
+namespace
+{
+ using lang::Locale;
+ using uno::Any;
+ using uno::Sequence;
+
+ sal_Bool lcl_FindEntry( const OUString &rEntry, const Sequence< OUString > &rCfgSvcs )
+ {
+ sal_Int32 nRes = -1;
+ sal_Int32 nEntries = rCfgSvcs.getLength();
+ const OUString *pEntry = rCfgSvcs.getConstArray();
+ for (sal_Int32 i = 0; i < nEntries && nRes == -1; ++i)
+ {
+ if (rEntry == pEntry[i])
+ nRes = i;
+ }
+ return nRes != -1;
+ }
+
+ Sequence< OUString > lcl_GetLastFoundSvcs(
+ SvtLinguConfig &rCfg,
+ const OUString &rLastFoundList ,
+ const Locale &rAvailLocale )
+ {
+ Sequence< OUString > aRes;
+
+ OUString aCfgLocaleStr( MsLangId::convertLanguageToIsoString(
+ LocaleToLanguage( rAvailLocale ) ) );
+
+ Sequence< OUString > aNodeNames( rCfg.GetNodeNames(rLastFoundList) );
+ sal_Bool bFound = lcl_FindEntry( aCfgLocaleStr, aNodeNames);
+
+ if (bFound)
+ {
+ Sequence< OUString > aNames(1);
+ OUString &rNodeName = aNames.getArray()[0];
+ rNodeName = rLastFoundList;
+ rNodeName += OUString::valueOf( (sal_Unicode)'/' );
+ rNodeName += aCfgLocaleStr;
+ Sequence< Any > aValues( rCfg.GetProperties( aNames ) );
+ if (aValues.getLength())
+ {
+ OSL_ENSURE( aValues.getLength() == 1, "unexpected length of sequence" );
+ Sequence< OUString > aSvcImplNames;
+ if (aValues.getConstArray()[0] >>= aSvcImplNames)
+ aRes = aSvcImplNames;
+ else
+ {
+ OSL_FAIL( "type mismatch" );
+ }
+ }
+ }
+
+ return aRes;
+ }
+
+ Sequence< OUString > lcl_RemoveMissingEntries(
+ const Sequence< OUString > &rCfgSvcs,
+ const Sequence< OUString > &rAvailSvcs )
+ {
+ Sequence< OUString > aRes( rCfgSvcs.getLength() );
+ OUString *pRes = aRes.getArray();
+ sal_Int32 nCnt = 0;
+
+ sal_Int32 nEntries = rCfgSvcs.getLength();
+ const OUString *pEntry = rCfgSvcs.getConstArray();
+ for (sal_Int32 i = 0; i < nEntries; ++i)
+ {
+ if (!pEntry[i].isEmpty() && lcl_FindEntry( pEntry[i], rAvailSvcs ))
+ pRes[ nCnt++ ] = pEntry[i];
+ }
+
+ aRes.realloc( nCnt );
+ return aRes;
+ }
+
+ Sequence< OUString > lcl_GetNewEntries(
+ const Sequence< OUString > &rLastFoundSvcs,
+ const Sequence< OUString > &rAvailSvcs )
+ {
+ sal_Int32 nLen = rAvailSvcs.getLength();
+ Sequence< OUString > aRes( nLen );
+ OUString *pRes = aRes.getArray();
+ sal_Int32 nCnt = 0;
+
+ const OUString *pEntry = rAvailSvcs.getConstArray();
+ for (sal_Int32 i = 0; i < nLen; ++i)
+ {
+ if (!pEntry[i].isEmpty() && !lcl_FindEntry( pEntry[i], rLastFoundSvcs ))
+ pRes[ nCnt++ ] = pEntry[i];
+ }
+
+ aRes.realloc( nCnt );
+ return aRes;
+ }
+
+ Sequence< OUString > lcl_MergeSeq(
+ const Sequence< OUString > &rCfgSvcs,
+ const Sequence< OUString > &rNewSvcs )
+ {
+ Sequence< OUString > aRes( rCfgSvcs.getLength() + rNewSvcs.getLength() );
+ OUString *pRes = aRes.getArray();
+ sal_Int32 nCnt = 0;
+
+ for (sal_Int32 k = 0; k < 2; ++k)
+ {
+ // add previously configuerd service first and append
+ // new found services at the end
+ const Sequence< OUString > &rSeq = k == 0 ? rCfgSvcs : rNewSvcs;
+
+ sal_Int32 nLen = rSeq.getLength();
+ const OUString *pEntry = rSeq.getConstArray();
+ for (sal_Int32 i = 0; i < nLen; ++i)
+ {
+ if (!pEntry[i].isEmpty() && !lcl_FindEntry( pEntry[i], aRes ))
+ pRes[ nCnt++ ] = pEntry[i];
+ }
+ }
+
+ aRes.realloc( nCnt );
+ return aRes;
+ }
+}
+
+void LngSvcMgr::UpdateAll()
+{
+ using beans::PropertyValue;
+ using lang::Locale;
+ using uno::Sequence;
+
+ typedef OUString OUstring_t;
+ typedef Sequence< OUString > Sequence_OUString_t;
+ typedef std::map< OUstring_t, Sequence_OUString_t > list_entry_map_t;
+
+ SvtLinguConfig aCfg;
+
+ const int nNumServices = 4;
+ const sal_Char * apServices[nNumServices] = { SN_SPELLCHECKER, SN_GRAMMARCHECKER, SN_HYPHENATOR, SN_THESAURUS };
+ const sal_Char * apCurLists[nNumServices] = { "ServiceManager/SpellCheckerList", "ServiceManager/GrammarCheckerList", "ServiceManager/HyphenatorList", "ServiceManager/ThesaurusList" };
+ const sal_Char * apLastFoundLists[nNumServices] = { "ServiceManager/LastFoundSpellCheckers", "ServiceManager/LastFoundGrammarCheckers", "ServiceManager/LastFoundHyphenators", "ServiceManager/LastFoundThesauri" };
+
+ // usage of indices as above: 0 = spell checker, 1 = grammar checker, 2 = hyphenator, 3 = thesaurus
+ std::vector< list_entry_map_t > aLastFoundSvcs(nNumServices);
+ std::vector< list_entry_map_t > aCurSvcs(nNumServices);
+
+ for (int k = 0; k < nNumServices; ++k)
+ {
+ OUString aService( ::rtl::OUString::createFromAscii( apServices[k] ) );
+ OUString aActiveList( ::rtl::OUString::createFromAscii( apCurLists[k] ) );
+ OUString aLastFoundList( ::rtl::OUString::createFromAscii( apLastFoundLists[k] ) );
+ sal_Int32 i;
+
+ //
+ // remove configured but not available language/services entries
+ //
+ Sequence< OUString > aNodeNames( aCfg.GetNodeNames( aActiveList ) ); // list of configured locales
+ sal_Int32 nNodeNames = aNodeNames.getLength();
+ const OUString *pNodeName = aNodeNames.getConstArray();
+ for (i = 0; i < nNodeNames; ++i)
+ {
+ Locale aLocale( CreateLocale( MsLangId::convertIsoStringToLanguage(pNodeName[i]) ) );
+ Sequence< OUString > aCfgSvcs( getConfiguredServices( aService, aLocale ));
+ Sequence< OUString > aAvailSvcs( getAvailableServices( aService, aLocale ));
+
+ aCfgSvcs = lcl_RemoveMissingEntries( aCfgSvcs, aAvailSvcs );
+
+ aCurSvcs[k][ pNodeName[i] ] = aCfgSvcs;
+ }
+
+ //
+ // add new available language/service entries
+ // and
+ // set last found services to currently available ones
+ //
+ Sequence< Locale > aAvailLocales( getAvailableLocales(aService) );
+ sal_Int32 nAvailLocales = aAvailLocales.getLength();
+ const Locale *pAvailLocale = aAvailLocales.getConstArray();
+ for (i = 0; i < nAvailLocales; ++i)
+ {
+ OUString aCfgLocaleStr( MsLangId::convertLanguageToIsoString(
+ LocaleToLanguage( pAvailLocale[i] ) ) );
+
+ Sequence< OUString > aAvailSvcs( getAvailableServices( aService, pAvailLocale[i] ));
+
+ aLastFoundSvcs[k][ aCfgLocaleStr ] = aAvailSvcs;
+
+ Sequence< OUString > aLastSvcs(
+ lcl_GetLastFoundSvcs( aCfg, aLastFoundList , pAvailLocale[i] ));
+ Sequence< OUString > aNewSvcs =
+ lcl_GetNewEntries( aLastSvcs, aAvailSvcs );
+
+ Sequence< OUString > aCfgSvcs( aCurSvcs[k][ aCfgLocaleStr ] );
+
+ // merge services list (previously configured to be listed first).
+ aCfgSvcs = lcl_MergeSeq( aCfgSvcs, aNewSvcs );
+
+ aCurSvcs[k][ aCfgLocaleStr ] = aCfgSvcs;
+ }
+ }
+
+ //
+ // write new data back to configuration
+ //
+ for (int k = 0; k < nNumServices; ++k)
+ {
+ for (int i = 0; i < 2; ++i)
+ {
+ const sal_Char *pSubNodeName = (i == 0) ? apCurLists[k] : apLastFoundLists[k];
+ OUString aSubNodeName( ::rtl::OUString::createFromAscii(pSubNodeName) );
+
+ list_entry_map_t &rCurMap = (i == 0) ? aCurSvcs[k] : aLastFoundSvcs[k];
+ list_entry_map_t::const_iterator aIt( rCurMap.begin() );
+ sal_Int32 nVals = static_cast< sal_Int32 >( rCurMap.size() );
+ Sequence< PropertyValue > aNewValues( nVals );
+ PropertyValue *pNewValue = aNewValues.getArray();
+ while (aIt != rCurMap.end())
+ {
+ OUString aCfgEntryName( aSubNodeName );
+ aCfgEntryName += OUString::valueOf( (sal_Unicode) '/' );
+ aCfgEntryName += (*aIt).first;
+
+ pNewValue->Name = aCfgEntryName;
+ pNewValue->Value <<= (*aIt).second;
+ ++pNewValue;
+ ++aIt;
+ }
+ OSL_ENSURE( pNewValue - aNewValues.getArray() == nVals,
+ "possible mismatch of sequence size and property number" );
+
+ {
+ // add new or replace existing entries.
+ sal_Bool bRes = aCfg.ReplaceSetProperties( aSubNodeName, aNewValues );
+ if (!bRes)
+ {
+#if OSL_DEBUG_LEVEL > 1
+ OSL_FAIL( "failed to set new configuration values" );
+#endif
+ }
+ }
+ }
+ }
+
+ //The new settings in the configuration get applied ! because we are
+ //listening to the configuration for changes of the relevant ! properties
+ //and Notify applies the new settings.
+}
void LngSvcMgr::Notify( const uno::Sequence< OUString > &rPropertyNames )
{
@@ -1256,32 +1588,21 @@ uno::Sequence< OUString > SAL_CALL
if (0 == rServiceName.compareToAscii( SN_SPELLCHECKER ))
{
- // don't used cached data here (force re-evaluation in order to have downloaded dictionaries
- // already found without the need to restart the office
- delete pAvailSpellSvcs; pAvailSpellSvcs = 0;
GetAvailableSpellSvcs_Impl();
pInfoArray = pAvailSpellSvcs;
}
else if (0 == rServiceName.compareToAscii( SN_GRAMMARCHECKER ))
{
-// disable force re-loading of the cache - re-start needed for new grammer checkers: fdo#35270
-// delete pAvailGrammarSvcs; pAvailGrammarSvcs = 0;
GetAvailableGrammarSvcs_Impl();
pInfoArray = pAvailGrammarSvcs;
}
else if (0 == rServiceName.compareToAscii( SN_HYPHENATOR ))
{
- // don't used cached data here (force re-evaluation in order to have downloaded dictionaries
- // already found without the need to restart the office
- delete pAvailHyphSvcs; pAvailHyphSvcs = 0;
GetAvailableHyphSvcs_Impl();
pInfoArray = pAvailHyphSvcs;
}
else if (0 == rServiceName.compareToAscii( SN_THESAURUS ))
{
- // don't used cached data here (force re-evaluation in order to have downloaded dictionaries
- // already found without the need to restart the office
- delete pAvailThesSvcs; pAvailThesSvcs = 0;
GetAvailableThesSvcs_Impl();
pInfoArray = pAvailThesSvcs;
}
diff --git a/linguistic/source/lngsvcmgr.hxx b/linguistic/source/lngsvcmgr.hxx
index 80cbd3d65abe..537750d7d262 100644
--- a/linguistic/source/lngsvcmgr.hxx
+++ b/linguistic/source/lngsvcmgr.hxx
@@ -30,7 +30,7 @@
#define _LINGUISTIC_LNGSVCMGR_HXX_
#include <uno/lbnames.h> // CPPU_CURRENT_LANGUAGE_BINDING_NAME macro, which specify the environment type
-#include <cppuhelper/implbase4.hxx> // helper for implementations
+#include <cppuhelper/implbase5.hxx> // helper for implementations
#include <cppuhelper/interfacecontainer.h> //OMultiTypeInterfaceContainerHelper
@@ -39,8 +39,10 @@
#include <com/sun/star/lang/XComponent.hpp>
#include <com/sun/star/linguistic2/XLinguServiceManager.hpp>
#include <com/sun/star/linguistic2/XAvailableLocales.hpp>
+#include <com/sun/star/util/XModifyBroadcaster.hpp>
+#include <com/sun/star/util/XModifyListener.hpp>
#include <unotools/configitem.hxx>
-
+#include <vcl/timer.hxx>
#include <boost/ptr_container/ptr_vector.hpp>
#include "linguistic/misc.hxx"
@@ -64,12 +66,13 @@ namespace com { namespace sun { namespace star { namespace linguistic2 {
class LngSvcMgr :
- public cppu::WeakImplHelper4
+ public cppu::WeakImplHelper5
<
com::sun::star::linguistic2::XLinguServiceManager,
com::sun::star::linguistic2::XAvailableLocales,
com::sun::star::lang::XComponent,
- com::sun::star::lang::XServiceInfo
+ com::sun::star::lang::XServiceInfo,
+ com::sun::star::util::XModifyListener
>,
private utl::ConfigItem
{
@@ -89,6 +92,12 @@ class LngSvcMgr :
com::sun::star::uno::Reference<
::com::sun::star::lang::XEventListener > xListenerHelper;
+ com::sun::star::uno::Reference<
+ ::com::sun::star::util::XModifyBroadcaster> xMB;
+
+ Timer aUpdateTimer;
+
+
com::sun::star::uno::Sequence<
com::sun::star::lang::Locale > aAvailSpellLocales;
com::sun::star::uno::Sequence<
@@ -139,6 +148,10 @@ class LngSvcMgr :
virtual void Notify( const com::sun::star::uno::Sequence< rtl::OUString > &rPropertyNames );
virtual void Commit();
+ void UpdateAll();
+ void stopListening();
+ DECL_LINK( updateAndBroadcast, void* );
+
public:
LngSvcMgr();
virtual ~LngSvcMgr();
@@ -166,6 +179,11 @@ public:
virtual ::sal_Bool SAL_CALL supportsService( const ::rtl::OUString& ServiceName ) throw (::com::sun::star::uno::RuntimeException);
virtual ::com::sun::star::uno::Sequence< ::rtl::OUString > SAL_CALL getSupportedServiceNames( ) throw (::com::sun::star::uno::RuntimeException);
+ // XEventListener
+ virtual void SAL_CALL disposing( const ::com::sun::star::lang::EventObject& rSource ) throw(::com::sun::star::uno::RuntimeException);
+
+ // XModifyListener
+ virtual void SAL_CALL modified( const ::com::sun::star::lang::EventObject& rEvent ) throw(::com::sun::star::uno::RuntimeException);
static inline ::rtl::OUString getImplementationName_Static();
static ::com::sun::star::uno::Sequence< ::rtl::OUString > getSupportedServiceNames_Static() throw();