Updated core
[LibreOffice.git] / unotools / source / config / configitem.cxx
blob7e89095e5e002fe4e4467b0da3aed1e410156c2b
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "unotools/configitem.hxx"
21 #include "unotools/configmgr.hxx"
22 #include "unotools/configpaths.hxx"
23 #include <comphelper/processfactory.hxx>
24 #include <com/sun/star/beans/XMultiPropertySet.hpp>
25 #include <com/sun/star/beans/XPropertySet.hpp>
26 #include <com/sun/star/util/XChangesListener.hpp>
27 #include <com/sun/star/util/XChangesNotifier.hpp>
28 #include <com/sun/star/container/XHierarchicalNameAccess.hpp>
29 #include <com/sun/star/container/XHierarchicalName.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/awt/XRequestCallback.hpp>
35 #include <com/sun/star/beans/PropertyValue.hpp>
36 #include <com/sun/star/beans/PropertyAttribute.hpp>
37 #include <com/sun/star/util/XStringEscape.hpp>
38 #include <com/sun/star/util/XChangesBatch.hpp>
39 #include <osl/diagnose.h>
40 #include <tools/solarmutex.hxx>
41 #include <rtl/ustrbuf.hxx>
43 using namespace utl;
44 using namespace com::sun::star::uno;
45 using namespace com::sun::star::util;
46 using namespace com::sun::star::lang;
47 using namespace com::sun::star::beans;
48 using namespace com::sun::star::container;
49 using namespace com::sun::star::configuration;
51 #include <cppuhelper/implbase1.hxx> // helper for implementations
53 #ifdef DBG_UTIL
54 static inline void lcl_CFG_DBG_EXCEPTION(const sal_Char* cText, const Exception& rEx)
56 OString sMsg(cText);
57 sMsg += OString(rEx.Message.getStr(), rEx.Message.getLength(), RTL_TEXTENCODING_ASCII_US);
58 OSL_FAIL(sMsg.getStr());
60 #define CATCH_INFO(a) \
61 catch(const Exception& rEx) \
62 { \
63 lcl_CFG_DBG_EXCEPTION(a, rEx);\
65 #else
66 #define CATCH_INFO(a) catch(const Exception&){}
67 #endif
70 The ConfigChangeListener_Impl receives notifications from the configuration about changes that
71 have happened. It forwards this notification to the ConfigItem it knows a pParent by calling its
72 "CallNotify" method. As ConfigItems are most probably not thread safe, the SolarMutex is acquired
73 before doing so.
76 namespace utl{
77 class ConfigChangeListener_Impl : public cppu::WeakImplHelper1
79 com::sun::star::util::XChangesListener
82 public:
83 ConfigItem* pParent;
84 const Sequence< OUString > aPropertyNames;
85 ConfigChangeListener_Impl(ConfigItem& rItem, const Sequence< OUString >& rNames);
86 ~ConfigChangeListener_Impl();
88 //XChangesListener
89 virtual void SAL_CALL changesOccurred( const ChangesEvent& Event ) throw(RuntimeException);
91 //XEventListener
92 virtual void SAL_CALL disposing( const EventObject& Source ) throw(RuntimeException);
95 struct ConfigItem_Impl
97 utl::ConfigManager* pManager;
98 sal_Int16 nMode;
99 sal_Bool bIsModified;
100 sal_Bool bEnableInternalNotification;
102 sal_Int16 nInValueChange;
103 ConfigItem_Impl() :
104 pManager(0),
105 nMode(0),
106 bIsModified(sal_False),
107 bEnableInternalNotification(sal_False),
108 nInValueChange(0)
113 class ValueCounter_Impl
115 sal_Int16& rCnt;
116 public:
117 ValueCounter_Impl(sal_Int16& rCounter):
118 rCnt(rCounter)
119 {rCnt++;}
120 ~ValueCounter_Impl()
122 OSL_ENSURE(rCnt>0, "RefCount < 0 ??");
123 rCnt--;
127 namespace
129 // helper to achieve exception - safe handling of an Item under construction
130 template <class TYP>
131 class AutoDeleter // : Noncopyable
133 TYP* m_pItem;
134 public:
135 AutoDeleter(TYP * pItem)
136 : m_pItem(pItem)
140 ~AutoDeleter()
142 delete m_pItem;
145 void keep() { m_pItem = 0; }
149 ConfigChangeListener_Impl::ConfigChangeListener_Impl(
150 ConfigItem& rItem, const Sequence< OUString >& rNames) :
151 pParent(&rItem),
152 aPropertyNames(rNames)
156 ConfigChangeListener_Impl::~ConfigChangeListener_Impl()
160 static sal_Bool lcl_Find(
161 const OUString& rTemp,
162 const OUString* pCheckPropertyNames,
163 sal_Int32 nLength)
165 //return true if the path is completely correct or if it is longer
166 //i.e ...Print/Content/Graphic and .../Print
167 for(sal_Int32 nIndex = 0; nIndex < nLength; nIndex++)
168 if( isPrefixOfConfigurationPath(rTemp, pCheckPropertyNames[nIndex]) )
169 return sal_True;
170 return sal_False;
172 //-----------------------------------------------------------------------------
173 void ConfigChangeListener_Impl::changesOccurred( const ChangesEvent& rEvent ) throw(RuntimeException)
175 const ElementChange* pElementChanges = rEvent.Changes.getConstArray();
177 Sequence<OUString> aChangedNames(rEvent.Changes.getLength());
178 OUString* pNames = aChangedNames.getArray();
180 const OUString* pCheckPropertyNames = aPropertyNames.getConstArray();
182 sal_Int32 nNotify = 0;
183 for(int i = 0; i < aChangedNames.getLength(); i++)
185 OUString sTemp;
186 pElementChanges[i].Accessor >>= sTemp;
187 if(lcl_Find(sTemp, pCheckPropertyNames, aPropertyNames.getLength()))
188 pNames[nNotify++] = sTemp;
190 if( nNotify )
192 if ( ::tools::SolarMutex::Acquire() )
194 aChangedNames.realloc(nNotify);
195 pParent->CallNotify(aChangedNames);
196 ::tools::SolarMutex::Release();
202 void ConfigChangeListener_Impl::disposing( const EventObject& /*rSource*/ ) throw(RuntimeException)
204 pParent->RemoveChangesListener();
207 ConfigItem::ConfigItem(const OUString &rSubTree, sal_Int16 nSetMode ) :
208 sSubTree(rSubTree),
209 pImpl(new ConfigItem_Impl)
211 AutoDeleter<ConfigItem_Impl> aNewImpl(pImpl);
213 pImpl->pManager = &ConfigManager::getConfigManager();
214 pImpl->nMode = nSetMode;
215 if(0 != (nSetMode&CONFIG_MODE_RELEASE_TREE))
216 pImpl->pManager->addConfigItem(*this);
217 else
218 m_xHierarchyAccess = pImpl->pManager->addConfigItem(*this);
220 aNewImpl.keep();
223 sal_Bool ConfigItem::IsValidConfigMgr() const
225 return pImpl->pManager != 0;
228 ConfigItem::~ConfigItem()
230 if(pImpl->pManager)
232 RemoveChangesListener();
233 pImpl->pManager->removeConfigItem(*this);
235 delete pImpl;
238 void ConfigItem::CallNotify( const com::sun::star::uno::Sequence<OUString>& rPropertyNames )
240 // the call is forwarded to the virtual Notify() method
241 // it is pure virtual, so all classes deriving from ConfigItem have to decide how they
242 // want to notify listeners
243 if(!IsInValueChange() || pImpl->bEnableInternalNotification)
244 Notify(rPropertyNames);
247 void ConfigItem::impl_packLocalizedProperties( const Sequence< OUString >& lInNames ,
248 const Sequence< Any >& lInValues ,
249 Sequence< Any >& lOutValues )
251 // Safe impossible cases.
252 // This method should be called for special ConfigItem-mode only!
253 OSL_ENSURE( ((pImpl->nMode & CONFIG_MODE_ALL_LOCALES ) == CONFIG_MODE_ALL_LOCALES), "ConfigItem::impl_packLocalizedProperties()\nWrong call of this method detected!\n" );
255 sal_Int32 nSourceCounter ; // used to step during input lists
256 sal_Int32 nSourceSize ; // marks end of loop over input lists
257 sal_Int32 nDestinationCounter ; // actual position in output lists
258 sal_Int32 nPropertyCounter ; // counter of inner loop for Sequence< PropertyValue >
259 sal_Int32 nPropertiesSize ; // marks end of inner loop
260 Sequence< OUString > lPropertyNames ; // list of all locales for localized entry
261 Sequence< PropertyValue > lProperties ; // localized values of an configuration entry packed for return
262 Reference< XInterface > xLocalizedNode ; // if cfg entry is localized ... lInValues contains an XInterface!
264 // Optimise follow algorithm ... A LITTLE BIT :-)
265 // There exist two different possibilities:
266 // i ) There exist no localized entries ... => size of lOutValues will be the same like lInNames/lInValues!
267 // ii) There exist some (mostly one or two) localized entries ... => size of lOutValues will be the same like lInNames/lInValues!
268 // ... Why? If a localized value exist - the any is filled with an XInterface object (is a SetNode-service).
269 // We read all his child nodes and pack it into Sequence< PropertyValue >.
270 // The result list we pack into the return any. We never change size of lists!
271 nSourceSize = lInNames.getLength();
272 lOutValues.realloc( nSourceSize );
274 // Algorithm:
275 // Copy all names and values from in to out lists.
276 // Look for special localized entries ... You can detect it as "XInterface" packed into an Any.
277 // Use this XInterface-object to read all localized values and pack it into Sequence< PropertValue >.
278 // Add this list to out lists then.
280 nDestinationCounter = 0;
281 for( nSourceCounter=0; nSourceCounter<nSourceSize; ++nSourceCounter )
283 // If item a special localized one ... convert and pack it ...
284 if( lInValues[nSourceCounter].getValueTypeName() == "com.sun.star.uno.XInterface" )
286 lInValues[nSourceCounter] >>= xLocalizedNode;
287 Reference< XNameContainer > xSetAccess( xLocalizedNode, UNO_QUERY );
288 if( xSetAccess.is() == sal_True )
290 lPropertyNames = xSetAccess->getElementNames() ;
291 nPropertiesSize = lPropertyNames.getLength() ;
292 lProperties.realloc( nPropertiesSize ) ;
294 for( nPropertyCounter=0; nPropertyCounter<nPropertiesSize; ++nPropertyCounter )
296 #if OSL_DEBUG_LEVEL > 1
297 // Sometimes it's better to see what's going on :-)
298 OUString sPropName = lInNames[nSourceCounter];
299 OUString sLocaleName = lPropertyNames[nPropertyCounter];
300 #endif
301 lProperties[nPropertyCounter].Name = lPropertyNames[nPropertyCounter] ;
302 OUString sLocaleValue;
303 xSetAccess->getByName( lPropertyNames[nPropertyCounter] ) >>= sLocaleValue ;
304 lProperties[nPropertyCounter].Value <<= sLocaleValue;
307 lOutValues[nDestinationCounter] <<= lProperties;
310 // ... or copy normal items to return lists directly.
311 else
313 lOutValues[nDestinationCounter] = lInValues[nSourceCounter];
315 ++nDestinationCounter;
319 void ConfigItem::impl_unpackLocalizedProperties( const Sequence< OUString >& lInNames ,
320 const Sequence< Any >& lInValues ,
321 Sequence< OUString >& lOutNames ,
322 Sequence< Any >& lOutValues )
324 // Safe impossible cases.
325 // This method should be called for special ConfigItem-mode only!
326 OSL_ENSURE( ((pImpl->nMode & CONFIG_MODE_ALL_LOCALES ) == CONFIG_MODE_ALL_LOCALES), "ConfigItem::impl_unpackLocalizedProperties()\nWrong call of this method detected!\n" );
328 sal_Int32 nSourceCounter ; // used to step during input lists
329 sal_Int32 nSourceSize ; // marks end of loop over input lists
330 sal_Int32 nDestinationCounter ; // actual position in output lists
331 sal_Int32 nPropertyCounter ; // counter of inner loop for Sequence< PropertyValue >
332 sal_Int32 nPropertiesSize ; // marks end of inner loop
333 OUString sNodeName ; // base name of node ( e.g. "UIName/" ) ... expand to locale ( e.g. "UIName/de" )
334 Sequence< PropertyValue > lProperties ; // localized values of an configuration entry getted from lInValues-Any
336 // Optimise follow algorithm ... A LITTLE BIT :-)
337 // There exist two different possibilities:
338 // i ) There exist no localized entries ... => size of lOutNames/lOutValues will be the same like lInNames/lInValues!
339 // ii) There exist some (mostly one or two) localized entries ... => size of lOutNames/lOutValues will be some bytes greater then lInNames/lInValues.
340 // => I think we should make it fast for i). ii) is a special case and mustn't be SOOOO... fast.
341 // We should reserve same space for output list like input ones first.
342 // Follow algorithm looks for these borders and change it for ii) only!
343 // It will be faster then a "realloc()" call in every loop ...
344 nSourceSize = lInNames.getLength();
346 lOutNames.realloc ( nSourceSize );
347 lOutValues.realloc ( nSourceSize );
349 // Algorithm:
350 // Copy all names and values from const to return lists.
351 // Look for special localized entries ... You can detect it as Sequence< PropertyValue > packed into an Any.
352 // Split it ... insert PropertyValue.Name to lOutNames and PropertyValue.Value to lOutValues.
354 nDestinationCounter = 0;
355 for( nSourceCounter=0; nSourceCounter<nSourceSize; ++nSourceCounter )
357 // If item a special localized one ... split it and insert his parts to output lists ...
358 if( lInValues[nSourceCounter].getValueType() == ::getCppuType( (const Sequence< PropertyValue >*)NULL ) )
360 lInValues[nSourceCounter] >>= lProperties;
361 nPropertiesSize = lProperties.getLength();
363 sNodeName = lInNames[nSourceCounter] + "/";
365 if( (nDestinationCounter+nPropertiesSize) > lOutNames.getLength() )
367 lOutNames.realloc ( nDestinationCounter+nPropertiesSize );
368 lOutValues.realloc ( nDestinationCounter+nPropertiesSize );
371 for( nPropertyCounter=0; nPropertyCounter<nPropertiesSize; ++nPropertyCounter )
373 lOutNames [nDestinationCounter] = sNodeName + lProperties[nPropertyCounter].Name ;
374 lOutValues[nDestinationCounter] = lProperties[nPropertyCounter].Value ;
375 ++nDestinationCounter;
378 // ... or copy normal items to return lists directly.
379 else
381 if( (nDestinationCounter+1) > lOutNames.getLength() )
383 lOutNames.realloc ( nDestinationCounter+1 );
384 lOutValues.realloc ( nDestinationCounter+1 );
387 lOutNames [nDestinationCounter] = lInNames [nSourceCounter];
388 lOutValues[nDestinationCounter] = lInValues[nSourceCounter];
389 ++nDestinationCounter;
394 Sequence< sal_Bool > ConfigItem::GetReadOnlyStates(const com::sun::star::uno::Sequence< OUString >& rNames)
396 sal_Int32 i;
398 // size of return list is fix!
399 // Every item must match to length of incoming name list.
400 sal_Int32 nCount = rNames.getLength();
401 Sequence< sal_Bool > lStates(nCount);
403 // We must be shure to return a valid information everytime!
404 // Set default to non readonly ... similar to the configuration handling of this property.
405 for ( i=0; i<nCount; ++i)
406 lStates[i] = sal_False;
408 // no access - no information ...
409 Reference< XHierarchicalNameAccess > xHierarchyAccess = GetTree();
410 if (!xHierarchyAccess.is())
411 return lStates;
413 for (i=0; i<nCount; ++i)
417 OUString sName = rNames[i];
418 OUString sPath;
419 OUString sProperty;
421 ::utl::splitLastFromConfigurationPath(sName,sPath,sProperty);
422 if (sPath.isEmpty() && sProperty.isEmpty())
424 OSL_FAIL("ConfigItem::IsReadonly()\nsplitt failed\n");
425 continue;
428 Reference< XInterface > xNode;
429 Reference< XPropertySet > xSet ;
430 Reference< XPropertySetInfo > xInfo;
431 if (!sPath.isEmpty())
433 Any aNode = xHierarchyAccess->getByHierarchicalName(sPath);
434 if (!(aNode >>= xNode) || !xNode.is())
436 OSL_FAIL("ConfigItem::IsReadonly()\nno set available\n");
437 continue;
440 else
442 xNode = Reference< XInterface >(xHierarchyAccess, UNO_QUERY);
445 xSet = Reference< XPropertySet >(xNode, UNO_QUERY);
446 if (xSet.is())
448 xInfo = xSet->getPropertySetInfo();
449 OSL_ENSURE(xInfo.is(), "ConfigItem::IsReadonly()\ngetPropertySetInfo failed ...\n");
451 else
453 xInfo = Reference< XPropertySetInfo >(xNode, UNO_QUERY);
454 OSL_ENSURE(xInfo.is(), "ConfigItem::IsReadonly()\nUNO_QUERY failed ...\n");
457 if (!xInfo.is())
459 OSL_FAIL("ConfigItem::IsReadonly()\nno prop info available\n");
460 continue;
463 Property aProp = xInfo->getPropertyByName(sProperty);
464 lStates[i] = ((aProp.Attributes & PropertyAttribute::READONLY) == PropertyAttribute::READONLY);
466 catch (const Exception&)
471 return lStates;
474 Sequence< Any > ConfigItem::GetProperties(const Sequence< OUString >& rNames)
476 Sequence< Any > aRet(rNames.getLength());
477 const OUString* pNames = rNames.getConstArray();
478 Any* pRet = aRet.getArray();
479 Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
480 if(xHierarchyAccess.is())
482 for(int i = 0; i < rNames.getLength(); i++)
486 pRet[i] = xHierarchyAccess->getByHierarchicalName(pNames[i]);
488 catch (const Exception& rEx)
490 #if OSL_DEBUG_LEVEL > 0
491 OString sMsg("XHierarchicalNameAccess: ");
492 sMsg += OString(rEx.Message.getStr(),
493 rEx.Message.getLength(),
494 RTL_TEXTENCODING_ASCII_US);
495 sMsg += OString("\n/org.openoffice.");
496 sMsg += OString(sSubTree.getStr(),
497 sSubTree.getLength(),
498 RTL_TEXTENCODING_ASCII_US);
499 sMsg += OString("/");
500 sMsg += OString(pNames[i].getStr(),
501 pNames[i].getLength(),
502 RTL_TEXTENCODING_ASCII_US);
503 OSL_FAIL(sMsg.getStr());
504 #else
505 (void) rEx; // avoid warning
506 #endif
510 // In special mode "ALL_LOCALES" we must convert localized values to Sequence< PropertyValue >.
511 if((pImpl->nMode & CONFIG_MODE_ALL_LOCALES ) == CONFIG_MODE_ALL_LOCALES)
513 Sequence< Any > lValues;
514 impl_packLocalizedProperties( rNames, aRet, lValues );
515 aRet = lValues;
518 return aRet;
521 sal_Bool ConfigItem::PutProperties( const Sequence< OUString >& rNames,
522 const Sequence< Any>& rValues)
524 ValueCounter_Impl aCounter(pImpl->nInValueChange);
525 Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
526 Reference<XNameReplace> xTopNodeReplace(xHierarchyAccess, UNO_QUERY);
527 sal_Bool bRet = xHierarchyAccess.is() && xTopNodeReplace.is();
528 if(bRet)
530 Sequence< OUString > lNames ;
531 Sequence< Any > lValues ;
532 const OUString* pNames = NULL ;
533 const Any* pValues = NULL ;
534 sal_Int32 nNameCount ;
535 if(( pImpl->nMode & CONFIG_MODE_ALL_LOCALES ) == CONFIG_MODE_ALL_LOCALES )
537 // If ConfigItem works in "ALL_LOCALES"-mode ... we must support a Sequence< PropertyValue >
538 // as value of an localized configuration entry!
539 // How we can do that?
540 // We must split all PropertyValues to "Sequence< OUString >" AND "Sequence< Any >"!
541 impl_unpackLocalizedProperties( rNames, rValues, lNames, lValues );
542 pNames = lNames.getConstArray ();
543 pValues = lValues.getConstArray ();
544 nNameCount = lNames.getLength ();
546 else
548 // This is the normal mode ...
549 // Use given input lists directly.
550 pNames = rNames.getConstArray ();
551 pValues = rValues.getConstArray ();
552 nNameCount = rNames.getLength ();
554 for(int i = 0; i < nNameCount; i++)
558 OUString sNode, sProperty;
559 if (splitLastFromConfigurationPath(pNames[i],sNode, sProperty))
561 Any aNode = xHierarchyAccess->getByHierarchicalName(sNode);
563 Reference<XNameAccess> xNodeAcc;
564 aNode >>= xNodeAcc;
565 Reference<XNameReplace> xNodeReplace(xNodeAcc, UNO_QUERY);
566 Reference<XNameContainer> xNodeCont (xNodeAcc, UNO_QUERY);
568 sal_Bool bExist = (xNodeAcc.is() && xNodeAcc->hasByName(sProperty));
569 if (bExist && xNodeReplace.is())
570 xNodeReplace->replaceByName(sProperty, pValues[i]);
571 else
572 if (!bExist && xNodeCont.is())
573 xNodeCont->insertByName(sProperty, pValues[i]);
574 else
575 bRet = sal_False;
577 else //direct value
579 xTopNodeReplace->replaceByName(sProperty, pValues[i]);
582 CATCH_INFO("Exception from PutProperties: ");
586 Reference<XChangesBatch> xBatch(xHierarchyAccess, UNO_QUERY);
587 xBatch->commitChanges();
589 CATCH_INFO("Exception from commitChanges(): ")
592 return bRet;
595 void ConfigItem::DisableNotification()
597 OSL_ENSURE( xChangeLstnr.is(), "ConfigItem::DisableNotification: notifications not enabled currently!" );
598 RemoveChangesListener();
601 sal_Bool ConfigItem::EnableNotification(const Sequence< OUString >& rNames,
602 sal_Bool bEnableInternalNotification )
605 OSL_ENSURE(0 == (pImpl->nMode&CONFIG_MODE_RELEASE_TREE), "notification in CONFIG_MODE_RELEASE_TREE mode not possible");
606 pImpl->bEnableInternalNotification = bEnableInternalNotification;
607 Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
608 Reference<XChangesNotifier> xChgNot(xHierarchyAccess, UNO_QUERY);
609 if(!xChgNot.is())
610 return sal_False;
612 OSL_ENSURE(!xChangeLstnr.is(), "EnableNotification already called");
613 if(xChangeLstnr.is())
614 xChgNot->removeChangesListener( xChangeLstnr );
615 sal_Bool bRet = sal_True;
619 xChangeLstnr = new ConfigChangeListener_Impl(*this, rNames);
620 xChgNot->addChangesListener( xChangeLstnr );
622 catch (const RuntimeException&)
624 bRet = sal_False;
626 return bRet;
629 void ConfigItem::RemoveChangesListener()
631 Reference<XChangesNotifier> xChgNot(m_xHierarchyAccess, UNO_QUERY);
632 if(xChgNot.is() && xChangeLstnr.is())
636 xChgNot->removeChangesListener( xChangeLstnr );
637 xChangeLstnr = 0;
639 catch (const Exception&)
645 static void lcl_normalizeLocalNames(Sequence< OUString >& _rNames, ConfigNameFormat _eFormat, Reference<XInterface> const& _xParentNode)
647 switch (_eFormat)
649 case CONFIG_NAME_LOCAL_NAME:
650 // unaltered - this is our input format
651 break;
653 case CONFIG_NAME_FULL_PATH:
655 Reference<XHierarchicalName> xFormatter(_xParentNode, UNO_QUERY);
656 if (xFormatter.is())
658 OUString * pNames = _rNames.getArray();
659 for(int i = 0; i<_rNames.getLength(); ++i)
663 pNames[i] = xFormatter->composeHierarchicalName(pNames[i]);
665 CATCH_INFO("Exception from composeHierarchicalName(): ")
667 break;
670 OSL_FAIL("Cannot create absolute paths: missing interface");
671 // make local paths instaed
673 case CONFIG_NAME_LOCAL_PATH:
675 Reference<XTemplateContainer> xTypeContainer(_xParentNode, UNO_QUERY);
676 if (xTypeContainer.is())
678 OUString sTypeName = xTypeContainer->getElementTemplateName();
679 sTypeName = sTypeName.copy(sTypeName.lastIndexOf('/')+1);
681 OUString * pNames = _rNames.getArray();
682 for(int i = 0; i<_rNames.getLength(); ++i)
684 pNames[i] = wrapConfigurationElementName(pNames[i],sTypeName);
687 else
689 Reference<XServiceInfo> xSVI(_xParentNode, UNO_QUERY);
690 if (xSVI.is() && xSVI->supportsService("com.sun.star.configuration.SetAccess"))
692 OUString * pNames = _rNames.getArray();
693 for(int i = 0; i<_rNames.getLength(); ++i)
695 pNames[i] = wrapConfigurationElementName(pNames[i]);
700 break;
702 case CONFIG_NAME_PLAINTEXT_NAME:
704 Reference<XStringEscape> xEscaper(_xParentNode, UNO_QUERY);
705 if (xEscaper.is())
707 OUString * pNames = _rNames.getArray();
708 for(int i = 0; i<_rNames.getLength(); ++i)
711 pNames[i] = xEscaper->unescapeString(pNames[i]);
713 CATCH_INFO("Exception from unescapeString(): ")
716 break;
721 Sequence< OUString > ConfigItem::GetNodeNames(const OUString& rNode)
723 ConfigNameFormat const eDefaultFormat = CONFIG_NAME_LOCAL_NAME; // CONFIG_NAME_DEFAULT;
725 return GetNodeNames(rNode, eDefaultFormat);
728 Sequence< OUString > ConfigItem::GetNodeNames(const OUString& rNode, ConfigNameFormat eFormat)
730 Sequence< OUString > aRet;
731 Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
732 if(xHierarchyAccess.is())
736 Reference<XNameAccess> xCont;
737 if(!rNode.isEmpty())
739 Any aNode = xHierarchyAccess->getByHierarchicalName(rNode);
740 aNode >>= xCont;
742 else
743 xCont = Reference<XNameAccess> (xHierarchyAccess, UNO_QUERY);
744 if(xCont.is())
746 aRet = xCont->getElementNames();
747 lcl_normalizeLocalNames(aRet,eFormat,xCont);
751 CATCH_INFO("Exception from GetNodeNames: ");
753 return aRet;
756 sal_Bool ConfigItem::ClearNodeSet(const OUString& rNode)
758 ValueCounter_Impl aCounter(pImpl->nInValueChange);
759 sal_Bool bRet = sal_False;
760 Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
761 if(xHierarchyAccess.is())
765 Reference<XNameContainer> xCont;
766 if(!rNode.isEmpty())
768 Any aNode = xHierarchyAccess->getByHierarchicalName(rNode);
769 aNode >>= xCont;
771 else
772 xCont = Reference<XNameContainer> (xHierarchyAccess, UNO_QUERY);
773 if(!xCont.is())
774 return sal_False;
775 Sequence< OUString > aNames = xCont->getElementNames();
776 const OUString* pNames = aNames.getConstArray();
777 Reference<XChangesBatch> xBatch(xHierarchyAccess, UNO_QUERY);
778 for(sal_Int32 i = 0; i < aNames.getLength(); i++)
782 xCont->removeByName(pNames[i]);
784 CATCH_INFO("Exception from removeByName(): ")
786 xBatch->commitChanges();
787 bRet = sal_True;
789 CATCH_INFO("Exception from ClearNodeSet")
791 return bRet;
794 sal_Bool ConfigItem::ClearNodeElements(const OUString& rNode, Sequence< OUString >& rElements)
796 ValueCounter_Impl aCounter(pImpl->nInValueChange);
797 sal_Bool bRet = sal_False;
798 Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
799 if(xHierarchyAccess.is())
801 const OUString* pElements = rElements.getConstArray();
804 Reference<XNameContainer> xCont;
805 if(!rNode.isEmpty())
807 Any aNode = xHierarchyAccess->getByHierarchicalName(rNode);
808 aNode >>= xCont;
810 else
811 xCont = Reference<XNameContainer> (xHierarchyAccess, UNO_QUERY);
812 if(!xCont.is())
813 return sal_False;
816 for(sal_Int32 nElement = 0; nElement < rElements.getLength(); nElement++)
818 xCont->removeByName(pElements[nElement]);
820 Reference<XChangesBatch> xBatch(xHierarchyAccess, UNO_QUERY);
821 xBatch->commitChanges();
823 CATCH_INFO("Exception from commitChanges(): ")
824 bRet = sal_True;
826 CATCH_INFO("Exception from GetNodeNames: ")
828 return bRet;
830 //----------------------------------------------------------------------------
831 static inline
832 OUString lcl_extractSetPropertyName( const OUString& rInPath, const OUString& rPrefix )
834 OUString const sSubPath = dropPrefixFromConfigurationPath( rInPath, rPrefix);
835 return extractFirstFromConfigurationPath( sSubPath );
837 //----------------------------------------------------------------------------
838 static
839 Sequence< OUString > lcl_extractSetPropertyNames( const Sequence< PropertyValue >& rValues, const OUString& rPrefix )
841 const PropertyValue* pProperties = rValues.getConstArray();
843 Sequence< OUString > aSubNodeNames(rValues.getLength());
844 OUString* pSubNodeNames = aSubNodeNames.getArray();
846 OUString sLastSubNode;
847 sal_Int32 nSubIndex = 0;
849 for(sal_Int32 i = 0; i < rValues.getLength(); i++)
851 OUString const sSubPath = dropPrefixFromConfigurationPath( pProperties[i].Name, rPrefix);
852 OUString const sSubNode = extractFirstFromConfigurationPath( sSubPath );
854 if(sLastSubNode != sSubNode)
856 pSubNodeNames[nSubIndex++] = sSubNode;
859 sLastSubNode = sSubNode;
861 aSubNodeNames.realloc(nSubIndex);
863 return aSubNodeNames;
866 // Add or change properties
867 sal_Bool ConfigItem::SetSetProperties(
868 const OUString& rNode, Sequence< PropertyValue > rValues)
870 ValueCounter_Impl aCounter(pImpl->nInValueChange);
871 sal_Bool bRet = sal_True;
872 Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
873 if(xHierarchyAccess.is())
875 Reference<XChangesBatch> xBatch(xHierarchyAccess, UNO_QUERY);
878 Reference<XNameContainer> xCont;
879 if(!rNode.isEmpty())
881 Any aNode = xHierarchyAccess->getByHierarchicalName(rNode);
882 aNode >>= xCont;
884 else
885 xCont = Reference<XNameContainer> (xHierarchyAccess, UNO_QUERY);
886 if(!xCont.is())
887 return sal_False;
889 Reference<XSingleServiceFactory> xFac(xCont, UNO_QUERY);
891 if(xFac.is())
893 const Sequence< OUString > aSubNodeNames = lcl_extractSetPropertyNames(rValues, rNode);
895 const sal_Int32 nSubNodeCount = aSubNodeNames.getLength();
897 for(sal_Int32 j = 0; j <nSubNodeCount ; j++)
899 if(!xCont->hasByName(aSubNodeNames[j]))
901 Reference<XInterface> xInst = xFac->createInstance();
902 Any aVal; aVal <<= xInst;
903 xCont->insertByName(aSubNodeNames[j], aVal);
905 //set values
909 xBatch->commitChanges();
911 CATCH_INFO("Exception from commitChanges(): ")
913 const PropertyValue* pProperties = rValues.getConstArray();
915 Sequence< OUString > aSetNames(rValues.getLength());
916 OUString* pSetNames = aSetNames.getArray();
918 Sequence< Any> aSetValues(rValues.getLength());
919 Any* pSetValues = aSetValues.getArray();
921 sal_Bool bEmptyNode = rNode.isEmpty();
922 for(sal_Int32 k = 0; k < rValues.getLength(); k++)
924 pSetNames[k] = pProperties[k].Name.copy( bEmptyNode ? 1 : 0);
925 pSetValues[k] = pProperties[k].Value;
927 bRet = PutProperties(aSetNames, aSetValues);
929 else
931 //if no factory is available then the node contains basic data elements
932 const PropertyValue* pValues = rValues.getConstArray();
933 for(int nValue = 0; nValue < rValues.getLength();nValue++)
937 OUString sSubNode = lcl_extractSetPropertyName( pValues[nValue].Name, rNode );
939 if(xCont->hasByName(sSubNode))
940 xCont->replaceByName(sSubNode, pValues[nValue].Value);
941 else
942 xCont->insertByName(sSubNode, pValues[nValue].Value);
944 OSL_ENSURE( xHierarchyAccess->hasByHierarchicalName(pValues[nValue].Name),
945 "Invalid config path" );
947 CATCH_INFO("Exception form insert/replaceByName(): ")
949 xBatch->commitChanges();
952 #ifdef DBG_UTIL
953 catch (const Exception& rEx)
955 lcl_CFG_DBG_EXCEPTION("Exception from SetSetProperties: ", rEx);
956 #else
957 catch (const Exception&)
959 #endif
960 bRet = sal_False;
963 return bRet;
966 sal_Bool ConfigItem::ReplaceSetProperties(
967 const OUString& rNode, Sequence< PropertyValue > rValues)
969 ValueCounter_Impl aCounter(pImpl->nInValueChange);
970 sal_Bool bRet = sal_True;
971 Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
972 if(xHierarchyAccess.is())
974 Reference<XChangesBatch> xBatch(xHierarchyAccess, UNO_QUERY);
977 Reference<XNameContainer> xCont;
978 if(!rNode.isEmpty())
980 Any aNode = xHierarchyAccess->getByHierarchicalName(rNode);
981 aNode >>= xCont;
983 else
984 xCont = Reference<XNameContainer> (xHierarchyAccess, UNO_QUERY);
985 if(!xCont.is())
986 return sal_False;
988 // JB: Change: now the same name handling for sets of simple values
989 const Sequence< OUString > aSubNodeNames = lcl_extractSetPropertyNames(rValues, rNode);
990 const OUString* pSubNodeNames = aSubNodeNames.getConstArray();
991 const sal_Int32 nSubNodeCount = aSubNodeNames.getLength();
993 Reference<XSingleServiceFactory> xFac(xCont, UNO_QUERY);
994 const bool isSimpleValueSet = !xFac.is();
996 //remove unknown members first
998 const Sequence<OUString> aContainerSubNodes = xCont->getElementNames();
999 const OUString* pContainerSubNodes = aContainerSubNodes.getConstArray();
1001 for(sal_Int32 nContSub = 0; nContSub < aContainerSubNodes.getLength(); nContSub++)
1003 sal_Bool bFound = sal_False;
1004 for(sal_Int32 j = 0; j < nSubNodeCount; j++)
1006 if(pSubNodeNames[j] == pContainerSubNodes[nContSub])
1008 bFound = sal_True;
1009 break;
1012 if(!bFound)
1015 xCont->removeByName(pContainerSubNodes[nContSub]);
1017 catch (const Exception&)
1019 if (isSimpleValueSet)
1023 // #i37322#: fallback action: replace with <void/>
1024 xCont->replaceByName(pContainerSubNodes[nContSub], Any());
1025 // fallback successful: continue looping
1026 continue;
1028 catch (Exception &)
1029 {} // propagate original exception, if fallback fails
1031 throw;
1034 try { xBatch->commitChanges(); }
1035 CATCH_INFO("Exception from commitChanges(): ")
1038 if(xFac.is()) // !isSimpleValueSet
1040 for(sal_Int32 j = 0; j < nSubNodeCount; j++)
1042 if(!xCont->hasByName(pSubNodeNames[j]))
1044 //create if not available
1045 Reference<XInterface> xInst = xFac->createInstance();
1046 Any aVal; aVal <<= xInst;
1047 xCont->insertByName(pSubNodeNames[j], aVal);
1050 try { xBatch->commitChanges(); }
1051 CATCH_INFO("Exception from commitChanges(): ")
1053 const PropertyValue* pProperties = rValues.getConstArray();
1055 Sequence< OUString > aSetNames(rValues.getLength());
1056 OUString* pSetNames = aSetNames.getArray();
1058 Sequence< Any> aSetValues(rValues.getLength());
1059 Any* pSetValues = aSetValues.getArray();
1061 sal_Bool bEmptyNode = rNode.isEmpty();
1062 for(sal_Int32 k = 0; k < rValues.getLength(); k++)
1064 pSetNames[k] = pProperties[k].Name.copy( bEmptyNode ? 1 : 0);
1065 pSetValues[k] = pProperties[k].Value;
1067 bRet = PutProperties(aSetNames, aSetValues);
1069 else
1071 const PropertyValue* pValues = rValues.getConstArray();
1073 //if no factory is available then the node contains basic data elements
1074 for(int nValue = 0; nValue < rValues.getLength();nValue++)
1078 OUString sSubNode = lcl_extractSetPropertyName( pValues[nValue].Name, rNode );
1080 if(xCont->hasByName(sSubNode))
1081 xCont->replaceByName(sSubNode, pValues[nValue].Value);
1082 else
1083 xCont->insertByName(sSubNode, pValues[nValue].Value);
1085 CATCH_INFO("Exception from insert/replaceByName(): ");
1087 xBatch->commitChanges();
1090 #ifdef DBG_UTIL
1091 catch (const Exception& rEx)
1093 lcl_CFG_DBG_EXCEPTION("Exception from ReplaceSetProperties: ", rEx);
1094 #else
1095 catch (const Exception&)
1097 #endif
1098 bRet = sal_False;
1101 return bRet;
1104 sal_Bool ConfigItem::getUniqueSetElementName( const OUString& _rSetNode, OUString& _rName)
1106 Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
1107 sal_Bool bRet = sal_False;
1108 if(xHierarchyAccess.is())
1112 Reference< XNameAccess > xSetNode;
1113 xHierarchyAccess->getByHierarchicalName(_rSetNode) >>= xSetNode;
1114 if (xSetNode.is())
1116 const sal_uInt32 nPrime = 65521; // a prime number
1117 const sal_uInt32 nPrimeLess2 = nPrime - 2;
1118 sal_uInt32 nEngendering = (rand() % nPrimeLess2) + 2; // the engendering of the field
1120 // the element which will loop through the field
1121 sal_uInt32 nFieldElement = nEngendering;
1123 for (; 1 != nFieldElement; nFieldElement = (nFieldElement * nEngendering) % nPrime)
1125 OUString sThisRoundTrial = _rName;
1126 sThisRoundTrial += OUString::valueOf((sal_Int32)nFieldElement);
1128 if (!xSetNode->hasByName(sThisRoundTrial))
1130 _rName = sThisRoundTrial;
1131 bRet = sal_True;
1132 break;
1137 CATCH_INFO("Exception from getUniqueSetElementName(): ")
1139 return bRet;
1142 sal_Bool ConfigItem::AddNode(const OUString& rNode, const OUString& rNewNode)
1144 ValueCounter_Impl aCounter(pImpl->nInValueChange);
1145 sal_Bool bRet = sal_True;
1146 Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
1147 if(xHierarchyAccess.is())
1149 Reference<XChangesBatch> xBatch(xHierarchyAccess, UNO_QUERY);
1152 Reference<XNameContainer> xCont;
1153 if(!rNode.isEmpty())
1155 Any aNode = xHierarchyAccess->getByHierarchicalName(rNode);
1156 aNode >>= xCont;
1158 else
1159 xCont = Reference<XNameContainer> (xHierarchyAccess, UNO_QUERY);
1160 if(!xCont.is())
1161 return sal_False;
1163 Reference<XSingleServiceFactory> xFac(xCont, UNO_QUERY);
1165 if(xFac.is())
1167 if(!xCont->hasByName(rNewNode))
1169 Reference<XInterface> xInst = xFac->createInstance();
1170 Any aVal; aVal <<= xInst;
1171 xCont->insertByName(rNewNode, aVal);
1175 xBatch->commitChanges();
1177 CATCH_INFO("Exception from commitChanges(): ")
1179 else
1181 //if no factory is available then the node contains basic data elements
1184 if(!xCont->hasByName(rNewNode))
1185 xCont->insertByName(rNewNode, Any());
1187 CATCH_INFO("Exception from AddNode(): ")
1189 xBatch->commitChanges();
1191 #ifdef DBG_UTIL
1192 catch (const Exception& rEx)
1194 lcl_CFG_DBG_EXCEPTION("Exception from AddNode(): ", rEx);
1195 #else
1196 catch (const Exception&)
1198 #endif
1199 bRet = sal_False;
1202 return bRet;
1205 sal_Int16 ConfigItem::GetMode() const
1207 return pImpl->nMode;
1210 void ConfigItem::SetModified()
1212 pImpl->bIsModified = sal_True;
1215 void ConfigItem::ClearModified()
1217 pImpl->bIsModified = sal_False;
1220 sal_Bool ConfigItem::IsModified() const
1222 return pImpl->bIsModified;
1225 sal_Bool ConfigItem::IsInValueChange() const
1227 return pImpl->nInValueChange > 0;
1230 Reference< XHierarchicalNameAccess> ConfigItem::GetTree()
1232 Reference< XHierarchicalNameAccess> xRet;
1233 if(!m_xHierarchyAccess.is())
1234 xRet = ConfigManager::acquireTree(*this);
1235 else
1236 xRet = m_xHierarchyAccess;
1237 return xRet;
1240 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */