/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace com::sun::star::uno { class XComponentContext; } namespace { OUString getDefaultLocale( css::uno::Reference< css::uno::XComponentContext > const & context) { return LanguageTag( css::uno::Reference< css::lang::XLocalizable >( css::configuration::theDefaultProvider::get(context), css::uno::UNO_QUERY_THROW)-> getLocale()).getBcp47(false); } OUString extendLocalizedPath(std::u16string_view path, OUString const & locale) { SAL_WARN_IF( locale.match("*"), "comphelper", "Locale \"" << locale << "\" starts with \"*\""); assert(locale.indexOf('&') == -1); assert(locale.indexOf('"') == -1); assert(locale.indexOf('\'') == -1); return OUString::Concat(path) + "/['*" + locale + "']"; } } std::shared_ptr< comphelper::ConfigurationChanges > comphelper::ConfigurationChanges::create() { return detail::ConfigurationWrapper::get().createChanges(); } comphelper::ConfigurationChanges::~ConfigurationChanges() {} void comphelper::ConfigurationChanges::commit() const { access_->commitChanges(); } comphelper::ConfigurationChanges::ConfigurationChanges( css::uno::Reference< css::uno::XComponentContext > const & context): access_( css::configuration::ReadWriteAccess::create( context, getDefaultLocale(context))) {} void comphelper::ConfigurationChanges::setPropertyValue( OUString const & path, css::uno::Any const & value) const { access_->replaceByHierarchicalName(path, value); } css::uno::Reference< css::container::XHierarchicalNameReplace > comphelper::ConfigurationChanges::getGroup(OUString const & path) const { return css::uno::Reference< css::container::XHierarchicalNameReplace >( access_->getByHierarchicalName(path), css::uno::UNO_QUERY_THROW); } css::uno::Reference< css::container::XNameContainer > comphelper::ConfigurationChanges::getSet(OUString const & path) const { return css::uno::Reference< css::container::XNameContainer >( access_->getByHierarchicalName(path), css::uno::UNO_QUERY_THROW); } comphelper::detail::ConfigurationWrapper const & comphelper::detail::ConfigurationWrapper::get() { static comphelper::detail::ConfigurationWrapper WRAPPER; return WRAPPER; } class comphelper::detail::ConfigurationChangesListener : public ::cppu::WeakImplHelper { comphelper::detail::ConfigurationWrapper& mrConfigurationWrapper; public: ConfigurationChangesListener(comphelper::detail::ConfigurationWrapper& rWrapper) : mrConfigurationWrapper(rWrapper) {} // util::XChangesListener virtual void SAL_CALL changesOccurred( const css::util::ChangesEvent& ) override { std::scoped_lock aGuard(mrConfigurationWrapper.maMutex); mrConfigurationWrapper.maPropertyCache.clear(); } virtual void SAL_CALL disposing(const css::lang::EventObject&) override { std::scoped_lock aGuard(mrConfigurationWrapper.maMutex); mrConfigurationWrapper.mbDisposed = true; mrConfigurationWrapper.maPropertyCache.clear(); mrConfigurationWrapper.maNotifier.clear(); mrConfigurationWrapper.maListener.clear(); } }; comphelper::detail::ConfigurationWrapper::ConfigurationWrapper(): context_(comphelper::getProcessComponentContext()), access_(css::configuration::ReadWriteAccess::create(context_, "*")), mbDisposed(false) { // Set up a configuration notifier to invalidate the cache as needed. try { css::uno::Reference< css::lang::XMultiServiceFactory > xConfigProvider( css::configuration::theDefaultProvider::get( context_ ) ); // set root path css::uno::Sequence< css::uno::Any > params { css::uno::Any( css::beans::NamedValue{ "nodepath", css::uno::Any( OUString("/"))} ), css::uno::Any( css::beans::NamedValue{ "locale", css::uno::Any( OUString("*"))} ) }; css::uno::Reference< css::uno::XInterface > xCfg = xConfigProvider->createInstanceWithArguments(u"com.sun.star.configuration.ConfigurationAccess"_ustr, params); maNotifier = css::uno::Reference< css::util::XChangesNotifier >(xCfg, css::uno::UNO_QUERY); assert(maNotifier.is()); maListener.set(new ConfigurationChangesListener(*this)); maNotifier->addChangesListener(maListener); } catch(const css::uno::Exception&) { assert(false); } } comphelper::detail::ConfigurationWrapper::~ConfigurationWrapper() { maPropertyCache.clear(); maNotifier.clear(); maListener.clear(); } bool comphelper::detail::ConfigurationWrapper::isReadOnly(OUString const & path) const { return (access_->getPropertyByHierarchicalName(path).Attributes & css::beans::PropertyAttribute::READONLY) != 0; } css::uno::Any comphelper::detail::ConfigurationWrapper::getPropertyValue(OUString const& path) const { std::scoped_lock aGuard(maMutex); if (mbDisposed) throw css::lang::DisposedException(); // Cache the configuration access, since some of the keys are used in hot code. auto it = maPropertyCache.find(path); if( it != maPropertyCache.end()) return it->second; sal_Int32 idx = path.lastIndexOf("/"); assert(idx!=-1); OUString parentPath = path.copy(0, idx); OUString childName = path.copy(idx+1); css::uno::Reference access( access_->getByHierarchicalName(parentPath), css::uno::UNO_QUERY_THROW); css::uno::Any property = access->getByName(childName); maPropertyCache.emplace(path, property); return property; } void comphelper::detail::ConfigurationWrapper::setPropertyValue( std::shared_ptr< ConfigurationChanges > const & batch, OUString const & path, css::uno::Any const & value) { assert(batch); batch->setPropertyValue(path, value); } css::uno::Any comphelper::detail::ConfigurationWrapper::getLocalizedPropertyValue( std::u16string_view path) const { return access_->getByHierarchicalName( extendLocalizedPath(path, getDefaultLocale(context_))); } void comphelper::detail::ConfigurationWrapper::setLocalizedPropertyValue( std::shared_ptr< ConfigurationChanges > const & batch, OUString const & path, css::uno::Any const & value) { assert(batch); batch->setPropertyValue(path, value); } css::uno::Reference< css::container::XHierarchicalNameAccess > comphelper::detail::ConfigurationWrapper::getGroupReadOnly( OUString const & path) const { return css::uno::Reference< css::container::XHierarchicalNameAccess >( (css::configuration::ReadOnlyAccess::create( context_, getDefaultLocale(context_))-> getByHierarchicalName(path)), css::uno::UNO_QUERY_THROW); } css::uno::Reference< css::container::XHierarchicalNameReplace > comphelper::detail::ConfigurationWrapper::getGroupReadWrite( std::shared_ptr< ConfigurationChanges > const & batch, OUString const & path) { assert(batch); return batch->getGroup(path); } css::uno::Reference< css::container::XNameAccess > comphelper::detail::ConfigurationWrapper::getSetReadOnly( OUString const & path) const { return css::uno::Reference< css::container::XNameAccess >( (css::configuration::ReadOnlyAccess::create( context_, getDefaultLocale(context_))-> getByHierarchicalName(path)), css::uno::UNO_QUERY_THROW); } css::uno::Reference< css::container::XNameContainer > comphelper::detail::ConfigurationWrapper::getSetReadWrite( std::shared_ptr< ConfigurationChanges > const & batch, OUString const & path) { assert(batch); return batch->getSet(path); } std::shared_ptr< comphelper::ConfigurationChanges > comphelper::detail::ConfigurationWrapper::createChanges() const { return std::shared_ptr< ConfigurationChanges >( new ConfigurationChanges(context_)); } void comphelper::ConfigurationListener::addListener(ConfigurationListenerPropertyBase *pListener) { maListeners.push_back( pListener ); mxConfig->addPropertyChangeListener( pListener->maName, this ); pListener->setProperty( mxConfig->getPropertyValue( pListener->maName ) ); } void comphelper::ConfigurationListener::removeListener(ConfigurationListenerPropertyBase *pListener) { auto it = std::find( maListeners.begin(), maListeners.end(), pListener ); if ( it != maListeners.end() ) { maListeners.erase( it ); mxConfig->removePropertyChangeListener( pListener->maName, this ); } } void comphelper::ConfigurationListener::dispose() { for (auto const& listener : maListeners) { mxConfig->removePropertyChangeListener( listener->maName, this ); listener->dispose(); } maListeners.clear(); mxConfig.clear(); mbDisposed = true; } void SAL_CALL comphelper::ConfigurationListener::disposing(css::lang::EventObject const &) { dispose(); } void SAL_CALL comphelper::ConfigurationListener::propertyChange( css::beans::PropertyChangeEvent const &rEvt ) { // Code is commonly used inside the SolarMutexGuard // so to avoid concurrent writes to the property, // and allow fast, lock-less access, guard here. // // Note that we are abusing rtl::Reference here to do acquire/release because, // unlike osl::Guard, it is tolerant of null pointers, and on some code paths, the // SolarMutex does not exist. rtl::Reference xMutexGuard( comphelper::SolarMutex::get() ); assert( rEvt.Source == mxConfig ); for (auto const& listener : maListeners) { if ( listener->maName == rEvt.PropertyName ) { // ignore rEvt.NewValue - in theory it could be stale => not set. css::uno::Any aValue = mxConfig->getPropertyValue( listener->maName ); listener->setProperty( aValue ); } } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */