office-gobmx/unotools/source/config/configitem.cxx
Stephan Bergmann 91ba9654ba Move tools/diagnose_ex.h to comphelper/diagnose_ex.hxx
...so that its TOOLS_WARN_EXCEPTION can be used in
comphelper/source/misc/logging.cxx in a follow-up commit.  (And while at it,
rename from diangose_ex.h to the more appropriate diagnose_ex.hxx.  The
comphelper module is sufficiently low-level for this immediate use case, so use
that at least for now; o3tl might be even more suitable but doesn't have a
Library until now.  Also, for the immediate use case it would have sufficed to
only break DbgGetCaughtException, exceptionToString, TOOLS_WARN_EXCEPTION,
TOOLS_WARN_EXCEPTION_IF, and TOOLS_INFO_EXCEPTION out of
include/tools/diagnose_ex.h into an additional new
include/comphelper/diagnose_ex.hxx, but its probably easier overall to just move
the complete include file as is.)

Change-Id: I9f3222d4ccf1a9ac29d7eb9ba1530d53e2affaee
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/138451
Tested-by: Jenkins
Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
2022-08-18 17:10:19 +02:00

1191 lines
42 KiB
C++

/* -*- 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 <comphelper/diagnose_ex.hxx>
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: */