/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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 /* 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 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 aMutexGuard( pMutex ); aChangedNames.realloc(nNotify); pParent->CallNotify(aChangedNames); } } } void ConfigChangeListener_Impl::disposing( const EventObject& /*rSource*/ ) { pParent->RemoveChangesListener(); } ConfigItem::ConfigItem(const OUString &rSubTree, ConfigItemMode nSetMode ) : sSubTree(rSubTree), 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& 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>= 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; nPropertyCountergetByName( 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>::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 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 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 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 xHierarchyAccess = GetTree(); Reference 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 xNodeAcc; aNode >>= xNodeAcc; Reference xNodeReplace(xNodeAcc, UNO_QUERY); Reference 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 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 const & xHierarchyAccess, const Sequence< OUString >& rNames, const Sequence< Any>& rValues, bool bAllLocales) { Reference 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 xNodeAcc; aNode >>= xNodeAcc; Reference xNodeReplace(xNodeAcc, UNO_QUERY); Reference 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 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 xHierarchyAccess = GetTree(); Reference 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 xHierarchyAccess = GetTree(); if(!xHierarchyAccess.is()) return; Reference 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 const& _xParentNode) { switch (_eFormat) { case ConfigNameFormat::LocalNode: // unaltered - this is our input format break; case ConfigNameFormat::LocalPath: { Reference 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 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 xHierarchyAccess = GetTree(); if(xHierarchyAccess.is()) return GetNodeNames(xHierarchyAccess, rNode, eFormat); return Sequence< OUString >(); } Sequence< OUString > ConfigItem::GetNodeNames( css::uno::Reference const & xHierarchyAccess, const OUString& rNode, ConfigNameFormat eFormat) { Sequence< OUString > aRet; try { Reference 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 xHierarchyAccess = GetTree(); if(xHierarchyAccess.is()) bRet = ClearNodeSet(xHierarchyAccess, rNode); return bRet; } bool ConfigItem::ClearNodeSet( css::uno::Reference const & xHierarchyAccess, const OUString& rNode) { bool bRet = false; try { Reference 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 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 xHierarchyAccess = GetTree(); if(xHierarchyAccess.is()) { try { Reference 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 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, const OUString& rPrefix ) { OUString const sSubPath = dropPrefixFromConfigurationPath( rInPath, rPrefix); return extractFirstFromConfigurationPath( sSubPath ); } static Sequence< OUString > lcl_extractSetPropertyNames( const Sequence< PropertyValue >& rValues, const OUString& 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 xHierarchyAccess = GetTree(); if(!xHierarchyAccess.is()) return true; return SetSetProperties(xHierarchyAccess, rNode, rValues); } // Add or change properties bool ConfigItem::SetSetProperties( css::uno::Reference const & xHierarchyAccess, const OUString& rNode, const Sequence< PropertyValue >& rValues) { bool bRet = true; Reference xBatch(xHierarchyAccess, UNO_QUERY); try { Reference xCont; if(!rNode.isEmpty()) { Any aNode = xHierarchyAccess->getByHierarchicalName(rNode); aNode >>= xCont; } else xCont.set(xHierarchyAccess, UNO_QUERY); if(!xCont.is()) return false; Reference 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 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 xHierarchyAccess = GetTree(); if(xHierarchyAccess.is()) bRet = ReplaceSetProperties(xHierarchyAccess, rNode, rValues, ( m_nMode & ConfigItemMode::AllLocales ) == ConfigItemMode::AllLocales); return bRet; } bool ConfigItem::ReplaceSetProperties( css::uno::Reference const & xHierarchyAccess, const OUString& rNode, const Sequence< PropertyValue >& rValues, bool bAllLocales) { bool bRet = true; Reference xBatch(xHierarchyAccess, UNO_QUERY); try { Reference 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 xFac(xCont, UNO_QUERY); const bool isSimpleValueSet = !xFac.is(); //remove unknown members first { const Sequence 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 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 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 xHierarchyAccess = GetTree(); if(xHierarchyAccess.is()) { Reference xBatch(xHierarchyAccess, UNO_QUERY); try { Reference xCont; if(!rNode.isEmpty()) { Any aNode = xHierarchyAccess->getByHierarchicalName(rNode); aNode >>= xCont; } else xCont.set(xHierarchyAccess, UNO_QUERY); if(!xCont.is()) return false; Reference xFac(xCont, UNO_QUERY); if(xFac.is()) { if(!xCont->hasByName(rNewNode)) { Reference 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: */