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