summaryrefslogtreecommitdiffstats
path: root/unotools/source/config/configitem.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'unotools/source/config/configitem.cxx')
-rw-r--r--unotools/source/config/configitem.cxx1191
1 files changed, 1191 insertions, 0 deletions
diff --git a/unotools/source/config/configitem.cxx b/unotools/source/config/configitem.cxx
new file mode 100644
index 000000000..90da75c04
--- /dev/null
+++ b/unotools/source/config/configitem.cxx
@@ -0,0 +1,1191 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <sal/log.hxx>
+#include <unotools/configitem.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/configpaths.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/util/XChangesListener.hpp>
+#include <com/sun/star/util/XChangesNotifier.hpp>
+#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
+#include <com/sun/star/configuration/XTemplateContainer.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/util/XChangesBatch.hpp>
+#include <o3tl/deleter.hxx>
+#include <osl/diagnose.h>
+#include <comphelper/sequence.hxx>
+#include <comphelper/solarmutex.hxx>
+#include <tools/diagnose_ex.h>
+
+using namespace utl;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::util;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::container;
+using namespace com::sun::star::configuration;
+
+#include <cppuhelper/implbase.hxx>
+#include <utility>
+
+/*
+ The ConfigChangeListener_Impl receives notifications from the configuration about changes that
+ have happened. It forwards this notification to the ConfigItem it knows a pParent by calling its
+ "CallNotify" method. As ConfigItems are most probably not thread safe, the SolarMutex is acquired
+ before doing so.
+*/
+
+namespace utl{
+ class ConfigChangeListener_Impl : public cppu::WeakImplHelper
+ <
+ css::util::XChangesListener
+ >
+ {
+ public:
+ ConfigItem* pParent;
+ const Sequence< OUString > aPropertyNames;
+ ConfigChangeListener_Impl(ConfigItem& rItem, const Sequence< OUString >& rNames);
+
+ //XChangesListener
+ virtual void SAL_CALL changesOccurred( const ChangesEvent& Event ) override;
+
+ //XEventListener
+ virtual void SAL_CALL disposing( const EventObject& Source ) override;
+ };
+}
+
+namespace {
+
+class ValueCounter_Impl
+{
+ sal_Int16& rCnt;
+public:
+ explicit ValueCounter_Impl(sal_Int16& rCounter)
+ : rCnt(rCounter)
+ {
+ rCnt++;
+ }
+ ~ValueCounter_Impl()
+ {
+ OSL_ENSURE(rCnt>0, "RefCount < 0 ??");
+ rCnt--;
+ }
+};
+
+}
+
+ConfigChangeListener_Impl::ConfigChangeListener_Impl(
+ ConfigItem& rItem, const Sequence< OUString >& rNames) :
+ pParent(&rItem),
+ aPropertyNames(rNames)
+{
+}
+
+void ConfigChangeListener_Impl::changesOccurred( const ChangesEvent& rEvent )
+{
+ Sequence<OUString> aChangedNames(rEvent.Changes.getLength());
+ OUString* pNames = aChangedNames.getArray();
+
+ sal_Int32 nNotify = 0;
+ for(const auto& rElementChange : rEvent.Changes)
+ {
+ OUString sTemp;
+ rElementChange.Accessor >>= sTemp;
+ //true if the path is completely correct or if it is longer
+ //i.e ...Print/Content/Graphic and .../Print
+ bool bFound = std::any_of(aPropertyNames.begin(), aPropertyNames.end(),
+ [&sTemp](const OUString& rCheckPropertyName) { return isPrefixOfConfigurationPath(sTemp, rCheckPropertyName); });
+ if(bFound)
+ pNames[nNotify++] = sTemp;
+ }
+ if( nNotify )
+ {
+ ::comphelper::SolarMutex *pMutex = ::comphelper::SolarMutex::get();
+ if ( pMutex )
+ {
+ osl::Guard<comphelper::SolarMutex> aMutexGuard( pMutex );
+ aChangedNames.realloc(nNotify);
+ pParent->CallNotify(aChangedNames);
+ }
+ }
+}
+
+void ConfigChangeListener_Impl::disposing( const EventObject& /*rSource*/ )
+{
+ pParent->RemoveChangesListener();
+}
+
+ConfigItem::ConfigItem(OUString aSubTree, ConfigItemMode nSetMode ) :
+ sSubTree(std::move(aSubTree)),
+ m_nMode(nSetMode),
+ m_bIsModified(false),
+ m_bEnableInternalNotification(false),
+ m_nInValueChange(0)
+{
+ if (utl::ConfigManager::IsFuzzing())
+ return;
+
+ if (nSetMode & ConfigItemMode::ReleaseTree)
+ ConfigManager::getConfigManager().addConfigItem(*this);
+ else
+ m_xHierarchyAccess = ConfigManager::getConfigManager().addConfigItem(*this);
+}
+
+ConfigItem::~ConfigItem()
+{
+ suppress_fun_call_w_exception(RemoveChangesListener());
+ ConfigManager::getConfigManager().removeConfigItem(*this);
+}
+
+void ConfigItem::CallNotify( const css::uno::Sequence<OUString>& rPropertyNames )
+{
+ // the call is forwarded to the virtual Notify() method
+ // it is pure virtual, so all classes deriving from ConfigItem have to decide how they
+ // want to notify listeners
+ if(m_nInValueChange <= 0 || m_bEnableInternalNotification)
+ Notify(rPropertyNames);
+}
+
+void ConfigItem::impl_packLocalizedProperties( const Sequence< OUString >& lInNames ,
+ const Sequence< Any >& lInValues ,
+ Sequence< Any >& lOutValues )
+{
+ // This method should be called for special AllLocales ConfigItem-mode only!
+
+ sal_Int32 nSourceCounter; // used to step during input lists
+ sal_Int32 nSourceSize; // marks end of loop over input lists
+ sal_Int32 nDestinationCounter; // actual position in output lists
+ sal_Int32 nPropertyCounter; // counter of inner loop for Sequence< PropertyValue >
+ sal_Int32 nPropertiesSize; // marks end of inner loop
+ Sequence< OUString > lPropertyNames; // list of all locales for localized entry
+ Sequence< PropertyValue > lProperties; // localized values of a configuration entry packed for return
+ Reference< XInterface > xLocalizedNode; // if cfg entry is localized ... lInValues contains an XInterface!
+
+ // Optimise follow algorithm ... A LITTLE BIT :-)
+ // There exist two different possibilities:
+ // i ) There exist no localized entries ... => size of lOutValues will be the same like lInNames/lInValues!
+ // ii) There exist some (mostly one or two) localized entries ... => size of lOutValues will be the same like lInNames/lInValues!
+ // ... Why? If a localized value exist - the any is filled with an XInterface object (is a SetNode-service).
+ // We read all his child nodes and pack it into Sequence< PropertyValue >.
+ // The result list we pack into the return any. We never change size of lists!
+ nSourceSize = lInNames.getLength();
+ lOutValues.realloc( nSourceSize );
+ auto plOutValues = lOutValues.getArray();
+
+ // Algorithm:
+ // Copy all names and values from in to out lists.
+ // Look for special localized entries ... You can detect it as "XInterface" packed into an Any.
+ // Use this XInterface-object to read all localized values and pack it into Sequence< PropertValue >.
+ // Add this list to out lists then.
+
+ nDestinationCounter = 0;
+ for( nSourceCounter=0; nSourceCounter<nSourceSize; ++nSourceCounter )
+ {
+ // If item a special localized one ... convert and pack it ...
+ if( lInValues[nSourceCounter].getValueTypeName() == "com.sun.star.uno.XInterface" )
+ {
+ lInValues[nSourceCounter] >>= xLocalizedNode;
+ Reference< XNameContainer > xSetAccess( xLocalizedNode, UNO_QUERY );
+ if( xSetAccess.is() )
+ {
+ lPropertyNames = xSetAccess->getElementNames();
+ nPropertiesSize = lPropertyNames.getLength();
+ lProperties.realloc( nPropertiesSize );
+ auto plProperties = lProperties.getArray();
+
+ for( nPropertyCounter=0; nPropertyCounter<nPropertiesSize; ++nPropertyCounter )
+ {
+ plProperties[nPropertyCounter].Name = lPropertyNames[nPropertyCounter];
+ OUString sLocaleValue;
+ xSetAccess->getByName( lPropertyNames[nPropertyCounter] ) >>= sLocaleValue;
+ plProperties[nPropertyCounter].Value <<= sLocaleValue;
+ }
+
+ plOutValues[nDestinationCounter] <<= lProperties;
+ }
+ }
+ // ... or copy normal items to return lists directly.
+ else
+ {
+ plOutValues[nDestinationCounter] = lInValues[nSourceCounter];
+ }
+ ++nDestinationCounter;
+ }
+}
+
+void ConfigItem::impl_unpackLocalizedProperties( const Sequence< OUString >& lInNames ,
+ const Sequence< Any >& lInValues ,
+ Sequence< OUString >& lOutNames ,
+ Sequence< Any >& lOutValues)
+{
+ // This method should be called for special AllLocales ConfigItem-mode only!
+
+ sal_Int32 nSourceSize; // marks end of loop over input lists
+ sal_Int32 nDestinationCounter; // actual position in output lists
+ sal_Int32 nPropertiesSize; // marks end of inner loop
+ OUString sNodeName; // base name of node ( e.g. "UIName/" ) ... expand to locale ( e.g. "UIName/de" )
+ Sequence< PropertyValue > lProperties; // localized values of a configuration entry gotten from lInValues-Any
+
+ // Optimise follow algorithm ... A LITTLE BIT :-)
+ // There exist two different possibilities:
+ // i ) There exist no localized entries ... => size of lOutNames/lOutValues will be the same like lInNames/lInValues!
+ // ii) There exist some (mostly one or two) localized entries ... => size of lOutNames/lOutValues will be some bytes greater then lInNames/lInValues.
+ // => I think we should make it fast for i). ii) is a special case and mustn't be SOOOO... fast.
+ // We should reserve same space for output list like input ones first.
+ // Follow algorithm looks for these borders and change it for ii) only!
+ // It will be faster then a "realloc()" call in every loop ...
+ nSourceSize = lInNames.getLength();
+
+ lOutNames.realloc ( nSourceSize );
+ auto plOutNames = lOutNames.getArray();
+ lOutValues.realloc ( nSourceSize );
+ auto plOutValues = lOutValues.getArray();
+
+ // Algorithm:
+ // Copy all names and values from const to return lists.
+ // Look for special localized entries ... You can detect it as Sequence< PropertyValue > packed into an Any.
+ // Split it ... insert PropertyValue.Name to lOutNames and PropertyValue.Value to lOutValues.
+
+ nDestinationCounter = 0;
+ for( sal_Int32 nSourceCounter=0; nSourceCounter<nSourceSize; ++nSourceCounter )
+ {
+ // If item a special localized one ... split it and insert his parts to output lists ...
+ if( lInValues[nSourceCounter].getValueType() == cppu::UnoType<Sequence<PropertyValue>>::get() )
+ {
+ lInValues[nSourceCounter] >>= lProperties;
+ nPropertiesSize = lProperties.getLength();
+
+ sNodeName = lInNames[nSourceCounter] + "/";
+
+ if( (nDestinationCounter+nPropertiesSize) > lOutNames.getLength() )
+ {
+ lOutNames.realloc ( nDestinationCounter+nPropertiesSize );
+ plOutNames = lOutNames.getArray();
+ lOutValues.realloc ( nDestinationCounter+nPropertiesSize );
+ plOutValues = lOutValues.getArray();
+ }
+
+ for( const auto& rProperty : std::as_const(lProperties) )
+ {
+ plOutNames [nDestinationCounter] = sNodeName + rProperty.Name;
+ plOutValues[nDestinationCounter] = rProperty.Value;
+ ++nDestinationCounter;
+ }
+ }
+ // ... or copy normal items to return lists directly.
+ else
+ {
+ if( (nDestinationCounter+1) > lOutNames.getLength() )
+ {
+ lOutNames.realloc ( nDestinationCounter+1 );
+ plOutNames = lOutNames.getArray();
+ lOutValues.realloc ( nDestinationCounter+1 );
+ plOutValues = lOutValues.getArray();
+ }
+
+ plOutNames [nDestinationCounter] = lInNames [nSourceCounter];
+ plOutValues[nDestinationCounter] = lInValues[nSourceCounter];
+ ++nDestinationCounter;
+ }
+ }
+}
+
+Sequence< sal_Bool > ConfigItem::GetReadOnlyStates(const css::uno::Sequence< OUString >& rNames)
+{
+ sal_Int32 i;
+
+ // size of return list is fix!
+ // Every item must match to length of incoming name list.
+ sal_Int32 nCount = rNames.getLength();
+ Sequence< sal_Bool > lStates(nCount);
+ sal_Bool* plStates = lStates.getArray();
+
+ // We must be sure to return a valid information every time!
+ // Set default to non readonly... similar to the configuration handling of this property.
+ std::fill_n(plStates, lStates.getLength(), false);
+
+ // no access - no information...
+ Reference< XHierarchicalNameAccess > xHierarchyAccess = GetTree();
+ if (!xHierarchyAccess.is())
+ return lStates;
+
+ for (i=0; i<nCount; ++i)
+ {
+ try
+ {
+ OUString sName = rNames[i];
+ OUString sPath;
+ OUString sProperty;
+
+ (void)::utl::splitLastFromConfigurationPath(sName,sPath,sProperty);
+ if (sPath.isEmpty() && sProperty.isEmpty())
+ {
+ OSL_FAIL("ConfigItem::IsReadonly() split failed");
+ continue;
+ }
+
+ Reference< XInterface > xNode;
+ Reference< XPropertySet > xSet;
+ Reference< XPropertySetInfo > xInfo;
+ if (!sPath.isEmpty())
+ {
+ Any aNode = xHierarchyAccess->getByHierarchicalName(sPath);
+ if (!(aNode >>= xNode) || !xNode.is())
+ {
+ OSL_FAIL("ConfigItem::IsReadonly() no set available");
+ continue;
+ }
+ }
+ else
+ {
+ xNode = xHierarchyAccess;
+ }
+
+ xSet.set(xNode, UNO_QUERY);
+ if (xSet.is())
+ {
+ xInfo = xSet->getPropertySetInfo();
+ OSL_ENSURE(xInfo.is(), "ConfigItem::IsReadonly() getPropertySetInfo failed ...");
+ }
+ else
+ {
+ xInfo.set(xNode, UNO_QUERY);
+ OSL_ENSURE(xInfo.is(), "ConfigItem::IsReadonly() UNO_QUERY failed ...");
+ }
+
+ if (!xInfo.is())
+ {
+ OSL_FAIL("ConfigItem::IsReadonly() no prop info available");
+ continue;
+ }
+
+ Property aProp = xInfo->getPropertyByName(sProperty);
+ plStates[i] = (aProp.Attributes & PropertyAttribute::READONLY) == PropertyAttribute::READONLY;
+ }
+ catch (const Exception&)
+ {
+ }
+ }
+
+ return lStates;
+}
+
+Sequence< Any > ConfigItem::GetProperties(const Sequence< OUString >& rNames)
+{
+ Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
+ if(xHierarchyAccess.is())
+ return GetProperties(xHierarchyAccess, rNames,
+ (m_nMode & ConfigItemMode::AllLocales ) == ConfigItemMode::AllLocales);
+ return Sequence< Any >(rNames.getLength());
+}
+
+Sequence< Any > ConfigItem::GetProperties(
+ css::uno::Reference<css::container::XHierarchicalNameAccess> const & xHierarchyAccess,
+ const Sequence< OUString >& rNames,
+ bool bAllLocales)
+{
+ Sequence< Any > aRet(rNames.getLength());
+ const OUString* pNames = rNames.getConstArray();
+ Any* pRet = aRet.getArray();
+ for(int i = 0; i < rNames.getLength(); i++)
+ {
+ try
+ {
+ pRet[i] = xHierarchyAccess->getByHierarchicalName(pNames[i]);
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION(
+ "unotools.config",
+ "ignoring XHierarchicalNameAccess " << pNames[i]);
+ }
+ }
+
+ // In special mode "ALL_LOCALES" we must convert localized values to Sequence< PropertyValue >.
+ if(bAllLocales)
+ {
+ Sequence< Any > lValues;
+ impl_packLocalizedProperties( rNames, aRet, lValues );
+ aRet = lValues;
+ }
+ return aRet;
+}
+
+bool ConfigItem::PutProperties( const Sequence< OUString >& rNames,
+ const Sequence< Any>& rValues)
+{
+ ValueCounter_Impl aCounter(m_nInValueChange);
+ Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
+ Reference<XNameReplace> xTopNodeReplace(xHierarchyAccess, UNO_QUERY);
+ bool bRet = xHierarchyAccess.is() && xTopNodeReplace.is();
+ if(bRet)
+ {
+ Sequence< OUString > lNames;
+ Sequence< Any > lValues;
+ const OUString* pNames = nullptr;
+ const Any* pValues = nullptr;
+ sal_Int32 nNameCount;
+ if(( m_nMode & ConfigItemMode::AllLocales ) == ConfigItemMode::AllLocales )
+ {
+ // If ConfigItem works in "ALL_LOCALES"-mode ... we must support a Sequence< PropertyValue >
+ // as value of a localized configuration entry!
+ // How we can do that?
+ // We must split all PropertyValues to "Sequence< OUString >" AND "Sequence< Any >"!
+ impl_unpackLocalizedProperties( rNames, rValues, lNames, lValues );
+ pNames = lNames.getConstArray ();
+ pValues = lValues.getConstArray ();
+ nNameCount = lNames.getLength ();
+ }
+ else
+ {
+ // This is the normal mode ...
+ // Use given input lists directly.
+ pNames = rNames.getConstArray ();
+ pValues = rValues.getConstArray ();
+ nNameCount = rNames.getLength ();
+ }
+ for(int i = 0; i < nNameCount; i++)
+ {
+ try
+ {
+ OUString sNode, sProperty;
+ if (splitLastFromConfigurationPath(pNames[i],sNode, sProperty))
+ {
+ Any aNode = xHierarchyAccess->getByHierarchicalName(sNode);
+
+ Reference<XNameAccess> xNodeAcc;
+ aNode >>= xNodeAcc;
+ Reference<XNameReplace> xNodeReplace(xNodeAcc, UNO_QUERY);
+ Reference<XNameContainer> xNodeCont (xNodeAcc, UNO_QUERY);
+
+ bool bExist = (xNodeAcc.is() && xNodeAcc->hasByName(sProperty));
+ if (bExist && xNodeReplace.is())
+ xNodeReplace->replaceByName(sProperty, pValues[i]);
+ else
+ if (!bExist && xNodeCont.is())
+ xNodeCont->insertByName(sProperty, pValues[i]);
+ else
+ bRet = false;
+ }
+ else //direct value
+ {
+ xTopNodeReplace->replaceByName(sProperty, pValues[i]);
+ }
+ }
+ catch (css::uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("unotools.config", "Exception from PutProperties");
+ }
+ }
+ try
+ {
+ Reference<XChangesBatch> xBatch(xHierarchyAccess, UNO_QUERY);
+ xBatch->commitChanges();
+ }
+ catch (css::uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("unotools.config", "Exception from commitChanges");
+ }
+ }
+
+ return bRet;
+}
+
+bool ConfigItem::PutProperties(
+ css::uno::Reference<css::container::XHierarchicalNameAccess> const & xHierarchyAccess,
+ const Sequence< OUString >& rNames,
+ const Sequence< Any>& rValues,
+ bool bAllLocales)
+{
+ Reference<XNameReplace> xTopNodeReplace(xHierarchyAccess, UNO_QUERY);
+ bool bRet = xTopNodeReplace.is();
+ if(bRet)
+ {
+ Sequence< OUString > lNames;
+ Sequence< Any > lValues;
+ const OUString* pNames = nullptr;
+ const Any* pValues = nullptr;
+ sal_Int32 nNameCount;
+ if(bAllLocales)
+ {
+ // If ConfigItem works in "ALL_LOCALES"-mode ... we must support a Sequence< PropertyValue >
+ // as value of a localized configuration entry!
+ // How we can do that?
+ // We must split all PropertyValues to "Sequence< OUString >" AND "Sequence< Any >"!
+ impl_unpackLocalizedProperties( rNames, rValues, lNames, lValues );
+ pNames = lNames.getConstArray ();
+ pValues = lValues.getConstArray ();
+ nNameCount = lNames.getLength ();
+ }
+ else
+ {
+ // This is the normal mode ...
+ // Use given input lists directly.
+ pNames = rNames.getConstArray ();
+ pValues = rValues.getConstArray ();
+ nNameCount = rNames.getLength ();
+ }
+ for(int i = 0; i < nNameCount; i++)
+ {
+ try
+ {
+ OUString sNode, sProperty;
+ if (splitLastFromConfigurationPath(pNames[i],sNode, sProperty))
+ {
+ Any aNode = xHierarchyAccess->getByHierarchicalName(sNode);
+
+ Reference<XNameAccess> xNodeAcc;
+ aNode >>= xNodeAcc;
+ Reference<XNameReplace> xNodeReplace(xNodeAcc, UNO_QUERY);
+ Reference<XNameContainer> xNodeCont (xNodeAcc, UNO_QUERY);
+
+ bool bExist = (xNodeAcc.is() && xNodeAcc->hasByName(sProperty));
+ if (bExist && xNodeReplace.is())
+ xNodeReplace->replaceByName(sProperty, pValues[i]);
+ else
+ if (!bExist && xNodeCont.is())
+ xNodeCont->insertByName(sProperty, pValues[i]);
+ else
+ bRet = false;
+ }
+ else //direct value
+ {
+ xTopNodeReplace->replaceByName(sProperty, pValues[i]);
+ }
+ }
+ catch (css::uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("unotools.config", "Exception from PutProperties");
+ }
+ }
+ try
+ {
+ Reference<XChangesBatch> xBatch(xHierarchyAccess, UNO_QUERY);
+ xBatch->commitChanges();
+ }
+ catch (css::uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("unotools.config", "Exception from commitChanges");
+ }
+ }
+
+ return bRet;
+}
+
+void ConfigItem::DisableNotification()
+{
+ OSL_ENSURE( xChangeLstnr.is(), "ConfigItem::DisableNotification: notifications not enabled currently!" );
+ RemoveChangesListener();
+}
+
+bool ConfigItem::EnableNotification(const Sequence< OUString >& rNames,
+ bool bEnableInternalNotification )
+{
+ OSL_ENSURE(!(m_nMode & ConfigItemMode::ReleaseTree), "notification in ConfigItemMode::ReleaseTree mode not possible");
+ m_bEnableInternalNotification = bEnableInternalNotification;
+ Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
+ Reference<XChangesNotifier> xChgNot(xHierarchyAccess, UNO_QUERY);
+ if(!xChgNot.is())
+ return false;
+
+ OSL_ENSURE(!xChangeLstnr.is(), "EnableNotification already called");
+ if(xChangeLstnr.is())
+ xChgNot->removeChangesListener( xChangeLstnr );
+ bool bRet = true;
+
+ try
+ {
+ xChangeLstnr = new ConfigChangeListener_Impl(*this, rNames);
+ xChgNot->addChangesListener( xChangeLstnr );
+ }
+ catch (const RuntimeException&)
+ {
+ bRet = false;
+ }
+ return bRet;
+}
+
+void ConfigItem::RemoveChangesListener()
+{
+ Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
+ if(!xHierarchyAccess.is())
+ return;
+
+ Reference<XChangesNotifier> xChgNot(xHierarchyAccess, UNO_QUERY);
+ if(xChgNot.is() && xChangeLstnr.is())
+ {
+ try
+ {
+ xChgNot->removeChangesListener( xChangeLstnr );
+ xChangeLstnr = nullptr;
+ }
+ catch (const Exception&)
+ {
+ }
+ }
+}
+
+static void lcl_normalizeLocalNames(Sequence< OUString >& _rNames, ConfigNameFormat _eFormat, Reference<XInterface> const& _xParentNode)
+{
+ switch (_eFormat)
+ {
+ case ConfigNameFormat::LocalNode:
+ // unaltered - this is our input format
+ break;
+
+ case ConfigNameFormat::LocalPath:
+ {
+ Reference<XTemplateContainer> xTypeContainer(_xParentNode, UNO_QUERY);
+ if (xTypeContainer.is())
+ {
+ OUString sTypeName = xTypeContainer->getElementTemplateName();
+ sTypeName = sTypeName.copy(sTypeName.lastIndexOf('/')+1);
+
+ std::transform(std::cbegin(_rNames), std::cend(_rNames), _rNames.getArray(),
+ [&sTypeName](const OUString& rName) -> OUString { return wrapConfigurationElementName(rName,sTypeName); });
+ }
+ else
+ {
+ Reference<XServiceInfo> xSVI(_xParentNode, UNO_QUERY);
+ if (xSVI.is() && xSVI->supportsService("com.sun.star.configuration.SetAccess"))
+ {
+ std::transform(std::cbegin(_rNames), std::cend(_rNames), _rNames.getArray(),
+ [](const OUString& rName) -> OUString { return wrapConfigurationElementName(rName); });
+ }
+ }
+ }
+ break;
+
+ }
+}
+
+Sequence< OUString > ConfigItem::GetNodeNames(const OUString& rNode)
+{
+ ConfigNameFormat const eDefaultFormat = ConfigNameFormat::LocalNode; // CONFIG_NAME_DEFAULT;
+
+ return GetNodeNames(rNode, eDefaultFormat);
+}
+
+Sequence< OUString > ConfigItem::GetNodeNames(const OUString& rNode, ConfigNameFormat eFormat)
+{
+ Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
+ if(xHierarchyAccess.is())
+ return GetNodeNames(xHierarchyAccess, rNode, eFormat);
+ return Sequence< OUString >();
+}
+
+Sequence< OUString > ConfigItem::GetNodeNames(
+ css::uno::Reference<css::container::XHierarchicalNameAccess> const & xHierarchyAccess,
+ const OUString& rNode,
+ ConfigNameFormat eFormat)
+{
+ Sequence< OUString > aRet;
+ try
+ {
+ Reference<XNameAccess> xCont;
+ if(!rNode.isEmpty())
+ {
+ Any aNode = xHierarchyAccess->getByHierarchicalName(rNode);
+ aNode >>= xCont;
+ }
+ else
+ xCont.set(xHierarchyAccess, UNO_QUERY);
+ if(xCont.is())
+ {
+ aRet = xCont->getElementNames();
+ lcl_normalizeLocalNames(aRet,eFormat,xCont);
+ }
+
+ }
+ catch (css::uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("unotools.config", "Exception from GetNodeNames");
+ }
+ return aRet;
+}
+
+bool ConfigItem::ClearNodeSet(const OUString& rNode)
+{
+ ValueCounter_Impl aCounter(m_nInValueChange);
+ bool bRet = false;
+ Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
+ if(xHierarchyAccess.is())
+ bRet = ClearNodeSet(xHierarchyAccess, rNode);
+ return bRet;
+}
+
+bool ConfigItem::ClearNodeSet(
+ css::uno::Reference<css::container::XHierarchicalNameAccess> const & xHierarchyAccess,
+ const OUString& rNode)
+{
+ bool bRet = false;
+ try
+ {
+ Reference<XNameContainer> xCont;
+ if(!rNode.isEmpty())
+ {
+ Any aNode = xHierarchyAccess->getByHierarchicalName(rNode);
+ aNode >>= xCont;
+ }
+ else
+ xCont.set(xHierarchyAccess, UNO_QUERY);
+ if(!xCont.is())
+ return false;
+ const Sequence< OUString > aNames = xCont->getElementNames();
+ Reference<XChangesBatch> xBatch(xHierarchyAccess, UNO_QUERY);
+ for(const OUString& rName : aNames)
+ {
+ try
+ {
+ xCont->removeByName(rName);
+ }
+ catch (css::uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("unotools.config", "Exception from removeByName");
+ }
+ }
+ xBatch->commitChanges();
+ bRet = true;
+ }
+ catch (css::uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("unotools.config", "Exception from ClearNodeSet");
+ }
+ return bRet;
+}
+
+bool ConfigItem::ClearNodeElements(const OUString& rNode, Sequence< OUString > const & rElements)
+{
+ ValueCounter_Impl aCounter(m_nInValueChange);
+ bool bRet = false;
+ Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
+ if(xHierarchyAccess.is())
+ {
+ try
+ {
+ Reference<XNameContainer> xCont;
+ if(!rNode.isEmpty())
+ {
+ Any aNode = xHierarchyAccess->getByHierarchicalName(rNode);
+ aNode >>= xCont;
+ }
+ else
+ xCont.set(xHierarchyAccess, UNO_QUERY);
+ if(!xCont.is())
+ return false;
+ try
+ {
+ for(const OUString& rElement : rElements)
+ {
+ xCont->removeByName(rElement);
+ }
+ Reference<XChangesBatch> xBatch(xHierarchyAccess, UNO_QUERY);
+ xBatch->commitChanges();
+ }
+ catch (css::uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("unotools.config", "Exception from commitChanges()");
+ }
+ bRet = true;
+ }
+ catch (css::uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("unotools.config", "Exception from GetNodeNames()");
+ }
+ }
+ return bRet;
+}
+
+static OUString lcl_extractSetPropertyName( const OUString& rInPath, std::u16string_view rPrefix )
+{
+ OUString const sSubPath = dropPrefixFromConfigurationPath( rInPath, rPrefix);
+ return extractFirstFromConfigurationPath( sSubPath );
+}
+
+static
+Sequence< OUString > lcl_extractSetPropertyNames( const Sequence< PropertyValue >& rValues, std::u16string_view rPrefix )
+{
+ Sequence< OUString > aSubNodeNames(rValues.getLength());
+ OUString* pSubNodeNames = aSubNodeNames.getArray();
+
+ OUString sLastSubNode;
+ sal_Int32 nSubIndex = 0;
+
+ for(const PropertyValue& rProperty : rValues)
+ {
+ OUString const sSubPath = dropPrefixFromConfigurationPath( rProperty.Name, rPrefix);
+ OUString const sSubNode = extractFirstFromConfigurationPath( sSubPath );
+
+ if(sLastSubNode != sSubNode)
+ {
+ pSubNodeNames[nSubIndex++] = sSubNode;
+ }
+
+ sLastSubNode = sSubNode;
+ }
+ aSubNodeNames.realloc(nSubIndex);
+
+ return aSubNodeNames;
+}
+
+// Add or change properties
+bool ConfigItem::SetSetProperties(
+ const OUString& rNode, const Sequence< PropertyValue >& rValues)
+{
+ ValueCounter_Impl aCounter(m_nInValueChange);
+ Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
+ if(!xHierarchyAccess.is())
+ return true;
+ return SetSetProperties(xHierarchyAccess, rNode, rValues);
+}
+
+// Add or change properties
+bool ConfigItem::SetSetProperties(
+ css::uno::Reference<css::container::XHierarchicalNameAccess> const & xHierarchyAccess,
+ const OUString& rNode, const Sequence< PropertyValue >& rValues)
+{
+ bool bRet = true;
+ Reference<XChangesBatch> xBatch(xHierarchyAccess, UNO_QUERY);
+ try
+ {
+ Reference<XNameContainer> xCont;
+ if(!rNode.isEmpty())
+ {
+ Any aNode = xHierarchyAccess->getByHierarchicalName(rNode);
+ aNode >>= xCont;
+ }
+ else
+ xCont.set(xHierarchyAccess, UNO_QUERY);
+ if(!xCont.is())
+ return false;
+
+ Reference<XSingleServiceFactory> xFac(xCont, UNO_QUERY);
+
+ if(xFac.is())
+ {
+ const Sequence< OUString > aSubNodeNames = lcl_extractSetPropertyNames(rValues, rNode);
+
+ for(const auto& rSubNodeName : aSubNodeNames)
+ {
+ if(!xCont->hasByName(rSubNodeName))
+ {
+ Reference<XInterface> xInst = xFac->createInstance();
+ Any aVal; aVal <<= xInst;
+ xCont->insertByName(rSubNodeName, aVal);
+ }
+ //set values
+ }
+ try
+ {
+ xBatch->commitChanges();
+ }
+ catch (css::uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("unotools.config", "Exception from commitChanges()");
+ }
+
+ const PropertyValue* pProperties = rValues.getConstArray();
+
+ Sequence< OUString > aSetNames(rValues.getLength());
+ OUString* pSetNames = aSetNames.getArray();
+
+ Sequence< Any> aSetValues(rValues.getLength());
+ Any* pSetValues = aSetValues.getArray();
+
+ bool bEmptyNode = rNode.isEmpty();
+ for(sal_Int32 k = 0; k < rValues.getLength(); k++)
+ {
+ pSetNames[k] = pProperties[k].Name.copy( bEmptyNode ? 1 : 0);
+ pSetValues[k] = pProperties[k].Value;
+ }
+ bRet = PutProperties(xHierarchyAccess, aSetNames, aSetValues, /*bAllLocales*/false);
+ }
+ else
+ {
+ //if no factory is available then the node contains basic data elements
+ for(const PropertyValue& rValue : rValues)
+ {
+ try
+ {
+ OUString sSubNode = lcl_extractSetPropertyName( rValue.Name, rNode );
+
+ if(xCont->hasByName(sSubNode))
+ xCont->replaceByName(sSubNode, rValue.Value);
+ else
+ xCont->insertByName(sSubNode, rValue.Value);
+
+ OSL_ENSURE( xHierarchyAccess->hasByHierarchicalName(rValue.Name),
+ "Invalid config path" );
+ }
+ catch (css::uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("unotools.config", "Exception from insert/replaceByName()");
+ }
+ }
+ xBatch->commitChanges();
+ }
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("unotools.config", "Exception from SetSetProperties");
+ bRet = false;
+ }
+ return bRet;
+}
+
+bool ConfigItem::ReplaceSetProperties(
+ const OUString& rNode, const Sequence< PropertyValue >& rValues)
+{
+ ValueCounter_Impl aCounter(m_nInValueChange);
+ bool bRet = true;
+ Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
+ if(xHierarchyAccess.is())
+ bRet = ReplaceSetProperties(xHierarchyAccess, rNode, rValues,
+ ( m_nMode & ConfigItemMode::AllLocales ) == ConfigItemMode::AllLocales);
+ return bRet;
+}
+
+bool ConfigItem::ReplaceSetProperties(
+ css::uno::Reference<css::container::XHierarchicalNameAccess> const & xHierarchyAccess,
+ const OUString& rNode,
+ const Sequence< PropertyValue >& rValues,
+ bool bAllLocales)
+{
+ bool bRet = true;
+ Reference<XChangesBatch> xBatch(xHierarchyAccess, UNO_QUERY);
+ try
+ {
+ Reference<XNameContainer> xCont;
+ if(!rNode.isEmpty())
+ {
+ Any aNode = xHierarchyAccess->getByHierarchicalName(rNode);
+ aNode >>= xCont;
+ }
+ else
+ xCont.set(xHierarchyAccess, UNO_QUERY);
+ if(!xCont.is())
+ return false;
+
+ // JB: Change: now the same name handling for sets of simple values
+ const Sequence< OUString > aSubNodeNames = lcl_extractSetPropertyNames(rValues, rNode);
+
+ Reference<XSingleServiceFactory> xFac(xCont, UNO_QUERY);
+ const bool isSimpleValueSet = !xFac.is();
+
+ //remove unknown members first
+ {
+ const Sequence<OUString> aContainerSubNodes = xCont->getElementNames();
+
+ for(const OUString& rContainerSubNode : aContainerSubNodes)
+ {
+ bool bFound = comphelper::findValue(aSubNodeNames, rContainerSubNode) != -1;
+ if(!bFound)
+ try
+ {
+ xCont->removeByName(rContainerSubNode);
+ }
+ catch (const Exception&)
+ {
+ if (isSimpleValueSet)
+ {
+ try
+ {
+ // #i37322#: fallback action: replace with <void/>
+ xCont->replaceByName(rContainerSubNode, Any());
+ // fallback successful: continue looping
+ continue;
+ }
+ catch (Exception &)
+ {} // propagate original exception, if fallback fails
+ }
+ throw;
+ }
+ }
+ try { xBatch->commitChanges(); }
+ catch (css::uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("unotools.config", "Exception from commitChanges");
+ }
+ }
+
+ if(xFac.is()) // !isSimpleValueSet
+ {
+ for(const OUString& rSubNodeName : aSubNodeNames)
+ {
+ if(!xCont->hasByName(rSubNodeName))
+ {
+ //create if not available
+ Reference<XInterface> xInst = xFac->createInstance();
+ Any aVal; aVal <<= xInst;
+ xCont->insertByName(rSubNodeName, aVal);
+ }
+ }
+ try { xBatch->commitChanges(); }
+ catch (css::uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("unotools.config", "Exception from commitChanges");
+ }
+
+ const PropertyValue* pProperties = rValues.getConstArray();
+
+ Sequence< OUString > aSetNames(rValues.getLength());
+ OUString* pSetNames = aSetNames.getArray();
+
+ Sequence< Any> aSetValues(rValues.getLength());
+ Any* pSetValues = aSetValues.getArray();
+
+ bool bEmptyNode = rNode.isEmpty();
+ for(sal_Int32 k = 0; k < rValues.getLength(); k++)
+ {
+ pSetNames[k] = pProperties[k].Name.copy( bEmptyNode ? 1 : 0);
+ pSetValues[k] = pProperties[k].Value;
+ }
+ bRet = PutProperties(xHierarchyAccess, aSetNames, aSetValues, bAllLocales);
+ }
+ else
+ {
+ //if no factory is available then the node contains basic data elements
+ for(const PropertyValue& rValue : rValues)
+ {
+ try
+ {
+ OUString sSubNode = lcl_extractSetPropertyName( rValue.Name, rNode );
+
+ if(xCont->hasByName(sSubNode))
+ xCont->replaceByName(sSubNode, rValue.Value);
+ else
+ xCont->insertByName(sSubNode, rValue.Value);
+ }
+ catch (css::uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("unotools.config", "Exception from insert/replaceByName");
+ }
+ }
+ xBatch->commitChanges();
+ }
+ }
+ catch (const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("unotools.config", "Exception from ReplaceSetProperties");
+ bRet = false;
+ }
+ return bRet;
+}
+
+bool ConfigItem::AddNode(const OUString& rNode, const OUString& rNewNode)
+{
+ ValueCounter_Impl aCounter(m_nInValueChange);
+ bool bRet = true;
+ Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
+ if(xHierarchyAccess.is())
+ {
+ Reference<XChangesBatch> xBatch(xHierarchyAccess, UNO_QUERY);
+ try
+ {
+ Reference<XNameContainer> xCont;
+ if(!rNode.isEmpty())
+ {
+ Any aNode = xHierarchyAccess->getByHierarchicalName(rNode);
+ aNode >>= xCont;
+ }
+ else
+ xCont.set(xHierarchyAccess, UNO_QUERY);
+ if(!xCont.is())
+ return false;
+
+ Reference<XSingleServiceFactory> xFac(xCont, UNO_QUERY);
+
+ if(xFac.is())
+ {
+ if(!xCont->hasByName(rNewNode))
+ {
+ Reference<XInterface> xInst = xFac->createInstance();
+ Any aVal; aVal <<= xInst;
+ xCont->insertByName(rNewNode, aVal);
+ }
+ try
+ {
+ xBatch->commitChanges();
+ }
+ catch (css::uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("unotools.config", "Exception from commitChanges");
+ }
+ }
+ else
+ {
+ //if no factory is available then the node contains basic data elements
+ try
+ {
+ if(!xCont->hasByName(rNewNode))
+ xCont->insertByName(rNewNode, Any());
+ }
+ catch (css::uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("unotools.config", "Exception from AddNode");
+ }
+ }
+ xBatch->commitChanges();
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("unotools.config");
+ bRet = false;
+ }
+ }
+ return bRet;
+}
+
+
+void ConfigItem::SetModified()
+{
+ m_bIsModified = true;
+}
+
+void ConfigItem::ClearModified()
+{
+ m_bIsModified = false;
+}
+
+Reference< XHierarchicalNameAccess> ConfigItem::GetTree()
+{
+ Reference< XHierarchicalNameAccess> xRet;
+ if (utl::ConfigManager::IsFuzzing())
+ return xRet;
+ if(!m_xHierarchyAccess.is())
+ xRet = ConfigManager::acquireTree(*this);
+ else
+ xRet = m_xHierarchyAccess;
+ return xRet;
+}
+
+void ConfigItem::Commit()
+{
+ ImplCommit();
+ ClearModified();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */