1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20 #include <sal/config.h>
21
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 <osl/diagnose.h>
38 #include <comphelper/sequence.hxx>
39 #include <comphelper/solarmutex.hxx>
40 #include <rtl/ref.hxx>
41 #include <tools/diagnose_ex.h>
42
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;
50
51 #include <cppuhelper/implbase.hxx>
52
53 /*
54 The ConfigChangeListener_Impl receives notifications from the configuration about changes that
55 have happened. It forwards this notification to the ConfigItem it knows a pParent by calling its
56 "CallNotify" method. As ConfigItems are most probably not thread safe, the SolarMutex is acquired
57 before doing so.
58 */
59
60 namespace utl{
61 class ConfigChangeListener_Impl : public cppu::WeakImplHelper
62 <
63 css::util::XChangesListener
64 >
65 {
66 public:
67 ConfigItem* pParent;
68 const Sequence< OUString > aPropertyNames;
69 ConfigChangeListener_Impl(ConfigItem& rItem, const Sequence< OUString >& rNames);
70
71 //XChangesListener
72 virtual void SAL_CALL changesOccurred( const ChangesEvent& Event ) override;
73
74 //XEventListener
75 virtual void SAL_CALL disposing( const EventObject& Source ) override;
76 };
77 }
78
79 class ValueCounter_Impl
80 {
81 sal_Int16& rCnt;
82 public:
ValueCounter_Impl(sal_Int16 & rCounter)83 explicit ValueCounter_Impl(sal_Int16& rCounter)
84 : rCnt(rCounter)
85 {
86 rCnt++;
87 }
~ValueCounter_Impl()88 ~ValueCounter_Impl()
89 {
90 OSL_ENSURE(rCnt>0, "RefCount < 0 ??");
91 rCnt--;
92 }
93 };
94
ConfigChangeListener_Impl(ConfigItem & rItem,const Sequence<OUString> & rNames)95 ConfigChangeListener_Impl::ConfigChangeListener_Impl(
96 ConfigItem& rItem, const Sequence< OUString >& rNames) :
97 pParent(&rItem),
98 aPropertyNames(rNames)
99 {
100 }
101
changesOccurred(const ChangesEvent & rEvent)102 void ConfigChangeListener_Impl::changesOccurred( const ChangesEvent& rEvent )
103 {
104 Sequence<OUString> aChangedNames(rEvent.Changes.getLength());
105 OUString* pNames = aChangedNames.getArray();
106
107 sal_Int32 nNotify = 0;
108 for(const auto& rElementChange : rEvent.Changes)
109 {
110 OUString sTemp;
111 rElementChange.Accessor >>= sTemp;
112 //true if the path is completely correct or if it is longer
113 //i.e ...Print/Content/Graphic and .../Print
114 bool bFound = std::any_of(aPropertyNames.begin(), aPropertyNames.end(),
115 [&sTemp](const OUString& rCheckPropertyName) { return isPrefixOfConfigurationPath(sTemp, rCheckPropertyName); });
116 if(bFound)
117 pNames[nNotify++] = sTemp;
118 }
119 if( nNotify )
120 {
121 ::comphelper::SolarMutex *pMutex = ::comphelper::SolarMutex::get();
122 if ( pMutex )
123 {
124 rtl::Reference< comphelper::SolarMutex > aGuard( pMutex );
125 aChangedNames.realloc(nNotify);
126 pParent->CallNotify(aChangedNames);
127 }
128 }
129 }
130
disposing(const EventObject &)131 void ConfigChangeListener_Impl::disposing( const EventObject& /*rSource*/ )
132 {
133 pParent->RemoveChangesListener();
134 }
135
ConfigItem(const OUString & rSubTree,ConfigItemMode nSetMode)136 ConfigItem::ConfigItem(const OUString &rSubTree, ConfigItemMode nSetMode ) :
137 sSubTree(rSubTree),
138 m_nMode(nSetMode),
139 m_bIsModified(false),
140 m_bEnableInternalNotification(false),
141 m_nInValueChange(0)
142 {
143 if (utl::ConfigManager::IsFuzzing())
144 return;
145
146 if (nSetMode & ConfigItemMode::ReleaseTree)
147 ConfigManager::getConfigManager().addConfigItem(*this);
148 else
149 m_xHierarchyAccess = ConfigManager::getConfigManager().addConfigItem(*this);
150 }
151
~ConfigItem()152 ConfigItem::~ConfigItem()
153 {
154 RemoveChangesListener();
155 ConfigManager::getConfigManager().removeConfigItem(*this);
156 }
157
CallNotify(const css::uno::Sequence<OUString> & rPropertyNames)158 void ConfigItem::CallNotify( const css::uno::Sequence<OUString>& rPropertyNames )
159 {
160 // the call is forwarded to the virtual Notify() method
161 // it is pure virtual, so all classes deriving from ConfigItem have to decide how they
162 // want to notify listeners
163 if(m_nInValueChange <= 0 || m_bEnableInternalNotification)
164 Notify(rPropertyNames);
165 }
166
impl_packLocalizedProperties(const Sequence<OUString> & lInNames,const Sequence<Any> & lInValues,Sequence<Any> & lOutValues)167 void ConfigItem::impl_packLocalizedProperties( const Sequence< OUString >& lInNames ,
168 const Sequence< Any >& lInValues ,
169 Sequence< Any >& lOutValues )
170 {
171 // Safe impossible cases.
172 // This method should be called for special ConfigItem-mode only!
173 OSL_ENSURE( ((m_nMode & ConfigItemMode::AllLocales ) == ConfigItemMode::AllLocales), "ConfigItem::impl_packLocalizedProperties() Wrong call of this method detected!" );
174
175 sal_Int32 nSourceCounter; // used to step during input lists
176 sal_Int32 nSourceSize; // marks end of loop over input lists
177 sal_Int32 nDestinationCounter; // actual position in output lists
178 sal_Int32 nPropertyCounter; // counter of inner loop for Sequence< PropertyValue >
179 sal_Int32 nPropertiesSize; // marks end of inner loop
180 Sequence< OUString > lPropertyNames; // list of all locales for localized entry
181 Sequence< PropertyValue > lProperties; // localized values of a configuration entry packed for return
182 Reference< XInterface > xLocalizedNode; // if cfg entry is localized ... lInValues contains an XInterface!
183
184 // Optimise follow algorithm ... A LITTLE BIT :-)
185 // There exist two different possibilities:
186 // i ) There exist no localized entries ... => size of lOutValues will be the same like lInNames/lInValues!
187 // ii) There exist some (mostly one or two) localized entries ... => size of lOutValues will be the same like lInNames/lInValues!
188 // ... Why? If a localized value exist - the any is filled with an XInterface object (is a SetNode-service).
189 // We read all his child nodes and pack it into Sequence< PropertyValue >.
190 // The result list we pack into the return any. We never change size of lists!
191 nSourceSize = lInNames.getLength();
192 lOutValues.realloc( nSourceSize );
193
194 // Algorithm:
195 // Copy all names and values from in to out lists.
196 // Look for special localized entries ... You can detect it as "XInterface" packed into an Any.
197 // Use this XInterface-object to read all localized values and pack it into Sequence< PropertValue >.
198 // Add this list to out lists then.
199
200 nDestinationCounter = 0;
201 for( nSourceCounter=0; nSourceCounter<nSourceSize; ++nSourceCounter )
202 {
203 // If item a special localized one ... convert and pack it ...
204 if( lInValues[nSourceCounter].getValueTypeName() == "com.sun.star.uno.XInterface" )
205 {
206 lInValues[nSourceCounter] >>= xLocalizedNode;
207 Reference< XNameContainer > xSetAccess( xLocalizedNode, UNO_QUERY );
208 if( xSetAccess.is() )
209 {
210 lPropertyNames = xSetAccess->getElementNames();
211 nPropertiesSize = lPropertyNames.getLength();
212 lProperties.realloc( nPropertiesSize );
213
214 for( nPropertyCounter=0; nPropertyCounter<nPropertiesSize; ++nPropertyCounter )
215 {
216 lProperties[nPropertyCounter].Name = lPropertyNames[nPropertyCounter];
217 OUString sLocaleValue;
218 xSetAccess->getByName( lPropertyNames[nPropertyCounter] ) >>= sLocaleValue;
219 lProperties[nPropertyCounter].Value <<= sLocaleValue;
220 }
221
222 lOutValues[nDestinationCounter] <<= lProperties;
223 }
224 }
225 // ... or copy normal items to return lists directly.
226 else
227 {
228 lOutValues[nDestinationCounter] = lInValues[nSourceCounter];
229 }
230 ++nDestinationCounter;
231 }
232 }
233
impl_unpackLocalizedProperties(const Sequence<OUString> & lInNames,const Sequence<Any> & lInValues,Sequence<OUString> & lOutNames,Sequence<Any> & lOutValues)234 void ConfigItem::impl_unpackLocalizedProperties( const Sequence< OUString >& lInNames ,
235 const Sequence< Any >& lInValues ,
236 Sequence< OUString >& lOutNames ,
237 Sequence< Any >& lOutValues )
238 {
239 // Safe impossible cases.
240 // This method should be called for special ConfigItem-mode only!
241 OSL_ENSURE( ((m_nMode & ConfigItemMode::AllLocales ) == ConfigItemMode::AllLocales), "ConfigItem::impl_unpackLocalizedProperties() Wrong call of this method detected!" );
242
243 sal_Int32 nSourceSize; // marks end of loop over input lists
244 sal_Int32 nDestinationCounter; // actual position in output lists
245 sal_Int32 nPropertiesSize; // marks end of inner loop
246 OUString sNodeName; // base name of node ( e.g. "UIName/" ) ... expand to locale ( e.g. "UIName/de" )
247 Sequence< PropertyValue > lProperties; // localized values of a configuration entry gotten from lInValues-Any
248
249 // Optimise follow algorithm ... A LITTLE BIT :-)
250 // There exist two different possibilities:
251 // i ) There exist no localized entries ... => size of lOutNames/lOutValues will be the same like lInNames/lInValues!
252 // ii) There exist some (mostly one or two) localized entries ... => size of lOutNames/lOutValues will be some bytes greater then lInNames/lInValues.
253 // => I think we should make it fast for i). ii) is a special case and mustn't be SOOOO... fast.
254 // We should reserve same space for output list like input ones first.
255 // Follow algorithm looks for these borders and change it for ii) only!
256 // It will be faster then a "realloc()" call in every loop ...
257 nSourceSize = lInNames.getLength();
258
259 lOutNames.realloc ( nSourceSize );
260 lOutValues.realloc ( nSourceSize );
261
262 // Algorithm:
263 // Copy all names and values from const to return lists.
264 // Look for special localized entries ... You can detect it as Sequence< PropertyValue > packed into an Any.
265 // Split it ... insert PropertyValue.Name to lOutNames and PropertyValue.Value to lOutValues.
266
267 nDestinationCounter = 0;
268 for( sal_Int32 nSourceCounter=0; nSourceCounter<nSourceSize; ++nSourceCounter )
269 {
270 // If item a special localized one ... split it and insert his parts to output lists ...
271 if( lInValues[nSourceCounter].getValueType() == cppu::UnoType<Sequence<PropertyValue>>::get() )
272 {
273 lInValues[nSourceCounter] >>= lProperties;
274 nPropertiesSize = lProperties.getLength();
275
276 sNodeName = lInNames[nSourceCounter] + "/";
277
278 if( (nDestinationCounter+nPropertiesSize) > lOutNames.getLength() )
279 {
280 lOutNames.realloc ( nDestinationCounter+nPropertiesSize );
281 lOutValues.realloc ( nDestinationCounter+nPropertiesSize );
282 }
283
284 for( const auto& rProperty : std::as_const(lProperties) )
285 {
286 lOutNames [nDestinationCounter] = sNodeName + rProperty.Name;
287 lOutValues[nDestinationCounter] = rProperty.Value;
288 ++nDestinationCounter;
289 }
290 }
291 // ... or copy normal items to return lists directly.
292 else
293 {
294 if( (nDestinationCounter+1) > lOutNames.getLength() )
295 {
296 lOutNames.realloc ( nDestinationCounter+1 );
297 lOutValues.realloc ( nDestinationCounter+1 );
298 }
299
300 lOutNames [nDestinationCounter] = lInNames [nSourceCounter];
301 lOutValues[nDestinationCounter] = lInValues[nSourceCounter];
302 ++nDestinationCounter;
303 }
304 }
305 }
306
GetReadOnlyStates(const css::uno::Sequence<OUString> & rNames)307 Sequence< sal_Bool > ConfigItem::GetReadOnlyStates(const css::uno::Sequence< OUString >& rNames)
308 {
309 sal_Int32 i;
310
311 // size of return list is fix!
312 // Every item must match to length of incoming name list.
313 sal_Int32 nCount = rNames.getLength();
314 Sequence< sal_Bool > lStates(nCount);
315
316 // We must be sure to return a valid information every time!
317 // Set default to non readonly... similar to the configuration handling of this property.
318 std::fill(lStates.begin(), lStates.end(), false);
319
320 // no access - no information...
321 Reference< XHierarchicalNameAccess > xHierarchyAccess = GetTree();
322 if (!xHierarchyAccess.is())
323 return lStates;
324
325 for (i=0; i<nCount; ++i)
326 {
327 try
328 {
329 OUString sName = rNames[i];
330 OUString sPath;
331 OUString sProperty;
332
333 (void)::utl::splitLastFromConfigurationPath(sName,sPath,sProperty);
334 if (sPath.isEmpty() && sProperty.isEmpty())
335 {
336 OSL_FAIL("ConfigItem::IsReadonly() split failed");
337 continue;
338 }
339
340 Reference< XInterface > xNode;
341 Reference< XPropertySet > xSet;
342 Reference< XPropertySetInfo > xInfo;
343 if (!sPath.isEmpty())
344 {
345 Any aNode = xHierarchyAccess->getByHierarchicalName(sPath);
346 if (!(aNode >>= xNode) || !xNode.is())
347 {
348 OSL_FAIL("ConfigItem::IsReadonly() no set available");
349 continue;
350 }
351 }
352 else
353 {
354 xNode = xHierarchyAccess;
355 }
356
357 xSet.set(xNode, UNO_QUERY);
358 if (xSet.is())
359 {
360 xInfo = xSet->getPropertySetInfo();
361 OSL_ENSURE(xInfo.is(), "ConfigItem::IsReadonly() getPropertySetInfo failed ...");
362 }
363 else
364 {
365 xInfo.set(xNode, UNO_QUERY);
366 OSL_ENSURE(xInfo.is(), "ConfigItem::IsReadonly() UNO_QUERY failed ...");
367 }
368
369 if (!xInfo.is())
370 {
371 OSL_FAIL("ConfigItem::IsReadonly() no prop info available");
372 continue;
373 }
374
375 Property aProp = xInfo->getPropertyByName(sProperty);
376 lStates[i] = ((aProp.Attributes & PropertyAttribute::READONLY) == PropertyAttribute::READONLY);
377 }
378 catch (const Exception&)
379 {
380 }
381 }
382
383 return lStates;
384 }
385
GetProperties(const Sequence<OUString> & rNames)386 Sequence< Any > ConfigItem::GetProperties(const Sequence< OUString >& rNames)
387 {
388 Sequence< Any > aRet(rNames.getLength());
389 const OUString* pNames = rNames.getConstArray();
390 Any* pRet = aRet.getArray();
391 Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
392 if(xHierarchyAccess.is())
393 {
394 for(int i = 0; i < rNames.getLength(); i++)
395 {
396 try
397 {
398 pRet[i] = xHierarchyAccess->getByHierarchicalName(pNames[i]);
399 }
400 catch (const Exception&)
401 {
402 TOOLS_WARN_EXCEPTION(
403 "unotools.config",
404 "ignoring XHierarchicalNameAccess to /org.openoffice."
405 << sSubTree << "/" << pNames[i]);
406 }
407 }
408
409 // In special mode "ALL_LOCALES" we must convert localized values to Sequence< PropertyValue >.
410 if((m_nMode & ConfigItemMode::AllLocales ) == ConfigItemMode::AllLocales)
411 {
412 Sequence< Any > lValues;
413 impl_packLocalizedProperties( rNames, aRet, lValues );
414 aRet = lValues;
415 }
416 }
417 return aRet;
418 }
419
PutProperties(const Sequence<OUString> & rNames,const Sequence<Any> & rValues)420 bool ConfigItem::PutProperties( const Sequence< OUString >& rNames,
421 const Sequence< Any>& rValues)
422 {
423 ValueCounter_Impl aCounter(m_nInValueChange);
424 Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
425 Reference<XNameReplace> xTopNodeReplace(xHierarchyAccess, UNO_QUERY);
426 bool bRet = xHierarchyAccess.is() && xTopNodeReplace.is();
427 if(bRet)
428 {
429 Sequence< OUString > lNames;
430 Sequence< Any > lValues;
431 const OUString* pNames = nullptr;
432 const Any* pValues = nullptr;
433 sal_Int32 nNameCount;
434 if(( m_nMode & ConfigItemMode::AllLocales ) == ConfigItemMode::AllLocales )
435 {
436 // If ConfigItem works in "ALL_LOCALES"-mode ... we must support a Sequence< PropertyValue >
437 // as value of a localized configuration entry!
438 // How we can do that?
439 // We must split all PropertyValues to "Sequence< OUString >" AND "Sequence< Any >"!
440 impl_unpackLocalizedProperties( rNames, rValues, lNames, lValues );
441 pNames = lNames.getConstArray ();
442 pValues = lValues.getConstArray ();
443 nNameCount = lNames.getLength ();
444 }
445 else
446 {
447 // This is the normal mode ...
448 // Use given input lists directly.
449 pNames = rNames.getConstArray ();
450 pValues = rValues.getConstArray ();
451 nNameCount = rNames.getLength ();
452 }
453 for(int i = 0; i < nNameCount; i++)
454 {
455 try
456 {
457 OUString sNode, sProperty;
458 if (splitLastFromConfigurationPath(pNames[i],sNode, sProperty))
459 {
460 Any aNode = xHierarchyAccess->getByHierarchicalName(sNode);
461
462 Reference<XNameAccess> xNodeAcc;
463 aNode >>= xNodeAcc;
464 Reference<XNameReplace> xNodeReplace(xNodeAcc, UNO_QUERY);
465 Reference<XNameContainer> xNodeCont (xNodeAcc, UNO_QUERY);
466
467 bool bExist = (xNodeAcc.is() && xNodeAcc->hasByName(sProperty));
468 if (bExist && xNodeReplace.is())
469 xNodeReplace->replaceByName(sProperty, pValues[i]);
470 else
471 if (!bExist && xNodeCont.is())
472 xNodeCont->insertByName(sProperty, pValues[i]);
473 else
474 bRet = false;
475 }
476 else //direct value
477 {
478 xTopNodeReplace->replaceByName(sProperty, pValues[i]);
479 }
480 }
481 catch (css::uno::Exception &)
482 {
483 TOOLS_WARN_EXCEPTION("unotools.config", "Exception from PutProperties");
484 }
485 }
486 try
487 {
488 Reference<XChangesBatch> xBatch(xHierarchyAccess, UNO_QUERY);
489 xBatch->commitChanges();
490 }
491 catch (css::uno::Exception &)
492 {
493 TOOLS_WARN_EXCEPTION("unotools.config", "Exception from commitChanges");
494 }
495 }
496
497 return bRet;
498 }
499
DisableNotification()500 void ConfigItem::DisableNotification()
501 {
502 OSL_ENSURE( xChangeLstnr.is(), "ConfigItem::DisableNotification: notifications not enabled currently!" );
503 RemoveChangesListener();
504 }
505
EnableNotification(const Sequence<OUString> & rNames,bool bEnableInternalNotification)506 bool ConfigItem::EnableNotification(const Sequence< OUString >& rNames,
507 bool bEnableInternalNotification )
508 {
509 OSL_ENSURE(!(m_nMode & ConfigItemMode::ReleaseTree), "notification in ConfigItemMode::ReleaseTree mode not possible");
510 m_bEnableInternalNotification = bEnableInternalNotification;
511 Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
512 Reference<XChangesNotifier> xChgNot(xHierarchyAccess, UNO_QUERY);
513 if(!xChgNot.is())
514 return false;
515
516 OSL_ENSURE(!xChangeLstnr.is(), "EnableNotification already called");
517 if(xChangeLstnr.is())
518 xChgNot->removeChangesListener( xChangeLstnr );
519 bool bRet = true;
520
521 try
522 {
523 xChangeLstnr = new ConfigChangeListener_Impl(*this, rNames);
524 xChgNot->addChangesListener( xChangeLstnr );
525 }
526 catch (const RuntimeException&)
527 {
528 bRet = false;
529 }
530 return bRet;
531 }
532
RemoveChangesListener()533 void ConfigItem::RemoveChangesListener()
534 {
535 Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
536 if(xHierarchyAccess.is())
537 {
538 Reference<XChangesNotifier> xChgNot(xHierarchyAccess, UNO_QUERY);
539 if(xChgNot.is() && xChangeLstnr.is())
540 {
541 try
542 {
543 xChgNot->removeChangesListener( xChangeLstnr );
544 xChangeLstnr = nullptr;
545 }
546 catch (const Exception&)
547 {
548 }
549 }
550 }
551 }
552
lcl_normalizeLocalNames(Sequence<OUString> & _rNames,ConfigNameFormat _eFormat,Reference<XInterface> const & _xParentNode)553 static void lcl_normalizeLocalNames(Sequence< OUString >& _rNames, ConfigNameFormat _eFormat, Reference<XInterface> const& _xParentNode)
554 {
555 switch (_eFormat)
556 {
557 case ConfigNameFormat::LocalNode:
558 // unaltered - this is our input format
559 break;
560
561 case ConfigNameFormat::LocalPath:
562 {
563 Reference<XTemplateContainer> xTypeContainer(_xParentNode, UNO_QUERY);
564 if (xTypeContainer.is())
565 {
566 OUString sTypeName = xTypeContainer->getElementTemplateName();
567 sTypeName = sTypeName.copy(sTypeName.lastIndexOf('/')+1);
568
569 std::transform(_rNames.begin(), _rNames.end(), _rNames.begin(),
570 [&sTypeName](const OUString& rName) -> OUString { return wrapConfigurationElementName(rName,sTypeName); });
571 }
572 else
573 {
574 Reference<XServiceInfo> xSVI(_xParentNode, UNO_QUERY);
575 if (xSVI.is() && xSVI->supportsService("com.sun.star.configuration.SetAccess"))
576 {
577 std::transform(_rNames.begin(), _rNames.end(), _rNames.begin(),
578 [](const OUString& rName) -> OUString { return wrapConfigurationElementName(rName); });
579 }
580 }
581 }
582 break;
583
584 }
585 }
586
GetNodeNames(const OUString & rNode)587 Sequence< OUString > ConfigItem::GetNodeNames(const OUString& rNode)
588 {
589 ConfigNameFormat const eDefaultFormat = ConfigNameFormat::LocalNode; // CONFIG_NAME_DEFAULT;
590
591 return GetNodeNames(rNode, eDefaultFormat);
592 }
593
GetNodeNames(const OUString & rNode,ConfigNameFormat eFormat)594 Sequence< OUString > ConfigItem::GetNodeNames(const OUString& rNode, ConfigNameFormat eFormat)
595 {
596 Sequence< OUString > aRet;
597 Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
598 if(xHierarchyAccess.is())
599 {
600 try
601 {
602 Reference<XNameAccess> xCont;
603 if(!rNode.isEmpty())
604 {
605 Any aNode = xHierarchyAccess->getByHierarchicalName(rNode);
606 aNode >>= xCont;
607 }
608 else
609 xCont.set(xHierarchyAccess, UNO_QUERY);
610 if(xCont.is())
611 {
612 aRet = xCont->getElementNames();
613 lcl_normalizeLocalNames(aRet,eFormat,xCont);
614 }
615
616 }
617 catch (css::uno::Exception &)
618 {
619 TOOLS_WARN_EXCEPTION("unotools.config", "Exception from GetNodeNames");
620 }
621 }
622 return aRet;
623 }
624
ClearNodeSet(const OUString & rNode)625 bool ConfigItem::ClearNodeSet(const OUString& rNode)
626 {
627 ValueCounter_Impl aCounter(m_nInValueChange);
628 bool bRet = false;
629 Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
630 if(xHierarchyAccess.is())
631 {
632 try
633 {
634 Reference<XNameContainer> xCont;
635 if(!rNode.isEmpty())
636 {
637 Any aNode = xHierarchyAccess->getByHierarchicalName(rNode);
638 aNode >>= xCont;
639 }
640 else
641 xCont.set(xHierarchyAccess, UNO_QUERY);
642 if(!xCont.is())
643 return false;
644 const Sequence< OUString > aNames = xCont->getElementNames();
645 Reference<XChangesBatch> xBatch(xHierarchyAccess, UNO_QUERY);
646 for(const OUString& rName : aNames)
647 {
648 try
649 {
650 xCont->removeByName(rName);
651 }
652 catch (css::uno::Exception &)
653 {
654 TOOLS_WARN_EXCEPTION("unotools.config", "Exception from removeByName");
655 }
656 }
657 xBatch->commitChanges();
658 bRet = true;
659 }
660 catch (css::uno::Exception &)
661 {
662 TOOLS_WARN_EXCEPTION("unotools.config", "Exception from ClearNodeSet");
663 }
664 }
665 return bRet;
666 }
667
ClearNodeElements(const OUString & rNode,Sequence<OUString> const & rElements)668 bool ConfigItem::ClearNodeElements(const OUString& rNode, Sequence< OUString > const & rElements)
669 {
670 ValueCounter_Impl aCounter(m_nInValueChange);
671 bool bRet = false;
672 Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
673 if(xHierarchyAccess.is())
674 {
675 try
676 {
677 Reference<XNameContainer> xCont;
678 if(!rNode.isEmpty())
679 {
680 Any aNode = xHierarchyAccess->getByHierarchicalName(rNode);
681 aNode >>= xCont;
682 }
683 else
684 xCont.set(xHierarchyAccess, UNO_QUERY);
685 if(!xCont.is())
686 return false;
687 try
688 {
689 for(const OUString& rElement : rElements)
690 {
691 xCont->removeByName(rElement);
692 }
693 Reference<XChangesBatch> xBatch(xHierarchyAccess, UNO_QUERY);
694 xBatch->commitChanges();
695 }
696 catch (css::uno::Exception &)
697 {
698 TOOLS_WARN_EXCEPTION("unotools.config", "Exception from commitChanges()");
699 }
700 bRet = true;
701 }
702 catch (css::uno::Exception &)
703 {
704 TOOLS_WARN_EXCEPTION("unotools.config", "Exception from GetNodeNames()");
705 }
706 }
707 return bRet;
708 }
709
lcl_extractSetPropertyName(const OUString & rInPath,const OUString & rPrefix)710 static OUString lcl_extractSetPropertyName( const OUString& rInPath, const OUString& rPrefix )
711 {
712 OUString const sSubPath = dropPrefixFromConfigurationPath( rInPath, rPrefix);
713 return extractFirstFromConfigurationPath( sSubPath );
714 }
715
716 static
lcl_extractSetPropertyNames(const Sequence<PropertyValue> & rValues,const OUString & rPrefix)717 Sequence< OUString > lcl_extractSetPropertyNames( const Sequence< PropertyValue >& rValues, const OUString& rPrefix )
718 {
719 Sequence< OUString > aSubNodeNames(rValues.getLength());
720 OUString* pSubNodeNames = aSubNodeNames.getArray();
721
722 OUString sLastSubNode;
723 sal_Int32 nSubIndex = 0;
724
725 for(const PropertyValue& rProperty : rValues)
726 {
727 OUString const sSubPath = dropPrefixFromConfigurationPath( rProperty.Name, rPrefix);
728 OUString const sSubNode = extractFirstFromConfigurationPath( sSubPath );
729
730 if(sLastSubNode != sSubNode)
731 {
732 pSubNodeNames[nSubIndex++] = sSubNode;
733 }
734
735 sLastSubNode = sSubNode;
736 }
737 aSubNodeNames.realloc(nSubIndex);
738
739 return aSubNodeNames;
740 }
741
742 // Add or change properties
SetSetProperties(const OUString & rNode,const Sequence<PropertyValue> & rValues)743 bool ConfigItem::SetSetProperties(
744 const OUString& rNode, const Sequence< PropertyValue >& rValues)
745 {
746 ValueCounter_Impl aCounter(m_nInValueChange);
747 bool bRet = true;
748 Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
749 if(xHierarchyAccess.is())
750 {
751 Reference<XChangesBatch> xBatch(xHierarchyAccess, UNO_QUERY);
752 try
753 {
754 Reference<XNameContainer> xCont;
755 if(!rNode.isEmpty())
756 {
757 Any aNode = xHierarchyAccess->getByHierarchicalName(rNode);
758 aNode >>= xCont;
759 }
760 else
761 xCont.set(xHierarchyAccess, UNO_QUERY);
762 if(!xCont.is())
763 return false;
764
765 Reference<XSingleServiceFactory> xFac(xCont, UNO_QUERY);
766
767 if(xFac.is())
768 {
769 const Sequence< OUString > aSubNodeNames = lcl_extractSetPropertyNames(rValues, rNode);
770
771 for(const auto& rSubNodeName : aSubNodeNames)
772 {
773 if(!xCont->hasByName(rSubNodeName))
774 {
775 Reference<XInterface> xInst = xFac->createInstance();
776 Any aVal; aVal <<= xInst;
777 xCont->insertByName(rSubNodeName, aVal);
778 }
779 //set values
780 }
781 try
782 {
783 xBatch->commitChanges();
784 }
785 catch (css::uno::Exception &)
786 {
787 TOOLS_WARN_EXCEPTION("unotools.config", "Exception from commitChanges()");
788 }
789
790 const PropertyValue* pProperties = rValues.getConstArray();
791
792 Sequence< OUString > aSetNames(rValues.getLength());
793 OUString* pSetNames = aSetNames.getArray();
794
795 Sequence< Any> aSetValues(rValues.getLength());
796 Any* pSetValues = aSetValues.getArray();
797
798 bool bEmptyNode = rNode.isEmpty();
799 for(sal_Int32 k = 0; k < rValues.getLength(); k++)
800 {
801 pSetNames[k] = pProperties[k].Name.copy( bEmptyNode ? 1 : 0);
802 pSetValues[k] = pProperties[k].Value;
803 }
804 bRet = PutProperties(aSetNames, aSetValues);
805 }
806 else
807 {
808 //if no factory is available then the node contains basic data elements
809 for(const PropertyValue& rValue : rValues)
810 {
811 try
812 {
813 OUString sSubNode = lcl_extractSetPropertyName( rValue.Name, rNode );
814
815 if(xCont->hasByName(sSubNode))
816 xCont->replaceByName(sSubNode, rValue.Value);
817 else
818 xCont->insertByName(sSubNode, rValue.Value);
819
820 OSL_ENSURE( xHierarchyAccess->hasByHierarchicalName(rValue.Name),
821 "Invalid config path" );
822 }
823 catch (css::uno::Exception &)
824 {
825 TOOLS_WARN_EXCEPTION("unotools.config", "Exception from insert/replaceByName()");
826 }
827 }
828 xBatch->commitChanges();
829 }
830 }
831 catch (const Exception&)
832 {
833 TOOLS_WARN_EXCEPTION("unotools.config", "Exception from SetSetProperties");
834 bRet = false;
835 }
836 }
837 return bRet;
838 }
839
ReplaceSetProperties(const OUString & rNode,const Sequence<PropertyValue> & rValues)840 bool ConfigItem::ReplaceSetProperties(
841 const OUString& rNode, const Sequence< PropertyValue >& rValues)
842 {
843 ValueCounter_Impl aCounter(m_nInValueChange);
844 bool bRet = true;
845 Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
846 if(xHierarchyAccess.is())
847 {
848 Reference<XChangesBatch> xBatch(xHierarchyAccess, UNO_QUERY);
849 try
850 {
851 Reference<XNameContainer> xCont;
852 if(!rNode.isEmpty())
853 {
854 Any aNode = xHierarchyAccess->getByHierarchicalName(rNode);
855 aNode >>= xCont;
856 }
857 else
858 xCont.set(xHierarchyAccess, UNO_QUERY);
859 if(!xCont.is())
860 return false;
861
862 // JB: Change: now the same name handling for sets of simple values
863 const Sequence< OUString > aSubNodeNames = lcl_extractSetPropertyNames(rValues, rNode);
864
865 Reference<XSingleServiceFactory> xFac(xCont, UNO_QUERY);
866 const bool isSimpleValueSet = !xFac.is();
867
868 //remove unknown members first
869 {
870 const Sequence<OUString> aContainerSubNodes = xCont->getElementNames();
871
872 for(const OUString& rContainerSubNode : aContainerSubNodes)
873 {
874 bool bFound = comphelper::findValue(aSubNodeNames, rContainerSubNode) != -1;
875 if(!bFound)
876 try
877 {
878 xCont->removeByName(rContainerSubNode);
879 }
880 catch (const Exception&)
881 {
882 if (isSimpleValueSet)
883 {
884 try
885 {
886 // #i37322#: fallback action: replace with <void/>
887 xCont->replaceByName(rContainerSubNode, Any());
888 // fallback successful: continue looping
889 continue;
890 }
891 catch (Exception &)
892 {} // propagate original exception, if fallback fails
893 }
894 throw;
895 }
896 }
897 try { xBatch->commitChanges(); }
898 catch (css::uno::Exception &)
899 {
900 TOOLS_WARN_EXCEPTION("unotools.config", "Exception from commitChanges");
901 }
902 }
903
904 if(xFac.is()) // !isSimpleValueSet
905 {
906 for(const OUString& rSubNodeName : aSubNodeNames)
907 {
908 if(!xCont->hasByName(rSubNodeName))
909 {
910 //create if not available
911 Reference<XInterface> xInst = xFac->createInstance();
912 Any aVal; aVal <<= xInst;
913 xCont->insertByName(rSubNodeName, aVal);
914 }
915 }
916 try { xBatch->commitChanges(); }
917 catch (css::uno::Exception &)
918 {
919 TOOLS_WARN_EXCEPTION("unotools.config", "Exception from commitChanges");
920 }
921
922 const PropertyValue* pProperties = rValues.getConstArray();
923
924 Sequence< OUString > aSetNames(rValues.getLength());
925 OUString* pSetNames = aSetNames.getArray();
926
927 Sequence< Any> aSetValues(rValues.getLength());
928 Any* pSetValues = aSetValues.getArray();
929
930 bool bEmptyNode = rNode.isEmpty();
931 for(sal_Int32 k = 0; k < rValues.getLength(); k++)
932 {
933 pSetNames[k] = pProperties[k].Name.copy( bEmptyNode ? 1 : 0);
934 pSetValues[k] = pProperties[k].Value;
935 }
936 bRet = PutProperties(aSetNames, aSetValues);
937 }
938 else
939 {
940 //if no factory is available then the node contains basic data elements
941 for(const PropertyValue& rValue : rValues)
942 {
943 try
944 {
945 OUString sSubNode = lcl_extractSetPropertyName( rValue.Name, rNode );
946
947 if(xCont->hasByName(sSubNode))
948 xCont->replaceByName(sSubNode, rValue.Value);
949 else
950 xCont->insertByName(sSubNode, rValue.Value);
951 }
952 catch (css::uno::Exception &)
953 {
954 TOOLS_WARN_EXCEPTION("unotools.config", "Exception from insert/replaceByName");
955 }
956 }
957 xBatch->commitChanges();
958 }
959 }
960 catch (const Exception& )
961 {
962 TOOLS_WARN_EXCEPTION("unotools.config", "Exception from ReplaceSetProperties");
963 bRet = false;
964 }
965 }
966 return bRet;
967 }
968
AddNode(const OUString & rNode,const OUString & rNewNode)969 bool ConfigItem::AddNode(const OUString& rNode, const OUString& rNewNode)
970 {
971 ValueCounter_Impl aCounter(m_nInValueChange);
972 bool bRet = true;
973 Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
974 if(xHierarchyAccess.is())
975 {
976 Reference<XChangesBatch> xBatch(xHierarchyAccess, UNO_QUERY);
977 try
978 {
979 Reference<XNameContainer> xCont;
980 if(!rNode.isEmpty())
981 {
982 Any aNode = xHierarchyAccess->getByHierarchicalName(rNode);
983 aNode >>= xCont;
984 }
985 else
986 xCont.set(xHierarchyAccess, UNO_QUERY);
987 if(!xCont.is())
988 return false;
989
990 Reference<XSingleServiceFactory> xFac(xCont, UNO_QUERY);
991
992 if(xFac.is())
993 {
994 if(!xCont->hasByName(rNewNode))
995 {
996 Reference<XInterface> xInst = xFac->createInstance();
997 Any aVal; aVal <<= xInst;
998 xCont->insertByName(rNewNode, aVal);
999 }
1000 try
1001 {
1002 xBatch->commitChanges();
1003 }
1004 catch (css::uno::Exception &)
1005 {
1006 TOOLS_WARN_EXCEPTION("unotools.config", "Exception from commitChanges");
1007 }
1008 }
1009 else
1010 {
1011 //if no factory is available then the node contains basic data elements
1012 try
1013 {
1014 if(!xCont->hasByName(rNewNode))
1015 xCont->insertByName(rNewNode, Any());
1016 }
1017 catch (css::uno::Exception &)
1018 {
1019 TOOLS_WARN_EXCEPTION("unotools.config", "Exception from AddNode");
1020 }
1021 }
1022 xBatch->commitChanges();
1023 }
1024 catch (const Exception&)
1025 {
1026 DBG_UNHANDLED_EXCEPTION("unotools.config");
1027 bRet = false;
1028 }
1029 }
1030 return bRet;
1031 }
1032
1033
SetModified()1034 void ConfigItem::SetModified()
1035 {
1036 m_bIsModified = true;
1037 }
1038
ClearModified()1039 void ConfigItem::ClearModified()
1040 {
1041 m_bIsModified = false;
1042 }
1043
GetTree()1044 Reference< XHierarchicalNameAccess> ConfigItem::GetTree()
1045 {
1046 Reference< XHierarchicalNameAccess> xRet;
1047 if (utl::ConfigManager::IsFuzzing())
1048 return xRet;
1049 if(!m_xHierarchyAccess.is())
1050 xRet = ConfigManager::acquireTree(*this);
1051 else
1052 xRet = m_xHierarchyAccess;
1053 return xRet;
1054 }
1055
Commit()1056 void ConfigItem::Commit()
1057 {
1058 ImplCommit();
1059 ClearModified();
1060 }
1061
1062 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1063