1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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>
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
62 class ConfigChangeListener_Impl
: public cppu::WeakImplHelper
64 css::util::XChangesListener
69 const Sequence
< OUString
> aPropertyNames
;
70 ConfigChangeListener_Impl(ConfigItem
& rItem
, const Sequence
< OUString
>& rNames
);
73 virtual void SAL_CALL
changesOccurred( const ChangesEvent
& Event
) override
;
76 virtual void SAL_CALL
disposing( const EventObject
& Source
) override
;
82 class ValueCounter_Impl
86 explicit ValueCounter_Impl(sal_Int16
& rCounter
)
93 OSL_ENSURE(rCnt
>0, "RefCount < 0 ??");
100 ConfigChangeListener_Impl::ConfigChangeListener_Impl(
101 ConfigItem
& rItem
, const Sequence
< OUString
>& rNames
) :
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
)
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
); });
122 pNames
[nNotify
++] = sTemp
;
126 ::comphelper::SolarMutex
*pMutex
= ::comphelper::SolarMutex::get();
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
)),
144 m_bIsModified(false),
145 m_bEnableInternalNotification(false),
148 if (utl::ConfigManager::IsFuzzing())
151 if (nSetMode
& ConfigItemMode::ReleaseTree
)
152 ConfigManager::getConfigManager().addConfigItem(*this);
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();
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.
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();
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.
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
)
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())
335 for (i
=0; i
<nCount
; ++i
)
339 OUString sName
= rNames
[i
];
343 (void)::utl::splitLastFromConfigurationPath(sName
,sPath
,sProperty
);
344 if (sPath
.isEmpty() && sProperty
.isEmpty())
346 OSL_FAIL("ConfigItem::IsReadonly() split failed");
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");
364 xNode
= xHierarchyAccess
;
367 xSet
.set(xNode
, UNO_QUERY
);
370 xInfo
= xSet
->getPropertySetInfo();
371 OSL_ENSURE(xInfo
.is(), "ConfigItem::IsReadonly() getPropertySetInfo failed ...");
375 xInfo
.set(xNode
, UNO_QUERY
);
376 OSL_ENSURE(xInfo
.is(), "ConfigItem::IsReadonly() UNO_QUERY failed ...");
381 OSL_FAIL("ConfigItem::IsReadonly() no prop info available");
385 Property aProp
= xInfo
->getPropertyByName(sProperty
);
386 plStates
[i
] = (aProp
.Attributes
& PropertyAttribute::READONLY
) == PropertyAttribute::READONLY
;
388 catch (const Exception
&)
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
,
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(
423 "ignoring XHierarchicalNameAccess " << pNames
[i
]);
427 // In special mode "ALL_LOCALES" we must convert localized values to Sequence< PropertyValue >.
430 Sequence
< Any
> lValues
;
431 impl_packLocalizedProperties( rNames
, aRet
, lValues
);
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();
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 ();
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
;
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
]);
488 if (!bExist
&& xNodeCont
.is())
489 xNodeCont
->insertByName(sProperty
, pValues
[i
]);
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");
517 bool ConfigItem::PutProperties(
518 css::uno::Reference
<css::container::XHierarchicalNameAccess
> const & xHierarchyAccess
,
519 const Sequence
< OUString
>& rNames
,
520 const Sequence
< Any
>& rValues
,
523 Reference
<XNameReplace
> xTopNodeReplace(xHierarchyAccess
, UNO_QUERY
);
524 bool bRet
= xTopNodeReplace
.is();
527 Sequence
< OUString
> lNames
;
528 Sequence
< Any
> lValues
;
529 const OUString
* pNames
= nullptr;
530 const Any
* pValues
= nullptr;
531 sal_Int32 nNameCount
;
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 ();
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
;
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
]);
569 if (!bExist
&& xNodeCont
.is())
570 xNodeCont
->insertByName(sProperty
, pValues
[i
]);
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");
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
);
614 OSL_ENSURE(!xChangeLstnr
.is(), "EnableNotification already called");
615 if(xChangeLstnr
.is())
616 xChgNot
->removeChangesListener( xChangeLstnr
);
621 xChangeLstnr
= new ConfigChangeListener_Impl(*this, rNames
);
622 xChgNot
->addChangesListener( xChangeLstnr
);
624 catch (const RuntimeException
&)
631 void ConfigItem::RemoveChangesListener()
633 Reference
<XHierarchicalNameAccess
> xHierarchyAccess
= GetTree();
634 if(!xHierarchyAccess
.is())
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
)
655 case ConfigNameFormat::LocalNode
:
656 // unaltered - this is our input format
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
); });
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
); });
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
;
711 Any aNode
= xHierarchyAccess
->getByHierarchicalName(rNode
);
715 xCont
.set(xHierarchyAccess
, UNO_QUERY
);
718 aRet
= xCont
->getElementNames();
719 lcl_normalizeLocalNames(aRet
,eFormat
,xCont
);
723 catch (css::uno::Exception
&)
725 TOOLS_WARN_EXCEPTION("unotools.config", "Exception from GetNodeNames");
730 bool ConfigItem::ClearNodeSet(const OUString
& rNode
)
732 ValueCounter_Impl
aCounter(m_nInValueChange
);
734 Reference
<XHierarchicalNameAccess
> xHierarchyAccess
= GetTree();
735 if(xHierarchyAccess
.is())
736 bRet
= ClearNodeSet(xHierarchyAccess
, rNode
);
740 bool ConfigItem::ClearNodeSet(
741 css::uno::Reference
<css::container::XHierarchicalNameAccess
> const & xHierarchyAccess
,
742 const OUString
& rNode
)
747 Reference
<XNameContainer
> xCont
;
750 Any aNode
= xHierarchyAccess
->getByHierarchicalName(rNode
);
754 xCont
.set(xHierarchyAccess
, UNO_QUERY
);
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();
773 catch (css::uno::Exception
&)
775 TOOLS_WARN_EXCEPTION("unotools.config", "Exception from ClearNodeSet");
780 bool ConfigItem::ClearNodeElements(const OUString
& rNode
, Sequence
< OUString
> const & rElements
)
782 ValueCounter_Impl
aCounter(m_nInValueChange
);
784 Reference
<XHierarchicalNameAccess
> xHierarchyAccess
= GetTree();
785 if(xHierarchyAccess
.is())
789 Reference
<XNameContainer
> xCont
;
792 Any aNode
= xHierarchyAccess
->getByHierarchicalName(rNode
);
796 xCont
.set(xHierarchyAccess
, UNO_QUERY
);
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()");
814 catch (css::uno::Exception
&)
816 TOOLS_WARN_EXCEPTION("unotools.config", "Exception from GetNodeNames()");
822 static OUString
lcl_extractSetPropertyName( const OUString
& rInPath
, std::u16string_view rPrefix
)
824 OUString
const sSubPath
= dropPrefixFromConfigurationPath( rInPath
, rPrefix
);
825 return extractFirstFromConfigurationPath( sSubPath
);
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())
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
)
871 Reference
<XChangesBatch
> xBatch(xHierarchyAccess
, UNO_QUERY
);
874 Reference
<XNameContainer
> xCont
;
877 Any aNode
= xHierarchyAccess
->getByHierarchicalName(rNode
);
881 xCont
.set(xHierarchyAccess
, UNO_QUERY
);
885 Reference
<XSingleServiceFactory
> xFac(xCont
, UNO_QUERY
);
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
);
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);
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
);
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");
959 bool ConfigItem::ReplaceSetProperties(
960 const OUString
& rNode
, const Sequence
< PropertyValue
>& rValues
)
962 ValueCounter_Impl
aCounter(m_nInValueChange
);
964 Reference
<XHierarchicalNameAccess
> xHierarchyAccess
= GetTree();
965 if(xHierarchyAccess
.is())
966 bRet
= ReplaceSetProperties(xHierarchyAccess
, rNode
, rValues
,
967 ( m_nMode
& ConfigItemMode::AllLocales
) == ConfigItemMode::AllLocales
);
971 bool ConfigItem::ReplaceSetProperties(
972 css::uno::Reference
<css::container::XHierarchicalNameAccess
> const & xHierarchyAccess
,
973 const OUString
& rNode
,
974 const Sequence
< PropertyValue
>& rValues
,
978 Reference
<XChangesBatch
> xBatch(xHierarchyAccess
, UNO_QUERY
);
981 Reference
<XNameContainer
> xCont
;
984 Any aNode
= xHierarchyAccess
->getByHierarchicalName(rNode
);
988 xCont
.set(xHierarchyAccess
, UNO_QUERY
);
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;
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
1022 {} // propagate original exception, if fallback fails
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
);
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
);
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");
1098 bool ConfigItem::AddNode(const OUString
& rNode
, const OUString
& rNewNode
)
1100 ValueCounter_Impl
aCounter(m_nInValueChange
);
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
);
1115 xCont
.set(xHierarchyAccess
, UNO_QUERY
);
1119 Reference
<XSingleServiceFactory
> xFac(xCont
, UNO_QUERY
);
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");
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");
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())
1178 if(!m_xHierarchyAccess
.is())
1179 xRet
= ConfigManager::acquireTree(*this);
1181 xRet
= m_xHierarchyAccess
;
1185 void ConfigItem::Commit()
1191 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */