Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / unotools / source / config / configitem.cxx
blob55e79e30c8a44f2a6e7644b43fb6e07706fcbd16
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <sal/log.hxx>
23 #include <unotools/configitem.hxx>
24 #include <unotools/configmgr.hxx>
25 #include <unotools/configpaths.hxx>
26 #include <com/sun/star/beans/XPropertySet.hpp>
27 #include <com/sun/star/util/XChangesListener.hpp>
28 #include <com/sun/star/util/XChangesNotifier.hpp>
29 #include <com/sun/star/container/XHierarchicalNameAccess.hpp>
30 #include <com/sun/star/configuration/XTemplateContainer.hpp>
31 #include <com/sun/star/container/XNameContainer.hpp>
32 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
33 #include <com/sun/star/lang/XServiceInfo.hpp>
34 #include <com/sun/star/beans/PropertyValue.hpp>
35 #include <com/sun/star/beans/PropertyAttribute.hpp>
36 #include <com/sun/star/util/XChangesBatch.hpp>
37 #include <o3tl/deleter.hxx>
38 #include <osl/diagnose.h>
39 #include <comphelper/sequence.hxx>
40 #include <comphelper/solarmutex.hxx>
41 #include <comphelper/diagnose_ex.hxx>
43 using namespace utl;
44 using namespace com::sun::star::uno;
45 using namespace com::sun::star::util;
46 using namespace com::sun::star::lang;
47 using namespace com::sun::star::beans;
48 using namespace com::sun::star::container;
49 using namespace com::sun::star::configuration;
51 #include <cppuhelper/implbase.hxx>
52 #include <utility>
55 The ConfigChangeListener_Impl receives notifications from the configuration about changes that
56 have happened. It forwards this notification to the ConfigItem it knows a pParent by calling its
57 "CallNotify" method. As ConfigItems are most probably not thread safe, the SolarMutex is acquired
58 before doing so.
61 namespace utl{
62 class ConfigChangeListener_Impl : public cppu::WeakImplHelper
64 css::util::XChangesListener
67 public:
68 ConfigItem* pParent;
69 const Sequence< OUString > aPropertyNames;
70 ConfigChangeListener_Impl(ConfigItem& rItem, const Sequence< OUString >& rNames);
72 //XChangesListener
73 virtual void SAL_CALL changesOccurred( const ChangesEvent& Event ) override;
75 //XEventListener
76 virtual void SAL_CALL disposing( const EventObject& Source ) override;
80 namespace {
82 class ValueCounter_Impl
84 sal_Int16& rCnt;
85 public:
86 explicit ValueCounter_Impl(sal_Int16& rCounter)
87 : rCnt(rCounter)
89 rCnt++;
91 ~ValueCounter_Impl()
93 OSL_ENSURE(rCnt>0, "RefCount < 0 ??");
94 rCnt--;
100 ConfigChangeListener_Impl::ConfigChangeListener_Impl(
101 ConfigItem& rItem, const Sequence< OUString >& rNames) :
102 pParent(&rItem),
103 aPropertyNames(rNames)
107 void ConfigChangeListener_Impl::changesOccurred( const ChangesEvent& rEvent )
109 Sequence<OUString> aChangedNames(rEvent.Changes.getLength());
110 OUString* pNames = aChangedNames.getArray();
112 sal_Int32 nNotify = 0;
113 for(const auto& rElementChange : rEvent.Changes)
115 OUString sTemp;
116 rElementChange.Accessor >>= sTemp;
117 //true if the path is completely correct or if it is longer
118 //i.e ...Print/Content/Graphic and .../Print
119 bool bFound = std::any_of(aPropertyNames.begin(), aPropertyNames.end(),
120 [&sTemp](const OUString& rCheckPropertyName) { return isPrefixOfConfigurationPath(sTemp, rCheckPropertyName); });
121 if(bFound)
122 pNames[nNotify++] = sTemp;
124 if( nNotify )
126 ::comphelper::SolarMutex *pMutex = ::comphelper::SolarMutex::get();
127 if ( pMutex )
129 osl::Guard<comphelper::SolarMutex> aMutexGuard( pMutex );
130 aChangedNames.realloc(nNotify);
131 pParent->CallNotify(aChangedNames);
136 void ConfigChangeListener_Impl::disposing( const EventObject& /*rSource*/ )
138 pParent->RemoveChangesListener();
141 ConfigItem::ConfigItem(OUString aSubTree, ConfigItemMode nSetMode ) :
142 sSubTree(std::move(aSubTree)),
143 m_nMode(nSetMode),
144 m_bIsModified(false),
145 m_bEnableInternalNotification(false),
146 m_nInValueChange(0)
148 if (utl::ConfigManager::IsFuzzing())
149 return;
151 if (nSetMode & ConfigItemMode::ReleaseTree)
152 ConfigManager::getConfigManager().addConfigItem(*this);
153 else
154 m_xHierarchyAccess = ConfigManager::getConfigManager().addConfigItem(*this);
157 ConfigItem::~ConfigItem()
159 suppress_fun_call_w_exception(RemoveChangesListener());
160 ConfigManager::getConfigManager().removeConfigItem(*this);
163 void ConfigItem::CallNotify( const css::uno::Sequence<OUString>& rPropertyNames )
165 // the call is forwarded to the virtual Notify() method
166 // it is pure virtual, so all classes deriving from ConfigItem have to decide how they
167 // want to notify listeners
168 if(m_nInValueChange <= 0 || m_bEnableInternalNotification)
169 Notify(rPropertyNames);
172 void ConfigItem::impl_packLocalizedProperties( const Sequence< OUString >& lInNames ,
173 const Sequence< Any >& lInValues ,
174 Sequence< Any >& lOutValues )
176 // This method should be called for special AllLocales ConfigItem-mode only!
178 sal_Int32 nSourceCounter; // used to step during input lists
179 sal_Int32 nSourceSize; // marks end of loop over input lists
180 sal_Int32 nDestinationCounter; // actual position in output lists
181 sal_Int32 nPropertyCounter; // counter of inner loop for Sequence< PropertyValue >
182 sal_Int32 nPropertiesSize; // marks end of inner loop
183 Sequence< OUString > lPropertyNames; // list of all locales for localized entry
184 Sequence< PropertyValue > lProperties; // localized values of a configuration entry packed for return
185 Reference< XInterface > xLocalizedNode; // if cfg entry is localized ... lInValues contains an XInterface!
187 // Optimise follow algorithm ... A LITTLE BIT :-)
188 // There exist two different possibilities:
189 // i ) There exist no localized entries ... => size of lOutValues will be the same like lInNames/lInValues!
190 // ii) There exist some (mostly one or two) localized entries ... => size of lOutValues will be the same like lInNames/lInValues!
191 // ... Why? If a localized value exist - the any is filled with an XInterface object (is a SetNode-service).
192 // We read all his child nodes and pack it into Sequence< PropertyValue >.
193 // The result list we pack into the return any. We never change size of lists!
194 nSourceSize = lInNames.getLength();
195 lOutValues.realloc( nSourceSize );
196 auto plOutValues = lOutValues.getArray();
198 // Algorithm:
199 // Copy all names and values from in to out lists.
200 // Look for special localized entries ... You can detect it as "XInterface" packed into an Any.
201 // Use this XInterface-object to read all localized values and pack it into Sequence< PropertValue >.
202 // Add this list to out lists then.
204 nDestinationCounter = 0;
205 for( nSourceCounter=0; nSourceCounter<nSourceSize; ++nSourceCounter )
207 // If item a special localized one ... convert and pack it ...
208 if( lInValues[nSourceCounter].getValueTypeName() == "com.sun.star.uno.XInterface" )
210 lInValues[nSourceCounter] >>= xLocalizedNode;
211 Reference< XNameContainer > xSetAccess( xLocalizedNode, UNO_QUERY );
212 if( xSetAccess.is() )
214 lPropertyNames = xSetAccess->getElementNames();
215 nPropertiesSize = lPropertyNames.getLength();
216 lProperties.realloc( nPropertiesSize );
217 auto plProperties = lProperties.getArray();
219 for( nPropertyCounter=0; nPropertyCounter<nPropertiesSize; ++nPropertyCounter )
221 plProperties[nPropertyCounter].Name = lPropertyNames[nPropertyCounter];
222 OUString sLocaleValue;
223 xSetAccess->getByName( lPropertyNames[nPropertyCounter] ) >>= sLocaleValue;
224 plProperties[nPropertyCounter].Value <<= sLocaleValue;
227 plOutValues[nDestinationCounter] <<= lProperties;
230 // ... or copy normal items to return lists directly.
231 else
233 plOutValues[nDestinationCounter] = lInValues[nSourceCounter];
235 ++nDestinationCounter;
239 void ConfigItem::impl_unpackLocalizedProperties( const Sequence< OUString >& lInNames ,
240 const Sequence< Any >& lInValues ,
241 Sequence< OUString >& lOutNames ,
242 Sequence< Any >& lOutValues)
244 // This method should be called for special AllLocales ConfigItem-mode only!
246 sal_Int32 nSourceSize; // marks end of loop over input lists
247 sal_Int32 nDestinationCounter; // actual position in output lists
248 sal_Int32 nPropertiesSize; // marks end of inner loop
249 OUString sNodeName; // base name of node ( e.g. "UIName/" ) ... expand to locale ( e.g. "UIName/de" )
250 Sequence< PropertyValue > lProperties; // localized values of a configuration entry gotten from lInValues-Any
252 // Optimise follow algorithm ... A LITTLE BIT :-)
253 // There exist two different possibilities:
254 // i ) There exist no localized entries ... => size of lOutNames/lOutValues will be the same like lInNames/lInValues!
255 // ii) There exist some (mostly one or two) localized entries ... => size of lOutNames/lOutValues will be some bytes greater then lInNames/lInValues.
256 // => I think we should make it fast for i). ii) is a special case and mustn't be SOOOO... fast.
257 // We should reserve same space for output list like input ones first.
258 // Follow algorithm looks for these borders and change it for ii) only!
259 // It will be faster then a "realloc()" call in every loop ...
260 nSourceSize = lInNames.getLength();
262 lOutNames.realloc ( nSourceSize );
263 auto plOutNames = lOutNames.getArray();
264 lOutValues.realloc ( nSourceSize );
265 auto plOutValues = lOutValues.getArray();
267 // Algorithm:
268 // Copy all names and values from const to return lists.
269 // Look for special localized entries ... You can detect it as Sequence< PropertyValue > packed into an Any.
270 // Split it ... insert PropertyValue.Name to lOutNames and PropertyValue.Value to lOutValues.
272 nDestinationCounter = 0;
273 for( sal_Int32 nSourceCounter=0; nSourceCounter<nSourceSize; ++nSourceCounter )
275 // If item a special localized one ... split it and insert his parts to output lists ...
276 if( lInValues[nSourceCounter].getValueType() == cppu::UnoType<Sequence<PropertyValue>>::get() )
278 lInValues[nSourceCounter] >>= lProperties;
279 nPropertiesSize = lProperties.getLength();
281 sNodeName = lInNames[nSourceCounter] + "/";
283 if( (nDestinationCounter+nPropertiesSize) > lOutNames.getLength() )
285 lOutNames.realloc ( nDestinationCounter+nPropertiesSize );
286 plOutNames = lOutNames.getArray();
287 lOutValues.realloc ( nDestinationCounter+nPropertiesSize );
288 plOutValues = lOutValues.getArray();
291 for( const auto& rProperty : std::as_const(lProperties) )
293 plOutNames [nDestinationCounter] = sNodeName + rProperty.Name;
294 plOutValues[nDestinationCounter] = rProperty.Value;
295 ++nDestinationCounter;
298 // ... or copy normal items to return lists directly.
299 else
301 if( (nDestinationCounter+1) > lOutNames.getLength() )
303 lOutNames.realloc ( nDestinationCounter+1 );
304 plOutNames = lOutNames.getArray();
305 lOutValues.realloc ( nDestinationCounter+1 );
306 plOutValues = lOutValues.getArray();
309 plOutNames [nDestinationCounter] = lInNames [nSourceCounter];
310 plOutValues[nDestinationCounter] = lInValues[nSourceCounter];
311 ++nDestinationCounter;
316 Sequence< sal_Bool > ConfigItem::GetReadOnlyStates(const css::uno::Sequence< OUString >& rNames)
318 sal_Int32 i;
320 // size of return list is fix!
321 // Every item must match to length of incoming name list.
322 sal_Int32 nCount = rNames.getLength();
323 Sequence< sal_Bool > lStates(nCount);
324 sal_Bool* plStates = lStates.getArray();
326 // We must be sure to return a valid information every time!
327 // Set default to non readonly... similar to the configuration handling of this property.
328 std::fill_n(plStates, lStates.getLength(), false);
330 // no access - no information...
331 Reference< XHierarchicalNameAccess > xHierarchyAccess = GetTree();
332 if (!xHierarchyAccess.is())
333 return lStates;
335 for (i=0; i<nCount; ++i)
339 OUString sName = rNames[i];
340 OUString sPath;
341 OUString sProperty;
343 (void)::utl::splitLastFromConfigurationPath(sName,sPath,sProperty);
344 if (sPath.isEmpty() && sProperty.isEmpty())
346 OSL_FAIL("ConfigItem::IsReadonly() split failed");
347 continue;
350 Reference< XInterface > xNode;
351 Reference< XPropertySet > xSet;
352 Reference< XPropertySetInfo > xInfo;
353 if (!sPath.isEmpty())
355 Any aNode = xHierarchyAccess->getByHierarchicalName(sPath);
356 if (!(aNode >>= xNode) || !xNode.is())
358 OSL_FAIL("ConfigItem::IsReadonly() no set available");
359 continue;
362 else
364 xNode = xHierarchyAccess;
367 xSet.set(xNode, UNO_QUERY);
368 if (xSet.is())
370 xInfo = xSet->getPropertySetInfo();
371 OSL_ENSURE(xInfo.is(), "ConfigItem::IsReadonly() getPropertySetInfo failed ...");
373 else
375 xInfo.set(xNode, UNO_QUERY);
376 OSL_ENSURE(xInfo.is(), "ConfigItem::IsReadonly() UNO_QUERY failed ...");
379 if (!xInfo.is())
381 OSL_FAIL("ConfigItem::IsReadonly() no prop info available");
382 continue;
385 Property aProp = xInfo->getPropertyByName(sProperty);
386 plStates[i] = (aProp.Attributes & PropertyAttribute::READONLY) == PropertyAttribute::READONLY;
388 catch (const Exception&)
393 return lStates;
396 Sequence< Any > ConfigItem::GetProperties(const Sequence< OUString >& rNames)
398 Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
399 if(xHierarchyAccess.is())
400 return GetProperties(xHierarchyAccess, rNames,
401 (m_nMode & ConfigItemMode::AllLocales ) == ConfigItemMode::AllLocales);
402 return Sequence< Any >(rNames.getLength());
405 Sequence< Any > ConfigItem::GetProperties(
406 css::uno::Reference<css::container::XHierarchicalNameAccess> const & xHierarchyAccess,
407 const Sequence< OUString >& rNames,
408 bool bAllLocales)
410 Sequence< Any > aRet(rNames.getLength());
411 const OUString* pNames = rNames.getConstArray();
412 Any* pRet = aRet.getArray();
413 for(int i = 0; i < rNames.getLength(); i++)
417 pRet[i] = xHierarchyAccess->getByHierarchicalName(pNames[i]);
419 catch (const Exception&)
421 TOOLS_WARN_EXCEPTION(
422 "unotools.config",
423 "ignoring XHierarchicalNameAccess " << pNames[i]);
427 // In special mode "ALL_LOCALES" we must convert localized values to Sequence< PropertyValue >.
428 if(bAllLocales)
430 Sequence< Any > lValues;
431 impl_packLocalizedProperties( rNames, aRet, lValues );
432 aRet = lValues;
434 return aRet;
437 bool ConfigItem::PutProperties( const Sequence< OUString >& rNames,
438 const Sequence< Any>& rValues)
440 ValueCounter_Impl aCounter(m_nInValueChange);
441 Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
442 Reference<XNameReplace> xTopNodeReplace(xHierarchyAccess, UNO_QUERY);
443 bool bRet = xHierarchyAccess.is() && xTopNodeReplace.is();
444 if(bRet)
446 Sequence< OUString > lNames;
447 Sequence< Any > lValues;
448 const OUString* pNames = nullptr;
449 const Any* pValues = nullptr;
450 sal_Int32 nNameCount;
451 if(( m_nMode & ConfigItemMode::AllLocales ) == ConfigItemMode::AllLocales )
453 // If ConfigItem works in "ALL_LOCALES"-mode ... we must support a Sequence< PropertyValue >
454 // as value of a localized configuration entry!
455 // How we can do that?
456 // We must split all PropertyValues to "Sequence< OUString >" AND "Sequence< Any >"!
457 impl_unpackLocalizedProperties( rNames, rValues, lNames, lValues );
458 pNames = lNames.getConstArray ();
459 pValues = lValues.getConstArray ();
460 nNameCount = lNames.getLength ();
462 else
464 // This is the normal mode ...
465 // Use given input lists directly.
466 pNames = rNames.getConstArray ();
467 pValues = rValues.getConstArray ();
468 nNameCount = rNames.getLength ();
470 for(int i = 0; i < nNameCount; i++)
474 OUString sNode, sProperty;
475 if (splitLastFromConfigurationPath(pNames[i],sNode, sProperty))
477 Any aNode = xHierarchyAccess->getByHierarchicalName(sNode);
479 Reference<XNameAccess> xNodeAcc;
480 aNode >>= xNodeAcc;
481 Reference<XNameReplace> xNodeReplace(xNodeAcc, UNO_QUERY);
482 Reference<XNameContainer> xNodeCont (xNodeAcc, UNO_QUERY);
484 bool bExist = (xNodeAcc.is() && xNodeAcc->hasByName(sProperty));
485 if (bExist && xNodeReplace.is())
486 xNodeReplace->replaceByName(sProperty, pValues[i]);
487 else
488 if (!bExist && xNodeCont.is())
489 xNodeCont->insertByName(sProperty, pValues[i]);
490 else
491 bRet = false;
493 else //direct value
495 xTopNodeReplace->replaceByName(sProperty, pValues[i]);
498 catch (css::uno::Exception &)
500 TOOLS_WARN_EXCEPTION("unotools.config", "Exception from PutProperties");
505 Reference<XChangesBatch> xBatch(xHierarchyAccess, UNO_QUERY);
506 xBatch->commitChanges();
508 catch (css::uno::Exception &)
510 TOOLS_WARN_EXCEPTION("unotools.config", "Exception from commitChanges");
514 return bRet;
517 bool ConfigItem::PutProperties(
518 css::uno::Reference<css::container::XHierarchicalNameAccess> const & xHierarchyAccess,
519 const Sequence< OUString >& rNames,
520 const Sequence< Any>& rValues,
521 bool bAllLocales)
523 Reference<XNameReplace> xTopNodeReplace(xHierarchyAccess, UNO_QUERY);
524 bool bRet = xTopNodeReplace.is();
525 if(bRet)
527 Sequence< OUString > lNames;
528 Sequence< Any > lValues;
529 const OUString* pNames = nullptr;
530 const Any* pValues = nullptr;
531 sal_Int32 nNameCount;
532 if(bAllLocales)
534 // If ConfigItem works in "ALL_LOCALES"-mode ... we must support a Sequence< PropertyValue >
535 // as value of a localized configuration entry!
536 // How we can do that?
537 // We must split all PropertyValues to "Sequence< OUString >" AND "Sequence< Any >"!
538 impl_unpackLocalizedProperties( rNames, rValues, lNames, lValues );
539 pNames = lNames.getConstArray ();
540 pValues = lValues.getConstArray ();
541 nNameCount = lNames.getLength ();
543 else
545 // This is the normal mode ...
546 // Use given input lists directly.
547 pNames = rNames.getConstArray ();
548 pValues = rValues.getConstArray ();
549 nNameCount = rNames.getLength ();
551 for(int i = 0; i < nNameCount; i++)
555 OUString sNode, sProperty;
556 if (splitLastFromConfigurationPath(pNames[i],sNode, sProperty))
558 Any aNode = xHierarchyAccess->getByHierarchicalName(sNode);
560 Reference<XNameAccess> xNodeAcc;
561 aNode >>= xNodeAcc;
562 Reference<XNameReplace> xNodeReplace(xNodeAcc, UNO_QUERY);
563 Reference<XNameContainer> xNodeCont (xNodeAcc, UNO_QUERY);
565 bool bExist = (xNodeAcc.is() && xNodeAcc->hasByName(sProperty));
566 if (bExist && xNodeReplace.is())
567 xNodeReplace->replaceByName(sProperty, pValues[i]);
568 else
569 if (!bExist && xNodeCont.is())
570 xNodeCont->insertByName(sProperty, pValues[i]);
571 else
572 bRet = false;
574 else //direct value
576 xTopNodeReplace->replaceByName(sProperty, pValues[i]);
579 catch (css::uno::Exception &)
581 TOOLS_WARN_EXCEPTION("unotools.config", "Exception from PutProperties");
586 Reference<XChangesBatch> xBatch(xHierarchyAccess, UNO_QUERY);
587 xBatch->commitChanges();
589 catch (css::uno::Exception &)
591 TOOLS_WARN_EXCEPTION("unotools.config", "Exception from commitChanges");
595 return bRet;
598 void ConfigItem::DisableNotification()
600 OSL_ENSURE( xChangeLstnr.is(), "ConfigItem::DisableNotification: notifications not enabled currently!" );
601 RemoveChangesListener();
604 bool ConfigItem::EnableNotification(const Sequence< OUString >& rNames,
605 bool bEnableInternalNotification )
607 OSL_ENSURE(!(m_nMode & ConfigItemMode::ReleaseTree), "notification in ConfigItemMode::ReleaseTree mode not possible");
608 m_bEnableInternalNotification = bEnableInternalNotification;
609 Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
610 Reference<XChangesNotifier> xChgNot(xHierarchyAccess, UNO_QUERY);
611 if(!xChgNot.is())
612 return false;
614 OSL_ENSURE(!xChangeLstnr.is(), "EnableNotification already called");
615 if(xChangeLstnr.is())
616 xChgNot->removeChangesListener( xChangeLstnr );
617 bool bRet = true;
621 xChangeLstnr = new ConfigChangeListener_Impl(*this, rNames);
622 xChgNot->addChangesListener( xChangeLstnr );
624 catch (const RuntimeException&)
626 bRet = false;
628 return bRet;
631 void ConfigItem::RemoveChangesListener()
633 Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
634 if(!xHierarchyAccess.is())
635 return;
637 Reference<XChangesNotifier> xChgNot(xHierarchyAccess, UNO_QUERY);
638 if(xChgNot.is() && xChangeLstnr.is())
642 xChgNot->removeChangesListener( xChangeLstnr );
643 xChangeLstnr = nullptr;
645 catch (const Exception&)
651 static void lcl_normalizeLocalNames(Sequence< OUString >& _rNames, ConfigNameFormat _eFormat, Reference<XInterface> const& _xParentNode)
653 switch (_eFormat)
655 case ConfigNameFormat::LocalNode:
656 // unaltered - this is our input format
657 break;
659 case ConfigNameFormat::LocalPath:
661 Reference<XTemplateContainer> xTypeContainer(_xParentNode, UNO_QUERY);
662 if (xTypeContainer.is())
664 OUString sTypeName = xTypeContainer->getElementTemplateName();
665 sTypeName = sTypeName.copy(sTypeName.lastIndexOf('/')+1);
667 std::transform(std::cbegin(_rNames), std::cend(_rNames), _rNames.getArray(),
668 [&sTypeName](const OUString& rName) -> OUString { return wrapConfigurationElementName(rName,sTypeName); });
670 else
672 Reference<XServiceInfo> xSVI(_xParentNode, UNO_QUERY);
673 if (xSVI.is() && xSVI->supportsService("com.sun.star.configuration.SetAccess"))
675 std::transform(std::cbegin(_rNames), std::cend(_rNames), _rNames.getArray(),
676 [](const OUString& rName) -> OUString { return wrapConfigurationElementName(rName); });
680 break;
685 Sequence< OUString > ConfigItem::GetNodeNames(const OUString& rNode)
687 ConfigNameFormat const eDefaultFormat = ConfigNameFormat::LocalNode; // CONFIG_NAME_DEFAULT;
689 return GetNodeNames(rNode, eDefaultFormat);
692 Sequence< OUString > ConfigItem::GetNodeNames(const OUString& rNode, ConfigNameFormat eFormat)
694 Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
695 if(xHierarchyAccess.is())
696 return GetNodeNames(xHierarchyAccess, rNode, eFormat);
697 return Sequence< OUString >();
700 Sequence< OUString > ConfigItem::GetNodeNames(
701 css::uno::Reference<css::container::XHierarchicalNameAccess> const & xHierarchyAccess,
702 const OUString& rNode,
703 ConfigNameFormat eFormat)
705 Sequence< OUString > aRet;
708 Reference<XNameAccess> xCont;
709 if(!rNode.isEmpty())
711 Any aNode = xHierarchyAccess->getByHierarchicalName(rNode);
712 aNode >>= xCont;
714 else
715 xCont.set(xHierarchyAccess, UNO_QUERY);
716 if(xCont.is())
718 aRet = xCont->getElementNames();
719 lcl_normalizeLocalNames(aRet,eFormat,xCont);
723 catch (css::uno::Exception &)
725 TOOLS_WARN_EXCEPTION("unotools.config", "Exception from GetNodeNames");
727 return aRet;
730 bool ConfigItem::ClearNodeSet(const OUString& rNode)
732 ValueCounter_Impl aCounter(m_nInValueChange);
733 bool bRet = false;
734 Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
735 if(xHierarchyAccess.is())
736 bRet = ClearNodeSet(xHierarchyAccess, rNode);
737 return bRet;
740 bool ConfigItem::ClearNodeSet(
741 css::uno::Reference<css::container::XHierarchicalNameAccess> const & xHierarchyAccess,
742 const OUString& rNode)
744 bool bRet = false;
747 Reference<XNameContainer> xCont;
748 if(!rNode.isEmpty())
750 Any aNode = xHierarchyAccess->getByHierarchicalName(rNode);
751 aNode >>= xCont;
753 else
754 xCont.set(xHierarchyAccess, UNO_QUERY);
755 if(!xCont.is())
756 return false;
757 const Sequence< OUString > aNames = xCont->getElementNames();
758 Reference<XChangesBatch> xBatch(xHierarchyAccess, UNO_QUERY);
759 for(const OUString& rName : aNames)
763 xCont->removeByName(rName);
765 catch (css::uno::Exception &)
767 TOOLS_WARN_EXCEPTION("unotools.config", "Exception from removeByName");
770 xBatch->commitChanges();
771 bRet = true;
773 catch (css::uno::Exception &)
775 TOOLS_WARN_EXCEPTION("unotools.config", "Exception from ClearNodeSet");
777 return bRet;
780 bool ConfigItem::ClearNodeElements(const OUString& rNode, Sequence< OUString > const & rElements)
782 ValueCounter_Impl aCounter(m_nInValueChange);
783 bool bRet = false;
784 Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
785 if(xHierarchyAccess.is())
789 Reference<XNameContainer> xCont;
790 if(!rNode.isEmpty())
792 Any aNode = xHierarchyAccess->getByHierarchicalName(rNode);
793 aNode >>= xCont;
795 else
796 xCont.set(xHierarchyAccess, UNO_QUERY);
797 if(!xCont.is())
798 return false;
801 for(const OUString& rElement : rElements)
803 xCont->removeByName(rElement);
805 Reference<XChangesBatch> xBatch(xHierarchyAccess, UNO_QUERY);
806 xBatch->commitChanges();
808 catch (css::uno::Exception &)
810 TOOLS_WARN_EXCEPTION("unotools.config", "Exception from commitChanges()");
812 bRet = true;
814 catch (css::uno::Exception &)
816 TOOLS_WARN_EXCEPTION("unotools.config", "Exception from GetNodeNames()");
819 return bRet;
822 static OUString lcl_extractSetPropertyName( const OUString& rInPath, std::u16string_view rPrefix )
824 OUString const sSubPath = dropPrefixFromConfigurationPath( rInPath, rPrefix);
825 return extractFirstFromConfigurationPath( sSubPath );
828 static
829 Sequence< OUString > lcl_extractSetPropertyNames( const Sequence< PropertyValue >& rValues, std::u16string_view rPrefix )
831 Sequence< OUString > aSubNodeNames(rValues.getLength());
832 OUString* pSubNodeNames = aSubNodeNames.getArray();
834 OUString sLastSubNode;
835 sal_Int32 nSubIndex = 0;
837 for(const PropertyValue& rProperty : rValues)
839 OUString const sSubPath = dropPrefixFromConfigurationPath( rProperty.Name, rPrefix);
840 OUString const sSubNode = extractFirstFromConfigurationPath( sSubPath );
842 if(sLastSubNode != sSubNode)
844 pSubNodeNames[nSubIndex++] = sSubNode;
847 sLastSubNode = sSubNode;
849 aSubNodeNames.realloc(nSubIndex);
851 return aSubNodeNames;
854 // Add or change properties
855 bool ConfigItem::SetSetProperties(
856 const OUString& rNode, const Sequence< PropertyValue >& rValues)
858 ValueCounter_Impl aCounter(m_nInValueChange);
859 Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
860 if(!xHierarchyAccess.is())
861 return true;
862 return SetSetProperties(xHierarchyAccess, rNode, rValues);
865 // Add or change properties
866 bool ConfigItem::SetSetProperties(
867 css::uno::Reference<css::container::XHierarchicalNameAccess> const & xHierarchyAccess,
868 const OUString& rNode, const Sequence< PropertyValue >& rValues)
870 bool bRet = true;
871 Reference<XChangesBatch> xBatch(xHierarchyAccess, UNO_QUERY);
874 Reference<XNameContainer> xCont;
875 if(!rNode.isEmpty())
877 Any aNode = xHierarchyAccess->getByHierarchicalName(rNode);
878 aNode >>= xCont;
880 else
881 xCont.set(xHierarchyAccess, UNO_QUERY);
882 if(!xCont.is())
883 return false;
885 Reference<XSingleServiceFactory> xFac(xCont, UNO_QUERY);
887 if(xFac.is())
889 const Sequence< OUString > aSubNodeNames = lcl_extractSetPropertyNames(rValues, rNode);
891 for(const auto& rSubNodeName : aSubNodeNames)
893 if(!xCont->hasByName(rSubNodeName))
895 Reference<XInterface> xInst = xFac->createInstance();
896 Any aVal; aVal <<= xInst;
897 xCont->insertByName(rSubNodeName, aVal);
899 //set values
903 xBatch->commitChanges();
905 catch (css::uno::Exception &)
907 TOOLS_WARN_EXCEPTION("unotools.config", "Exception from commitChanges()");
910 const PropertyValue* pProperties = rValues.getConstArray();
912 Sequence< OUString > aSetNames(rValues.getLength());
913 OUString* pSetNames = aSetNames.getArray();
915 Sequence< Any> aSetValues(rValues.getLength());
916 Any* pSetValues = aSetValues.getArray();
918 bool bEmptyNode = rNode.isEmpty();
919 for(sal_Int32 k = 0; k < rValues.getLength(); k++)
921 pSetNames[k] = pProperties[k].Name.copy( bEmptyNode ? 1 : 0);
922 pSetValues[k] = pProperties[k].Value;
924 bRet = PutProperties(xHierarchyAccess, aSetNames, aSetValues, /*bAllLocales*/false);
926 else
928 //if no factory is available then the node contains basic data elements
929 for(const PropertyValue& rValue : rValues)
933 OUString sSubNode = lcl_extractSetPropertyName( rValue.Name, rNode );
935 if(xCont->hasByName(sSubNode))
936 xCont->replaceByName(sSubNode, rValue.Value);
937 else
938 xCont->insertByName(sSubNode, rValue.Value);
940 OSL_ENSURE( xHierarchyAccess->hasByHierarchicalName(rValue.Name),
941 "Invalid config path" );
943 catch (css::uno::Exception &)
945 TOOLS_WARN_EXCEPTION("unotools.config", "Exception from insert/replaceByName()");
948 xBatch->commitChanges();
951 catch (const Exception&)
953 TOOLS_WARN_EXCEPTION("unotools.config", "Exception from SetSetProperties");
954 bRet = false;
956 return bRet;
959 bool ConfigItem::ReplaceSetProperties(
960 const OUString& rNode, const Sequence< PropertyValue >& rValues)
962 ValueCounter_Impl aCounter(m_nInValueChange);
963 bool bRet = true;
964 Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
965 if(xHierarchyAccess.is())
966 bRet = ReplaceSetProperties(xHierarchyAccess, rNode, rValues,
967 ( m_nMode & ConfigItemMode::AllLocales ) == ConfigItemMode::AllLocales);
968 return bRet;
971 bool ConfigItem::ReplaceSetProperties(
972 css::uno::Reference<css::container::XHierarchicalNameAccess> const & xHierarchyAccess,
973 const OUString& rNode,
974 const Sequence< PropertyValue >& rValues,
975 bool bAllLocales)
977 bool bRet = true;
978 Reference<XChangesBatch> xBatch(xHierarchyAccess, UNO_QUERY);
981 Reference<XNameContainer> xCont;
982 if(!rNode.isEmpty())
984 Any aNode = xHierarchyAccess->getByHierarchicalName(rNode);
985 aNode >>= xCont;
987 else
988 xCont.set(xHierarchyAccess, UNO_QUERY);
989 if(!xCont.is())
990 return false;
992 // JB: Change: now the same name handling for sets of simple values
993 const Sequence< OUString > aSubNodeNames = lcl_extractSetPropertyNames(rValues, rNode);
995 Reference<XSingleServiceFactory> xFac(xCont, UNO_QUERY);
996 const bool isSimpleValueSet = !xFac.is();
998 //remove unknown members first
1000 const Sequence<OUString> aContainerSubNodes = xCont->getElementNames();
1002 for(const OUString& rContainerSubNode : aContainerSubNodes)
1004 bool bFound = comphelper::findValue(aSubNodeNames, rContainerSubNode) != -1;
1005 if(!bFound)
1008 xCont->removeByName(rContainerSubNode);
1010 catch (const Exception&)
1012 if (isSimpleValueSet)
1016 // #i37322#: fallback action: replace with <void/>
1017 xCont->replaceByName(rContainerSubNode, Any());
1018 // fallback successful: continue looping
1019 continue;
1021 catch (Exception &)
1022 {} // propagate original exception, if fallback fails
1024 throw;
1027 try { xBatch->commitChanges(); }
1028 catch (css::uno::Exception &)
1030 TOOLS_WARN_EXCEPTION("unotools.config", "Exception from commitChanges");
1034 if(xFac.is()) // !isSimpleValueSet
1036 for(const OUString& rSubNodeName : aSubNodeNames)
1038 if(!xCont->hasByName(rSubNodeName))
1040 //create if not available
1041 Reference<XInterface> xInst = xFac->createInstance();
1042 Any aVal; aVal <<= xInst;
1043 xCont->insertByName(rSubNodeName, aVal);
1046 try { xBatch->commitChanges(); }
1047 catch (css::uno::Exception &)
1049 TOOLS_WARN_EXCEPTION("unotools.config", "Exception from commitChanges");
1052 const PropertyValue* pProperties = rValues.getConstArray();
1054 Sequence< OUString > aSetNames(rValues.getLength());
1055 OUString* pSetNames = aSetNames.getArray();
1057 Sequence< Any> aSetValues(rValues.getLength());
1058 Any* pSetValues = aSetValues.getArray();
1060 bool bEmptyNode = rNode.isEmpty();
1061 for(sal_Int32 k = 0; k < rValues.getLength(); k++)
1063 pSetNames[k] = pProperties[k].Name.copy( bEmptyNode ? 1 : 0);
1064 pSetValues[k] = pProperties[k].Value;
1066 bRet = PutProperties(xHierarchyAccess, aSetNames, aSetValues, bAllLocales);
1068 else
1070 //if no factory is available then the node contains basic data elements
1071 for(const PropertyValue& rValue : rValues)
1075 OUString sSubNode = lcl_extractSetPropertyName( rValue.Name, rNode );
1077 if(xCont->hasByName(sSubNode))
1078 xCont->replaceByName(sSubNode, rValue.Value);
1079 else
1080 xCont->insertByName(sSubNode, rValue.Value);
1082 catch (css::uno::Exception &)
1084 TOOLS_WARN_EXCEPTION("unotools.config", "Exception from insert/replaceByName");
1087 xBatch->commitChanges();
1090 catch (const Exception& )
1092 TOOLS_WARN_EXCEPTION("unotools.config", "Exception from ReplaceSetProperties");
1093 bRet = false;
1095 return bRet;
1098 bool ConfigItem::AddNode(const OUString& rNode, const OUString& rNewNode)
1100 ValueCounter_Impl aCounter(m_nInValueChange);
1101 bool bRet = true;
1102 Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
1103 if(xHierarchyAccess.is())
1105 Reference<XChangesBatch> xBatch(xHierarchyAccess, UNO_QUERY);
1108 Reference<XNameContainer> xCont;
1109 if(!rNode.isEmpty())
1111 Any aNode = xHierarchyAccess->getByHierarchicalName(rNode);
1112 aNode >>= xCont;
1114 else
1115 xCont.set(xHierarchyAccess, UNO_QUERY);
1116 if(!xCont.is())
1117 return false;
1119 Reference<XSingleServiceFactory> xFac(xCont, UNO_QUERY);
1121 if(xFac.is())
1123 if(!xCont->hasByName(rNewNode))
1125 Reference<XInterface> xInst = xFac->createInstance();
1126 Any aVal; aVal <<= xInst;
1127 xCont->insertByName(rNewNode, aVal);
1131 xBatch->commitChanges();
1133 catch (css::uno::Exception &)
1135 TOOLS_WARN_EXCEPTION("unotools.config", "Exception from commitChanges");
1138 else
1140 //if no factory is available then the node contains basic data elements
1143 if(!xCont->hasByName(rNewNode))
1144 xCont->insertByName(rNewNode, Any());
1146 catch (css::uno::Exception &)
1148 TOOLS_WARN_EXCEPTION("unotools.config", "Exception from AddNode");
1151 xBatch->commitChanges();
1153 catch (const Exception&)
1155 DBG_UNHANDLED_EXCEPTION("unotools.config");
1156 bRet = false;
1159 return bRet;
1163 void ConfigItem::SetModified()
1165 m_bIsModified = true;
1168 void ConfigItem::ClearModified()
1170 m_bIsModified = false;
1173 Reference< XHierarchicalNameAccess> ConfigItem::GetTree()
1175 Reference< XHierarchicalNameAccess> xRet;
1176 if (utl::ConfigManager::IsFuzzing())
1177 return xRet;
1178 if(!m_xHierarchyAccess.is())
1179 xRet = ConfigManager::acquireTree(*this);
1180 else
1181 xRet = m_xHierarchyAccess;
1182 return xRet;
1185 void ConfigItem::Commit()
1187 ImplCommit();
1188 ClearModified();
1191 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */