xref: /core/configmgr/source/access.cxx (revision 80f990b8)
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 <cassert>
23 #include <cstdlib>
24 #include <vector>
25 
26 #include <com/sun/star/beans/Property.hpp>
27 #include <com/sun/star/beans/PropertyAttribute.hpp>
28 #include <com/sun/star/beans/PropertyChangeEvent.hpp>
29 #include <com/sun/star/beans/PropertyVetoException.hpp>
30 #include <com/sun/star/beans/UnknownPropertyException.hpp>
31 #include <com/sun/star/beans/XExactName.hpp>
32 #include <com/sun/star/beans/XHierarchicalPropertySet.hpp>
33 #include <com/sun/star/beans/XHierarchicalPropertySetInfo.hpp>
34 #include <com/sun/star/beans/XMultiHierarchicalPropertySet.hpp>
35 #include <com/sun/star/beans/XMultiPropertySet.hpp>
36 #include <com/sun/star/beans/XPropertiesChangeListener.hpp>
37 #include <com/sun/star/beans/XProperty.hpp>
38 #include <com/sun/star/beans/XPropertyChangeListener.hpp>
39 #include <com/sun/star/beans/XPropertySet.hpp>
40 #include <com/sun/star/beans/XPropertySetInfo.hpp>
41 #include <com/sun/star/beans/XVetoableChangeListener.hpp>
42 #include <com/sun/star/container/ContainerEvent.hpp>
43 #include <com/sun/star/container/NoSuchElementException.hpp>
44 #include <com/sun/star/container/XContainer.hpp>
45 #include <com/sun/star/container/XContainerListener.hpp>
46 #include <com/sun/star/container/XElementAccess.hpp>
47 #include <com/sun/star/container/XHierarchicalName.hpp>
48 #include <com/sun/star/container/XHierarchicalNameAccess.hpp>
49 #include <com/sun/star/container/XHierarchicalNameReplace.hpp>
50 #include <com/sun/star/container/XNameAccess.hpp>
51 #include <com/sun/star/container/XNameContainer.hpp>
52 #include <com/sun/star/container/XNamed.hpp>
53 #include <com/sun/star/lang/DisposedException.hpp>
54 #include <com/sun/star/lang/EventObject.hpp>
55 #include <com/sun/star/lang/IllegalArgumentException.hpp>
56 #include <com/sun/star/lang/NoSupportException.hpp>
57 #include <com/sun/star/lang/WrappedTargetException.hpp>
58 #include <com/sun/star/lang/XComponent.hpp>
59 #include <com/sun/star/lang/XEventListener.hpp>
60 #include <com/sun/star/lang/XServiceInfo.hpp>
61 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
62 #include <com/sun/star/lang/XTypeProvider.hpp>
63 #include <com/sun/star/lang/XUnoTunnel.hpp>
64 #include <com/sun/star/uno/Any.hxx>
65 #include <com/sun/star/uno/Reference.hxx>
66 #include <com/sun/star/uno/RuntimeException.hpp>
67 #include <com/sun/star/uno/Sequence.hxx>
68 #include <com/sun/star/uno/Type.hxx>
69 #include <com/sun/star/uno/TypeClass.hpp>
70 #include <com/sun/star/uno/XInterface.hpp>
71 #include <com/sun/star/uno/XWeak.hpp>
72 #include <com/sun/star/util/ElementChange.hpp>
73 #include <comphelper/sequence.hxx>
74 #include <cppu/unotype.hxx>
75 #include <cppuhelper/queryinterface.hxx>
76 #include <cppuhelper/supportsservice.hxx>
77 #include <cppuhelper/weak.hxx>
78 #include <osl/interlck.h>
79 #include <osl/mutex.hxx>
80 #include <rtl/character.hxx>
81 #include <rtl/ref.hxx>
82 #include <rtl/ustrbuf.hxx>
83 #include <rtl/ustring.h>
84 #include <rtl/ustring.hxx>
85 #include <sal/log.hxx>
86 #include <sal/types.h>
87 
88 #include "access.hxx"
89 #include "broadcaster.hxx"
90 #include "childaccess.hxx"
91 #include "components.hxx"
92 #include "data.hxx"
93 #include "groupnode.hxx"
94 #include "localizedpropertynode.hxx"
95 #include "localizedvaluenode.hxx"
96 #include "lock.hxx"
97 #include "modifications.hxx"
98 #include "node.hxx"
99 #include "nodemap.hxx"
100 #include "propertynode.hxx"
101 #include "rootaccess.hxx"
102 #include "setnode.hxx"
103 #include "type.hxx"
104 
105 namespace configmgr {
106 
107 namespace {
108 
109 // Conservatively forbid what is either not an XML Char (including lone
110 // surrogates, even though they should not appear in well-formed UNO OUString
111 // instances anyway), or is a slash (as it causes problems in path syntax):
112 bool isValidName(OUString const & name, bool setMember) {
113     for (sal_Int32 i = 0; i != name.getLength();) {
114         sal_uInt32 c = name.iterateCodePoints(&i);
115         if ((c < 0x20 && !(c == 0x09 || c == 0x0A || c == 0x0D))
116             || rtl::isSurrogate(c) || c == 0xFFFE || c == 0xFFFF
117             || (!setMember && c == '/'))
118         {
119             return false;
120         }
121     }
122     return !name.isEmpty();
123 }
124 
125 }
126 
127 oslInterlockedCount Access::acquireCounting() {
128     return osl_atomic_increment(&m_refCount);
129 }
130 
131 void Access::releaseNondeleting() {
132     osl_atomic_decrement(&m_refCount);
133 }
134 
135 bool Access::isValue() {
136     rtl::Reference< Node > p(getNode());
137     switch (p->kind()) {
138     case Node::KIND_PROPERTY:
139     case Node::KIND_LOCALIZED_VALUE:
140         return true;
141     case Node::KIND_LOCALIZED_PROPERTY:
142         return !Components::allLocales(getRootAccess()->getLocale());
143     default:
144         return false;
145     }
146 }
147 
148 void Access::markChildAsModified(rtl::Reference< ChildAccess > const & child) {
149     assert(child.is() && child->getParentAccess() == this);
150     modifiedChildren_[child->getNameInternal()] = ModifiedChild(child, true);
151     for (rtl::Reference< Access > p(this);;) {
152         rtl::Reference< Access > parent(p->getParentAccess());
153         if (!parent.is()) {
154             break;
155         }
156         assert(dynamic_cast< ChildAccess * >(p.get()) != nullptr);
157         parent->modifiedChildren_.emplace(
158                 p->getNameInternal(),
159                 ModifiedChild(static_cast< ChildAccess * >(p.get()), false));
160         p = parent;
161     }
162 }
163 
164 void Access::releaseChild(OUString const & name) {
165     cachedChildren_.erase(name);
166 }
167 
168 void Access::initBroadcaster(
169     Modifications::Node const & modifications, Broadcaster * broadcaster)
170 {
171     initBroadcasterAndChanges(modifications, broadcaster, nullptr);
172 }
173 
174 css::uno::Sequence< css::uno::Type > Access::getTypes()
175 {
176     assert(thisIs(IS_ANY));
177     osl::MutexGuard g(*lock_);
178     checkLocalizedPropertyAccess();
179     std::vector< css::uno::Type > types;
180     types.push_back(cppu::UnoType< css::uno::XInterface >::get());
181     types.push_back(cppu::UnoType< css::uno::XWeak >::get());
182     types.push_back(cppu::UnoType< css::lang::XTypeProvider >::get());
183     types.push_back(cppu::UnoType< css::lang::XServiceInfo >::get());
184     types.push_back(cppu::UnoType< css::lang::XComponent >::get());
185     types.push_back(cppu::UnoType< css::container::XContainer >::get());
186     types.push_back(cppu::UnoType< css::beans::XExactName >::get());
187     types.push_back(cppu::UnoType< css::container::XHierarchicalName >::get());
188     types.push_back(cppu::UnoType< css::container::XNamed >::get());
189     types.push_back(cppu::UnoType< css::beans::XProperty >::get());
190     types.push_back(cppu::UnoType< css::container::XElementAccess >::get());
191     types.push_back(cppu::UnoType< css::container::XNameAccess >::get());
192     if (getNode()->kind() == Node::KIND_GROUP) {
193         types.push_back(cppu::UnoType< css::beans::XPropertySetInfo >::get());
194         types.push_back(cppu::UnoType< css::beans::XPropertySet >::get());
195         types.push_back(cppu::UnoType< css::beans::XMultiPropertySet >::get());
196         types.push_back(
197             cppu::UnoType< css::beans::XHierarchicalPropertySet >::get());
198         types.push_back(
199             cppu::UnoType< css::beans::XMultiHierarchicalPropertySet >::get());
200         types.push_back(
201             cppu::UnoType< css::beans::XHierarchicalPropertySetInfo >::get());
202     }
203     if (getRootAccess()->isUpdate()) {
204         types.push_back(cppu::UnoType< css::container::XNameReplace >::get());
205         types.push_back(
206             cppu::UnoType< css::container::XHierarchicalNameReplace >::get());
207         if (getNode()->kind() != Node::KIND_GROUP ||
208             static_cast< GroupNode * >(getNode().get())->isExtensible())
209         {
210             types.push_back(
211                 cppu::UnoType< css::container::XNameContainer >::get());
212         }
213         if (getNode()->kind() == Node::KIND_SET) {
214             types.push_back(
215                 cppu::UnoType< css::lang::XSingleServiceFactory >::get());
216         }
217     } else {
218         types.push_back(
219             cppu::UnoType< css::container::XHierarchicalNameAccess >::get());
220     }
221     addTypes(&types);
222     return comphelper::containerToSequence(types);
223 }
224 
225 css::uno::Sequence< sal_Int8 > Access::getImplementationId()
226 {
227     assert(thisIs(IS_ANY));
228     osl::MutexGuard g(*lock_);
229     checkLocalizedPropertyAccess();
230     return css::uno::Sequence< sal_Int8 >();
231 }
232 
233 OUString Access::getImplementationName()
234 {
235     assert(thisIs(IS_ANY));
236     osl::MutexGuard g(*lock_);
237     checkLocalizedPropertyAccess();
238     return OUString("org.openoffice-configmgr::Access");
239 }
240 
241 sal_Bool Access::supportsService(OUString const & ServiceName)
242 {
243     return cppu::supportsService(this, ServiceName);
244 }
245 
246 css::uno::Sequence< OUString > Access::getSupportedServiceNames()
247 {
248     assert(thisIs(IS_ANY));
249     osl::MutexGuard g(*lock_);
250     checkLocalizedPropertyAccess();
251     std::vector<OUString> services;
252     services.emplace_back("com.sun.star.configuration.ConfigurationAccess");
253     if (getRootAccess()->isUpdate()) {
254         services.emplace_back("com.sun.star.configuration.ConfigurationUpdateAccess");
255     }
256     services.emplace_back("com.sun.star.configuration.HierarchyAccess");
257     services.emplace_back("com.sun.star.configuration.HierarchyElement");
258     if (getNode()->kind() == Node::KIND_GROUP) {
259         services.emplace_back("com.sun.star.configuration.GroupAccess");
260         services.emplace_back("com.sun.star.configuration.PropertyHierarchy");
261         if (getRootAccess()->isUpdate()) {
262             services.emplace_back("com.sun.star.configuration.GroupUpdate");
263         }
264     } else {
265         services.emplace_back("com.sun.star.configuration.SetAccess");
266         services.emplace_back("com.sun.star.configuration.SimpleSetAccess");
267         if (getRootAccess()->isUpdate()) {
268             services.emplace_back("com.sun.star.configuration.SetUpdate");
269             services.emplace_back("com.sun.star.configuration.SimpleSetUpdate");
270         }
271     }
272     addSupportedServiceNames(&services);
273     return comphelper::containerToSequence(services);
274 }
275 
276 void Access::dispose() {
277     assert(thisIs(IS_ANY));
278     Broadcaster bc;
279     {
280         osl::MutexGuard g(*lock_);
281         checkLocalizedPropertyAccess();
282         if (getParentAccess().is()) {
283             throw css::uno::RuntimeException(
284                 "configmgr dispose inappropriate Access",
285                 static_cast< cppu::OWeakObject * >(this));
286         }
287         if (disposed_) {
288             return;
289         }
290         initDisposeBroadcaster(&bc);
291         clearListeners();
292         disposed_ = true;
293     }
294     bc.send();
295 }
296 
297 void Access::addEventListener(
298     css::uno::Reference< css::lang::XEventListener > const & xListener)
299 {
300     assert(thisIs(IS_ANY));
301     {
302         osl::MutexGuard g(*lock_);
303         checkLocalizedPropertyAccess();
304         if (!xListener.is()) {
305             throw css::uno::RuntimeException(
306                 "null listener", static_cast< cppu::OWeakObject * >(this));
307         }
308         if (!disposed_) {
309             disposeListeners_.insert(xListener);
310             return;
311         }
312     }
313     try {
314         xListener->disposing(
315             css::lang::EventObject(static_cast< cppu::OWeakObject * >(this)));
316     } catch (css::lang::DisposedException &) {}
317 }
318 
319 void Access::removeEventListener(
320     css::uno::Reference< css::lang::XEventListener > const & aListener)
321 {
322     assert(thisIs(IS_ANY));
323     osl::MutexGuard g(*lock_);
324     checkLocalizedPropertyAccess();
325     DisposeListeners::iterator i(disposeListeners_.find(aListener));
326     if (i != disposeListeners_.end()) {
327         disposeListeners_.erase(i);
328     }
329 }
330 
331 css::uno::Type Access::getElementType() {
332     assert(thisIs(IS_ANY));
333     osl::MutexGuard g(*lock_);
334     checkLocalizedPropertyAccess();
335     rtl::Reference< Node > p(getNode());
336     switch (p->kind()) {
337     case Node::KIND_LOCALIZED_PROPERTY:
338         return mapType(
339             static_cast< LocalizedPropertyNode * >(p.get())->getStaticType());
340     case Node::KIND_GROUP:
341         //TODO: Should a specific type be returned for a non-extensible group
342         // with homogeneous members or for an extensible group that currently
343         // has only homogeneous members?
344         return cppu::UnoType<void>::get();
345     case Node::KIND_SET:
346         return cppu::UnoType<void>::get(); //TODO: correct?
347     default:
348         assert(false);
349         throw css::uno::RuntimeException(
350             "this cannot happen", static_cast< cppu::OWeakObject * >(this));
351     }
352 }
353 
354 sal_Bool Access::hasElements() {
355     assert(thisIs(IS_ANY));
356     osl::MutexGuard g(*lock_);
357     checkLocalizedPropertyAccess();
358     return !getAllChildren().empty(); //TODO: optimize
359 }
360 
361 bool Access::getByNameFast(const OUString & name, css::uno::Any & value)
362 {
363     bool bGotValue = false;
364     rtl::Reference< ChildAccess > child;
365 
366     if (getNode()->kind() != Node::KIND_LOCALIZED_PROPERTY)
367     { // try to get it directly
368         ModifiedChildren::iterator i(modifiedChildren_.find(name));
369         if (i != modifiedChildren_.end())
370         {
371             child = getModifiedChild(i);
372             if (child.is())
373             {
374                 value = child->asValue();
375                 bGotValue = true;
376             }
377         }
378         else
379         {
380             rtl::Reference< Node > node(getNode()->getMember(name));
381             if (!node.is())
382                 return false;
383             bGotValue = ChildAccess::asSimpleValue(node, value, components_);
384         }
385     }
386 
387     if (!bGotValue)
388     {
389         child = getChild(name);
390         if (!child.is())
391             return false;
392         value = child->asValue();
393     }
394     return true;
395 }
396 
397 css::uno::Any Access::getByName(OUString const & aName)
398 {
399     assert(thisIs(IS_ANY));
400     osl::MutexGuard g(*lock_);
401     checkLocalizedPropertyAccess();
402     css::uno::Any value;
403     if (!getByNameFast(aName, value))
404         throw css::container::NoSuchElementException(
405             aName, static_cast< cppu::OWeakObject * >(this));
406     return value;
407 }
408 
409 css::uno::Sequence< OUString > Access::getElementNames()
410 {
411     assert(thisIs(IS_ANY));
412     osl::MutexGuard g(*lock_);
413     checkLocalizedPropertyAccess();
414     std::vector< rtl::Reference< ChildAccess > > children(getAllChildren());
415     std::vector<OUString> names;
416     names.reserve(children.size());
417     for (auto const& child : children)
418     {
419         names.push_back(child->getNameInternal());
420     }
421     return comphelper::containerToSequence(names);
422 }
423 
424 sal_Bool Access::hasByName(OUString const & aName)
425 {
426     assert(thisIs(IS_ANY));
427     osl::MutexGuard g(*lock_);
428     checkLocalizedPropertyAccess();
429     return getChild(aName).is();
430 }
431 
432 css::uno::Any Access::getByHierarchicalName(OUString const & aName)
433 {
434     assert(thisIs(IS_ANY));
435     osl::MutexGuard g(*lock_);
436     checkLocalizedPropertyAccess();
437     rtl::Reference< ChildAccess > child(getSubChild(aName));
438     if (!child.is()) {
439         throw css::container::NoSuchElementException(
440             aName, static_cast< cppu::OWeakObject * >(this));
441     }
442     return child->asValue();
443 }
444 
445 sal_Bool Access::hasByHierarchicalName(OUString const & aName)
446 {
447     assert(thisIs(IS_ANY));
448     osl::MutexGuard g(*lock_);
449     checkLocalizedPropertyAccess();
450     return getSubChild(aName).is();
451 }
452 
453 void Access::replaceByHierarchicalName(
454     OUString const & aName, css::uno::Any const & aElement)
455 {
456     //TODO: Actually support sets and combine with replaceByName:
457     assert(thisIs(IS_UPDATE));
458     Broadcaster bc;
459     {
460         osl::MutexGuard g(*lock_);
461         checkLocalizedPropertyAccess();
462         rtl::Reference< ChildAccess > child(getSubChild(aName));
463         if (!child.is()) {
464             throw css::container::NoSuchElementException(
465                 aName, static_cast< cppu::OWeakObject * >(this));
466         }
467         child->checkFinalized();
468         rtl::Reference< Node > parent(child->getParentNode());
469         assert(parent.is());
470         Modifications localMods;
471         switch (parent->kind()) {
472         case Node::KIND_LOCALIZED_PROPERTY:
473         case Node::KIND_GROUP:
474             child->setProperty(aElement, &localMods);
475             break;
476         case Node::KIND_SET:
477             throw css::lang::IllegalArgumentException(
478                 ("configmgr::Access::replaceByHierarchicalName does not"
479                  " currently support set members"),
480                 static_cast< cppu::OWeakObject * >(this), 0);
481         case Node::KIND_ROOT:
482             throw css::lang::IllegalArgumentException(
483                 ("configmgr::Access::replaceByHierarchicalName does not allow"
484                  " changing component " + aName),
485                 static_cast< cppu::OWeakObject * >(this), 0);
486         default:
487             assert(false); // this cannot happen
488             break;
489         }
490         getNotificationRoot()->initBroadcaster(localMods.getRoot(), &bc);
491     }
492     bc.send();
493 }
494 
495 void Access::addContainerListener(
496     css::uno::Reference< css::container::XContainerListener > const & xListener)
497 {
498     assert(thisIs(IS_ANY));
499     {
500         osl::MutexGuard g(*lock_);
501         checkLocalizedPropertyAccess();
502         if (!xListener.is()) {
503             throw css::uno::RuntimeException(
504                 "null listener", static_cast< cppu::OWeakObject * >(this));
505         }
506         if (!disposed_) {
507             containerListeners_.insert(xListener);
508             return;
509         }
510     }
511     try {
512         xListener->disposing(
513             css::lang::EventObject(static_cast< cppu::OWeakObject * >(this)));
514     } catch (css::lang::DisposedException &) {}
515 }
516 
517 void Access::removeContainerListener(
518     css::uno::Reference< css::container::XContainerListener > const & xListener)
519 {
520     assert(thisIs(IS_ANY));
521     osl::MutexGuard g(*lock_);
522     checkLocalizedPropertyAccess();
523     ContainerListeners::iterator i(containerListeners_.find(xListener));
524     if (i != containerListeners_.end()) {
525         containerListeners_.erase(i);
526     }
527 }
528 
529 OUString Access::getExactName(OUString const & aApproximateName)
530 {
531     assert(thisIs(IS_ANY));
532     osl::MutexGuard g(*lock_);
533     checkLocalizedPropertyAccess();
534     return aApproximateName;
535 }
536 
537 css::uno::Sequence< css::beans::Property > Access::getProperties()
538 {
539     assert(thisIs(IS_GROUP));
540     osl::MutexGuard g(*lock_);
541     std::vector< rtl::Reference< ChildAccess > > children(getAllChildren());
542     std::vector< css::beans::Property > properties;
543     properties.reserve(children.size());
544     for (auto const& child : children)
545     {
546         properties.push_back(child->asProperty());
547     }
548     return comphelper::containerToSequence(properties);
549 }
550 
551 css::beans::Property Access::getPropertyByName(OUString const & aName)
552 {
553     assert(thisIs(IS_GROUP));
554     osl::MutexGuard g(*lock_);
555     rtl::Reference< ChildAccess > child(getChild(aName));
556     if (!child.is()) {
557         throw css::beans::UnknownPropertyException(
558             aName, static_cast< cppu::OWeakObject * >(this));
559     }
560     return child->asProperty();
561 }
562 
563 sal_Bool Access::hasPropertyByName(OUString const & Name)
564 {
565     assert(thisIs(IS_GROUP));
566     osl::MutexGuard g(*lock_);
567     return getChild(Name).is();
568 }
569 
570 OUString Access::getHierarchicalName() {
571     assert(thisIs(IS_ANY));
572     osl::MutexGuard g(*lock_);
573     checkLocalizedPropertyAccess();
574     // For backwards compatibility, return an absolute path representation where
575     // available:
576     OUString rootPath;
577     rtl::Reference< RootAccess > root(getRootAccess());
578     if (root.is()) {
579         rootPath = root->getAbsolutePathRepresentation();
580     }
581     OUString rel(getRelativePathRepresentation());
582     OUStringBuffer path(rootPath);
583     if (!rootPath.isEmpty() && rootPath != "/" && !rel.isEmpty()) {
584         path.append('/');
585     }
586     path.append(rel);
587     return path.makeStringAndClear();
588 }
589 
590 OUString Access::composeHierarchicalName(
591     OUString const & aRelativeName)
592 {
593     assert(thisIs(IS_ANY));
594     osl::MutexGuard g(*lock_);
595     checkLocalizedPropertyAccess();
596     if (aRelativeName.isEmpty() || aRelativeName[0] == '/') {
597         throw css::lang::IllegalArgumentException(
598             "configmgr composeHierarchicalName inappropriate relative name",
599             static_cast< cppu::OWeakObject * >(this), -1);
600     }
601     OUStringBuffer path(getRelativePathRepresentation());
602     if (!path.isEmpty()) {
603         path.append('/');
604     }
605     path.append(aRelativeName);
606     return path.makeStringAndClear();
607 }
608 
609 OUString Access::getName() {
610     assert(thisIs(IS_ANY));
611     osl::MutexGuard g(*lock_);
612     checkLocalizedPropertyAccess();
613     return getNameInternal();
614 }
615 
616 void Access::setName(OUString const & aName)
617 {
618     assert(thisIs(IS_ANY));
619     Broadcaster bc;
620     {
621         osl::MutexGuard g(*lock_);
622         checkLocalizedPropertyAccess();
623         checkFinalized();
624         Modifications localMods;
625         switch (getNode()->kind()) {
626         case Node::KIND_GROUP:
627         case Node::KIND_SET:
628             {
629                 rtl::Reference< Access > parent(getParentAccess());
630                 if (parent.is()) {
631                     rtl::Reference< Node > node(getNode());
632                     if (! node->getTemplateName().isEmpty()) {
633                         rtl::Reference< ChildAccess > other(
634                             parent->getChild(aName));
635                         if (other.get() == this) {
636                             break;
637                         }
638                         if (node->getMandatory() == Data::NO_LAYER &&
639                             !(other.is() && other->isFinalized()))
640                         {
641                             if (!isValidName(aName, true)) {
642                                 throw css::uno::RuntimeException(
643                                     "invalid element name " + aName);
644                             }
645                             rtl::Reference< RootAccess > root(getRootAccess());
646                             rtl::Reference< ChildAccess > childAccess(
647                                 static_cast< ChildAccess * >(this));
648                             localMods.add(getRelativePath());
649                             // unbind() modifies the parent chain that
650                             // markChildAsModified() walks, so order is
651                             // important:
652                             parent->markChildAsModified(childAccess);
653                                 //TODO: must not throw
654                             childAccess->unbind(); // must not throw
655                             if (other.is()) {
656                                 other->unbind(); // must not throw
657                             }
658                             childAccess->bind(root, parent, aName);
659                                 // must not throw
660                             parent->markChildAsModified(childAccess);
661                                 //TODO: must not throw
662                             localMods.add(getRelativePath());
663                             break;
664                         }
665                     }
666                 }
667             }
668             SAL_FALLTHROUGH;
669         case Node::KIND_LOCALIZED_PROPERTY:
670             // renaming a property could only work for an extension property,
671             // but a localized property is never an extension property
672             throw css::uno::RuntimeException(
673                 "configmgr setName inappropriate node",
674                 static_cast< cppu::OWeakObject * >(this));
675         default:
676             assert(false); // this cannot happen
677             break;
678         }
679         getNotificationRoot()->initBroadcaster(localMods.getRoot(), &bc);
680     }
681     bc.send();
682 }
683 
684 css::beans::Property Access::getAsProperty()
685 {
686     assert(thisIs(IS_ANY));
687     osl::MutexGuard g(*lock_);
688     checkLocalizedPropertyAccess();
689     return asProperty();
690 }
691 
692 css::uno::Reference< css::beans::XPropertySetInfo > Access::getPropertySetInfo()
693 {
694     assert(thisIs(IS_GROUP));
695     return this;
696 }
697 
698 void Access::setPropertyValue(
699     OUString const & aPropertyName, css::uno::Any const & aValue)
700 {
701     assert(thisIs(IS_GROUP));
702     Broadcaster bc;
703     {
704         osl::MutexGuard g(*lock_);
705         if (!getRootAccess()->isUpdate()) {
706             throw css::uno::RuntimeException(
707                 "configmgr setPropertyValue on non-update access",
708                 static_cast< cppu::OWeakObject * >(this));
709         }
710         Modifications localMods;
711         if (!setChildProperty(aPropertyName, aValue, &localMods)) {
712             throw css::beans::UnknownPropertyException(
713                 aPropertyName, static_cast< cppu::OWeakObject * >(this));
714         }
715         getNotificationRoot()->initBroadcaster(localMods.getRoot(), &bc);
716     }
717     bc.send();
718 }
719 
720 css::uno::Any Access::getPropertyValue(OUString const & PropertyName)
721 {
722     assert(thisIs(IS_GROUP));
723     osl::MutexGuard g(*lock_);
724 
725     css::uno::Any value;
726     if (!getByNameFast(PropertyName, value))
727         throw css::beans::UnknownPropertyException(
728             PropertyName, static_cast< cppu::OWeakObject * >(this));
729     return value;
730 }
731 
732 void Access::addPropertyChangeListener(
733     OUString const & aPropertyName,
734     css::uno::Reference< css::beans::XPropertyChangeListener > const &
735         xListener)
736 {
737     assert(thisIs(IS_GROUP));
738     {
739         osl::MutexGuard g(*lock_);
740         if (!xListener.is()) {
741             throw css::uno::RuntimeException(
742                 "null listener", static_cast< cppu::OWeakObject * >(this));
743         }
744         checkKnownProperty(aPropertyName);
745         if (!disposed_) {
746             propertyChangeListeners_[aPropertyName].insert(xListener);
747             return;
748         }
749     }
750     try {
751         xListener->disposing(
752             css::lang::EventObject(static_cast< cppu::OWeakObject * >(this)));
753     } catch (css::lang::DisposedException &) {}
754 }
755 
756 void Access::removePropertyChangeListener(
757     OUString const & aPropertyName,
758     css::uno::Reference< css::beans::XPropertyChangeListener > const &
759         aListener)
760 {
761     assert(thisIs(IS_GROUP));
762     osl::MutexGuard g(*lock_);
763     checkKnownProperty(aPropertyName);
764     PropertyChangeListeners::iterator i(
765         propertyChangeListeners_.find(aPropertyName));
766     if (i != propertyChangeListeners_.end()) {
767         PropertyChangeListenersElement::iterator j(i->second.find(aListener));
768         if (j != i->second.end()) {
769             i->second.erase(j);
770             if (i->second.empty()) {
771                 propertyChangeListeners_.erase(i);
772             }
773         }
774     }
775 }
776 
777 void Access::addVetoableChangeListener(
778     OUString const & PropertyName,
779     css::uno::Reference< css::beans::XVetoableChangeListener > const &
780         aListener)
781 {
782     assert(thisIs(IS_GROUP));
783     {
784         osl::MutexGuard g(*lock_);
785         if (!aListener.is()) {
786             throw css::uno::RuntimeException(
787                 "null listener", static_cast< cppu::OWeakObject * >(this));
788         }
789         checkKnownProperty(PropertyName);
790         if (!disposed_) {
791             vetoableChangeListeners_[PropertyName].insert(aListener);
792             //TODO: actually call vetoableChangeListeners_
793             return;
794         }
795     }
796     try {
797         aListener->disposing(
798             css::lang::EventObject(static_cast< cppu::OWeakObject * >(this)));
799     } catch (css::lang::DisposedException &) {}
800 }
801 
802 void Access::removeVetoableChangeListener(
803     OUString const & PropertyName,
804     css::uno::Reference< css::beans::XVetoableChangeListener > const &
805         aListener)
806 {
807     assert(thisIs(IS_GROUP));
808     osl::MutexGuard g(*lock_);
809     checkKnownProperty(PropertyName);
810     VetoableChangeListeners::iterator i(
811         vetoableChangeListeners_.find(PropertyName));
812     if (i != vetoableChangeListeners_.end()) {
813         VetoableChangeListenersElement::iterator j(i->second.find(aListener));
814         if (j != i->second.end()) {
815             i->second.erase(j);
816             if (i->second.empty()) {
817                 vetoableChangeListeners_.erase(i);
818             }
819         }
820     }
821 }
822 
823 void Access::setPropertyValues(
824     css::uno::Sequence< OUString > const & aPropertyNames,
825     css::uno::Sequence< css::uno::Any > const & aValues)
826 {
827     assert(thisIs(IS_GROUP));
828     Broadcaster bc;
829     {
830         osl::MutexGuard g(*lock_);
831         if (!getRootAccess()->isUpdate()) {
832             throw css::uno::RuntimeException(
833                 "configmgr setPropertyValues on non-update access",
834                 static_cast< cppu::OWeakObject * >(this));
835         }
836         if (aPropertyNames.getLength() != aValues.getLength()) {
837             throw css::lang::IllegalArgumentException(
838                 ("configmgr setPropertyValues: aPropertyNames/aValues of"
839                  " different length"),
840                 static_cast< cppu::OWeakObject * >(this), -1);
841         }
842         Modifications localMods;
843         for (sal_Int32 i = 0; i < aPropertyNames.getLength(); ++i) {
844             if (!setChildProperty(aPropertyNames[i], aValues[i], &localMods)) {
845                 throw css::lang::IllegalArgumentException(
846                     "configmgr setPropertyValues inappropriate property name",
847                     static_cast< cppu::OWeakObject * >(this), -1);
848             }
849         }
850         getNotificationRoot()->initBroadcaster(localMods.getRoot(), &bc);
851     }
852     bc.send();
853 }
854 
855 css::uno::Sequence< css::uno::Any > Access::getPropertyValues(
856     css::uno::Sequence< OUString > const & aPropertyNames)
857 {
858     assert(thisIs(IS_GROUP));
859     osl::MutexGuard g(*lock_);
860     css::uno::Sequence< css::uno::Any > vals(aPropertyNames.getLength());
861 
862     for (sal_Int32 i = 0; i < aPropertyNames.getLength(); ++i)
863     {
864         if (!getByNameFast(aPropertyNames[i], vals[i]))
865             throw css::uno::RuntimeException(
866                 "configmgr getPropertyValues inappropriate property name",
867                 static_cast< cppu::OWeakObject * >(this));
868     }
869 
870     return vals;
871 }
872 
873 void Access::addPropertiesChangeListener(
874     css::uno::Sequence< OUString > const &,
875     css::uno::Reference< css::beans::XPropertiesChangeListener > const &
876         xListener)
877 {
878     assert(thisIs(IS_GROUP));
879     {
880         osl::MutexGuard g(*lock_);
881         if (!xListener.is()) {
882             throw css::uno::RuntimeException(
883                 "null listener", static_cast< cppu::OWeakObject * >(this));
884         }
885         if (!disposed_) {
886             propertiesChangeListeners_.insert(xListener);
887             return;
888         }
889     }
890     try {
891         xListener->disposing(
892             css::lang::EventObject(static_cast< cppu::OWeakObject * >(this)));
893     } catch (css::lang::DisposedException &) {}
894 }
895 
896 void Access::removePropertiesChangeListener(
897     css::uno::Reference< css::beans::XPropertiesChangeListener > const &
898         xListener)
899 {
900     assert(thisIs(IS_GROUP));
901     osl::MutexGuard g(*lock_);
902     PropertiesChangeListeners::iterator i(
903         propertiesChangeListeners_.find(xListener));
904     if (i != propertiesChangeListeners_.end()) {
905         propertiesChangeListeners_.erase(i);
906     }
907 }
908 
909 void Access::firePropertiesChangeEvent(
910     css::uno::Sequence< OUString > const & aPropertyNames,
911     css::uno::Reference< css::beans::XPropertiesChangeListener > const &
912         xListener)
913 {
914     assert(thisIs(IS_GROUP));
915     css::uno::Sequence< css::beans::PropertyChangeEvent > events(
916         aPropertyNames.getLength());
917     for (sal_Int32 i = 0; i < events.getLength(); ++i) {
918         events[i].Source = static_cast< cppu::OWeakObject * >(this);
919         events[i].PropertyName = aPropertyNames[i];
920         events[i].Further = false;
921         events[i].PropertyHandle = -1;
922     }
923     xListener->propertiesChange(events);
924 }
925 
926 css::uno::Reference< css::beans::XHierarchicalPropertySetInfo >
927 Access::getHierarchicalPropertySetInfo() {
928     assert(thisIs(IS_GROUP));
929     return this;
930 }
931 
932 void Access::setHierarchicalPropertyValue(
933     OUString const & aHierarchicalPropertyName,
934     css::uno::Any const & aValue)
935 {
936     assert(thisIs(IS_GROUP));
937     Broadcaster bc;
938     {
939         osl::MutexGuard g(*lock_);
940         if (!getRootAccess()->isUpdate()) {
941             throw css::uno::RuntimeException(
942                 "configmgr setHierarchicalPropertyName on non-update access",
943                 static_cast< cppu::OWeakObject * >(this));
944         }
945         rtl::Reference< ChildAccess > child(
946             getSubChild(aHierarchicalPropertyName));
947         if (!child.is()) {
948             throw css::beans::UnknownPropertyException(
949                 aHierarchicalPropertyName,
950                 static_cast< cppu::OWeakObject * >(this));
951         }
952         child->checkFinalized();
953         Modifications localMods;
954         child->setProperty(aValue, &localMods);
955         getNotificationRoot()->initBroadcaster(localMods.getRoot(), &bc);
956     }
957     bc.send();
958 }
959 
960 css::uno::Any Access::getHierarchicalPropertyValue(
961     OUString const & aHierarchicalPropertyName)
962 {
963     assert(thisIs(IS_GROUP));
964     osl::MutexGuard g(*lock_);
965     rtl::Reference< ChildAccess > child(getSubChild(aHierarchicalPropertyName));
966     if (!child.is()) {
967         throw css::beans::UnknownPropertyException(
968             aHierarchicalPropertyName,
969             static_cast< cppu::OWeakObject * >(this));
970     }
971     return child->asValue();
972 }
973 
974 void Access::setHierarchicalPropertyValues(
975     css::uno::Sequence< OUString > const & aHierarchicalPropertyNames,
976     css::uno::Sequence< css::uno::Any > const & Values)
977 {
978     assert(thisIs(IS_GROUP));
979     Broadcaster bc;
980     {
981         osl::MutexGuard g(*lock_);
982         if (!getRootAccess()->isUpdate()) {
983             throw css::uno::RuntimeException(
984                 "configmgr setPropertyValues on non-update access",
985                 static_cast< cppu::OWeakObject * >(this));
986         }
987         if (aHierarchicalPropertyNames.getLength() != Values.getLength()) {
988             throw css::lang::IllegalArgumentException(
989                 ("configmgr setHierarchicalPropertyValues:"
990                  " aHierarchicalPropertyNames/Values of different length"),
991                 static_cast< cppu::OWeakObject * >(this), -1);
992         }
993         Modifications localMods;
994         for (sal_Int32 i = 0; i < aHierarchicalPropertyNames.getLength(); ++i) {
995             rtl::Reference< ChildAccess > child(
996                 getSubChild(aHierarchicalPropertyNames[i]));
997             if (!child.is()) {
998                 throw css::lang::IllegalArgumentException(
999                     ("configmgr setHierarchicalPropertyValues inappropriate"
1000                      " property name"),
1001                     static_cast< cppu::OWeakObject * >(this), -1);
1002             }
1003             child->checkFinalized();
1004             child->setProperty(Values[i], &localMods);
1005         }
1006         getNotificationRoot()->initBroadcaster(localMods.getRoot(), &bc);
1007     }
1008     bc.send();
1009 }
1010 
1011 css::uno::Sequence< css::uno::Any > Access::getHierarchicalPropertyValues(
1012     css::uno::Sequence< OUString > const & aHierarchicalPropertyNames)
1013 {
1014     assert(thisIs(IS_GROUP));
1015     osl::MutexGuard g(*lock_);
1016     css::uno::Sequence< css::uno::Any > vals(
1017         aHierarchicalPropertyNames.getLength());
1018     for (sal_Int32 i = 0; i < aHierarchicalPropertyNames.getLength(); ++i) {
1019         rtl::Reference< ChildAccess > child(
1020             getSubChild(aHierarchicalPropertyNames[i]));
1021         if (!child.is()) {
1022             throw css::lang::IllegalArgumentException(
1023                 ("configmgr getHierarchicalPropertyValues inappropriate"
1024                  " hierarchical property name"),
1025                 static_cast< cppu::OWeakObject * >(this), -1);
1026         }
1027         vals[i] = child->asValue();
1028     }
1029     return vals;
1030 }
1031 
1032 css::beans::Property Access::getPropertyByHierarchicalName(
1033     OUString const & aHierarchicalName)
1034 {
1035     assert(thisIs(IS_GROUP));
1036     osl::MutexGuard g(*lock_);
1037     rtl::Reference< ChildAccess > child(getSubChild(aHierarchicalName));
1038     if (!child.is()) {
1039         throw css::beans::UnknownPropertyException(
1040             aHierarchicalName, static_cast< cppu::OWeakObject * >(this));
1041     }
1042     return child->asProperty();
1043 }
1044 
1045 sal_Bool Access::hasPropertyByHierarchicalName(
1046     OUString const & aHierarchicalName)
1047 {
1048     assert(thisIs(IS_GROUP));
1049     osl::MutexGuard g(*lock_);
1050     return getSubChild(aHierarchicalName).is();
1051 }
1052 
1053 void Access::replaceByName(
1054     OUString const & aName, css::uno::Any const & aElement)
1055 {
1056     assert(thisIs(IS_UPDATE));
1057     Broadcaster bc;
1058     {
1059         osl::MutexGuard g(*lock_);
1060         checkLocalizedPropertyAccess();
1061         rtl::Reference< ChildAccess > child(getChild(aName));
1062         if (!child.is()) {
1063             throw css::container::NoSuchElementException(
1064                 aName, static_cast< cppu::OWeakObject * >(this));
1065         }
1066         child->checkFinalized();
1067         Modifications localMods;
1068         switch (getNode()->kind()) {
1069         case Node::KIND_LOCALIZED_PROPERTY:
1070         case Node::KIND_GROUP:
1071             child->setProperty(aElement, &localMods);
1072             break;
1073         case Node::KIND_SET:
1074             {
1075                 rtl::Reference< ChildAccess > freeAcc(
1076                     getFreeSetMember(aElement));
1077                 rtl::Reference< RootAccess > root(getRootAccess());
1078                 localMods.add(child->getRelativePath());
1079                 child->unbind(); // must not throw
1080                 freeAcc->bind(root, this, aName); // must not throw
1081                 markChildAsModified(freeAcc); //TODO: must not throw
1082             }
1083             break;
1084         default:
1085             assert(false); // this cannot happen
1086             break;
1087         }
1088         getNotificationRoot()->initBroadcaster(localMods.getRoot(), &bc);
1089     }
1090     bc.send();
1091 }
1092 
1093 void Access::insertByName(
1094     OUString const & aName, css::uno::Any const & aElement)
1095 {
1096     assert(thisIs(IS_EXTENSIBLE|IS_UPDATE));
1097     Broadcaster bc;
1098     {
1099         osl::MutexGuard g(*lock_);
1100         checkLocalizedPropertyAccess();
1101         checkFinalized();
1102         if (getChild(aName).is()) {
1103             throw css::container::ElementExistException(
1104                 aName, static_cast< cppu::OWeakObject * >(this));
1105         }
1106         Modifications localMods;
1107         switch (getNode()->kind()) {
1108         case Node::KIND_LOCALIZED_PROPERTY:
1109             if (!isValidName(aName, false)) {
1110                 throw css::lang::IllegalArgumentException(
1111                     aName, static_cast<cppu::OWeakObject *>(this), 0);
1112             }
1113             insertLocalizedValueChild(aName, aElement, &localMods);
1114             break;
1115         case Node::KIND_GROUP:
1116             {
1117                 if (!isValidName(aName, false)) {
1118                     throw css::lang::IllegalArgumentException(
1119                         aName, static_cast<cppu::OWeakObject *>(this), 0);
1120                 }
1121                 checkValue(aElement, TYPE_ANY, true);
1122                 rtl::Reference< ChildAccess > child(
1123                     new ChildAccess(
1124                         components_, getRootAccess(), this, aName,
1125                         new PropertyNode(
1126                             Data::NO_LAYER, TYPE_ANY, true, aElement, true)));
1127                 markChildAsModified(child);
1128                 localMods.add(child->getRelativePath());
1129             }
1130             break;
1131         case Node::KIND_SET:
1132             {
1133                 if (!isValidName(aName, true)) {
1134                     throw css::lang::IllegalArgumentException(
1135                         aName, static_cast<cppu::OWeakObject *>(this), 0);
1136                 }
1137                 rtl::Reference< ChildAccess > freeAcc(
1138                     getFreeSetMember(aElement));
1139                 freeAcc->bind(getRootAccess(), this, aName); // must not throw
1140                 markChildAsModified(freeAcc); //TODO: must not throw
1141                 localMods.add(freeAcc->getRelativePath());
1142             }
1143             break;
1144         default:
1145             assert(false); // this cannot happen
1146             break;
1147         }
1148         getNotificationRoot()->initBroadcaster(localMods.getRoot(), &bc);
1149     }
1150     bc.send();
1151 }
1152 
1153 void Access::removeByName(OUString const & aName)
1154 {
1155     assert(thisIs(IS_EXTENSIBLE|IS_UPDATE));
1156     Broadcaster bc;
1157     {
1158         osl::MutexGuard g(*lock_);
1159         checkLocalizedPropertyAccess();
1160         rtl::Reference< ChildAccess > child(getChild(aName));
1161         if (!child.is() || child->isFinalized() ||
1162             child->getNode()->getMandatory() != Data::NO_LAYER)
1163         {
1164             throw css::container::NoSuchElementException(
1165                 aName, static_cast< cppu::OWeakObject * >(this));
1166         }
1167         if (getNode()->kind() == Node::KIND_GROUP) {
1168             rtl::Reference< Node > p(child->getNode());
1169             if (p->kind() != Node::KIND_PROPERTY ||
1170                 !static_cast< PropertyNode * >(p.get())->isExtension())
1171             {
1172                 throw css::container::NoSuchElementException(
1173                     aName, static_cast< cppu::OWeakObject * >(this));
1174             }
1175         }
1176         Modifications localMods;
1177         localMods.add(child->getRelativePath());
1178         // unbind() modifies the parent chain that markChildAsModified() walks,
1179         // so order is important:
1180         markChildAsModified(child); //TODO: must not throw
1181         child->unbind();
1182         getNotificationRoot()->initBroadcaster(localMods.getRoot(), &bc);
1183     }
1184     bc.send();
1185 }
1186 
1187 css::uno::Reference< css::uno::XInterface > Access::createInstance()
1188 {
1189     assert(thisIs(IS_SET|IS_UPDATE));
1190     OUString tmplName(
1191         static_cast< SetNode * >(getNode().get())->getDefaultTemplateName());
1192     rtl::Reference< Node > tmpl(
1193         components_.getTemplate(tmplName));
1194     if (!tmpl.is()) {
1195         throw css::uno::Exception(
1196             "unknown template " + tmplName,
1197             static_cast< cppu::OWeakObject * >(this));
1198     }
1199     rtl::Reference< Node > node(tmpl->clone(true));
1200     node->setLayer(Data::NO_LAYER);
1201     return static_cast< cppu::OWeakObject * >(
1202         new ChildAccess(components_, getRootAccess(), node));
1203 }
1204 
1205 css::uno::Reference< css::uno::XInterface > Access::createInstanceWithArguments(
1206     css::uno::Sequence< css::uno::Any > const & aArguments)
1207 {
1208     assert(thisIs(IS_SET|IS_UPDATE));
1209     if (aArguments.getLength() != 0) {
1210         throw css::uno::Exception(
1211             ("configuration SimpleSetUpdate createInstanceWithArguments"
1212              " must not specify any arguments"),
1213             static_cast< cppu::OWeakObject * >(this));
1214     }
1215     return createInstance();
1216 }
1217 
1218 Access::Access(Components & components):
1219     components_(components), disposed_(false), lock_( lock() )
1220 {
1221 }
1222 
1223 Access::~Access() {}
1224 
1225 void Access::initDisposeBroadcaster(Broadcaster * broadcaster) {
1226     assert(broadcaster != nullptr);
1227     for (auto const& disposeListener : disposeListeners_)
1228     {
1229         broadcaster->addDisposeNotification(
1230             disposeListener,
1231             css::lang::EventObject(static_cast< cppu::OWeakObject * >(this)));
1232     }
1233     for (auto const& containerListener : containerListeners_)
1234     {
1235         broadcaster->addDisposeNotification(
1236             containerListener.get(),
1237             css::lang::EventObject(static_cast< cppu::OWeakObject * >(this)));
1238     }
1239     for (auto const& propertyChangeListener : propertyChangeListeners_)
1240     {
1241         for (auto const& propertyChangeListenerElement : propertyChangeListener.second)
1242         {
1243             broadcaster->addDisposeNotification(
1244                 propertyChangeListenerElement.get(),
1245                 css::lang::EventObject(
1246                     static_cast< cppu::OWeakObject * >(this)));
1247         }
1248     }
1249     for (auto const& vetoableChangeListener : vetoableChangeListeners_)
1250     {
1251         for (auto const& vetoableChangeListenerElement : vetoableChangeListener.second)
1252         {
1253             broadcaster->addDisposeNotification(
1254                 vetoableChangeListenerElement.get(),
1255                 css::lang::EventObject(
1256                     static_cast< cppu::OWeakObject * >(this)));
1257         }
1258     }
1259     for (auto const& propertiesChangeListener : propertiesChangeListeners_)
1260     {
1261         broadcaster->addDisposeNotification(
1262             propertiesChangeListener.get(),
1263             css::lang::EventObject(static_cast< cppu::OWeakObject * >(this)));
1264     }
1265     //TODO: iterate over children w/ listeners (incl. unmodified ones):
1266     for (ModifiedChildren::iterator i(modifiedChildren_.begin());
1267          i != modifiedChildren_.end(); ++i)
1268     {
1269         rtl::Reference< ChildAccess > child(getModifiedChild(i));
1270         if (child.is()) {
1271             child->initDisposeBroadcaster(broadcaster);
1272         }
1273     }
1274 }
1275 
1276 void Access::clearListeners() throw() {
1277     disposeListeners_.clear();
1278     containerListeners_.clear();
1279     propertyChangeListeners_.clear();
1280     vetoableChangeListeners_.clear();
1281     propertiesChangeListeners_.clear();
1282     //TODO: iterate over children w/ listeners (incl. unmodified ones):
1283     for (ModifiedChildren::iterator i(modifiedChildren_.begin());
1284          i != modifiedChildren_.end(); ++i)
1285     {
1286         rtl::Reference< ChildAccess > child(getModifiedChild(i));
1287         if (child.is()) {
1288             child->clearListeners();
1289         }
1290     }
1291 }
1292 
1293 css::uno::Any Access::queryInterface(css::uno::Type const & aType)
1294 {
1295     css::uno::Any res(OWeakObject::queryInterface(aType));
1296     if (res.hasValue()) {
1297         return res;
1298     }
1299     res = cppu::queryInterface(
1300         aType, static_cast< css::lang::XTypeProvider * >(this),
1301         static_cast< css::lang::XServiceInfo * >(this),
1302         static_cast< css::lang::XComponent * >(this),
1303         static_cast< css::container::XHierarchicalNameAccess * >(this),
1304         static_cast< css::container::XContainer * >(this),
1305         static_cast< css::beans::XExactName * >(this),
1306         static_cast< css::container::XHierarchicalName * >(this),
1307         static_cast< css::container::XNamed * >(this),
1308         static_cast< css::beans::XProperty * >(this),
1309         static_cast< css::container::XElementAccess * >(this),
1310         static_cast< css::container::XNameAccess * >(this));
1311     if (res.hasValue()) {
1312         return res;
1313     }
1314     if (getNode()->kind() == Node::KIND_GROUP) {
1315         res = cppu::queryInterface(
1316             aType, static_cast< css::beans::XPropertySetInfo * >(this),
1317             static_cast< css::beans::XPropertySet * >(this),
1318             static_cast< css::beans::XMultiPropertySet * >(this),
1319             static_cast< css::beans::XHierarchicalPropertySet * >(this),
1320             static_cast< css::beans::XMultiHierarchicalPropertySet * >(this),
1321             static_cast< css::beans::XHierarchicalPropertySetInfo * >(this));
1322         if (res.hasValue()) {
1323             return res;
1324         }
1325     }
1326     if (getRootAccess()->isUpdate()) {
1327         res = cppu::queryInterface(
1328             aType, static_cast< css::container::XNameReplace * >(this),
1329             static_cast< css::container::XHierarchicalNameReplace * >(this));
1330         if (res.hasValue()) {
1331             return res;
1332         }
1333         if (getNode()->kind() != Node::KIND_GROUP ||
1334             static_cast< GroupNode * >(getNode().get())->isExtensible())
1335         {
1336             res = cppu::queryInterface(
1337                 aType, static_cast< css::container::XNameContainer * >(this));
1338             if (res.hasValue()) {
1339                 return res;
1340             }
1341         }
1342         if (getNode()->kind() == Node::KIND_SET) {
1343             res = cppu::queryInterface(
1344                 aType, static_cast< css::lang::XSingleServiceFactory * >(this));
1345         }
1346     }
1347     return res;
1348 }
1349 
1350 
1351 void Access::checkLocalizedPropertyAccess() {
1352     if (getNode()->kind() == Node::KIND_LOCALIZED_PROPERTY &&
1353         !Components::allLocales(getRootAccess()->getLocale()))
1354     {
1355         throw css::uno::RuntimeException(
1356             "configmgr Access to specialized LocalizedPropertyNode",
1357             static_cast< cppu::OWeakObject * >(this));
1358     }
1359 }
1360 
1361 rtl::Reference< Node > Access::getParentNode() {
1362     rtl::Reference< Access > parent(getParentAccess());
1363     return parent.is() ? parent->getNode() : rtl::Reference< Node >();
1364 }
1365 
1366 rtl::Reference< ChildAccess > Access::getChild(OUString const & name) {
1367     OUString locale;
1368     if (getNode()->kind() == Node::KIND_LOCALIZED_PROPERTY
1369         && name.startsWith("*", &locale))
1370     {
1371         if (locale.startsWith("*")) {
1372             SAL_WARN(
1373                 "configmgr",
1374                 ("access best-matching localized property value via"
1375                  " \"*<locale>\" with <locale> \"")
1376                     << locale << "\" recursively starting with \"*\"");
1377             return getChild(locale);
1378         }
1379         SAL_WARN_IF(
1380             locale.isEmpty(), "configmgr",
1381             ("access best-matching localized property value via \"*<locale>\""
1382              " with empty <locale>; falling back to defaults"));
1383         if (!locale.isEmpty()) {
1384             // Find best match using an adaption of RFC 4647 lookup matching
1385             // rules, removing "-" or "_" delimited segments from the end:
1386             for (;;) {
1387                 rtl::Reference< ChildAccess > child(getChild(locale));
1388                 if (child.is()) {
1389                     return child;
1390                 }
1391                 sal_Int32 i = locale.getLength() - 1;
1392                 while (i > 0 && locale[i] != '-' && locale[i] != '_') {
1393                     --i;
1394                 }
1395                 if (i <= 0) {
1396                     break;
1397                 }
1398                 locale = locale.copy(0, i);
1399             }
1400             // As a workaround for broken xcu data that does not use shortest
1401             // xml:lang attributes, look for the first entry with the same first
1402             // segment as the requested language tag before falling back to
1403             // defaults (see fdo#33638):
1404             assert(
1405                 !locale.isEmpty() && locale.indexOf('-') == -1 &&
1406                 locale.indexOf('_') == -1);
1407             std::vector< rtl::Reference< ChildAccess > > children(
1408                 getAllChildren());
1409             for (auto const& child : children)
1410             {
1411                 OUString name2(child->getNameInternal());
1412                 if (name2.startsWith(locale) &&
1413                     (name2.getLength() == locale.getLength() ||
1414                      name2[locale.getLength()] == '-' ||
1415                      name2[locale.getLength()] == '_'))
1416                 {
1417                     return child;
1418                 }
1419             }
1420         }
1421         // Defaults are the "en-US" locale, the "en" locale, the empty string locale, the first child (if
1422         // any, and if the property is non-nillable), or a null ChildAccess, in that order:
1423         rtl::Reference< ChildAccess > child(getChild("en-US"));
1424         if (child.is()) {
1425             return child;
1426         }
1427         child = getChild("en");
1428         if (child.is()) {
1429             return child;
1430         }
1431         child = getChild("");
1432         if (child.is()) {
1433             return child;
1434         }
1435         if (!static_cast<LocalizedPropertyNode *>(getNode().get())->isNillable()) {
1436             std::vector< rtl::Reference< ChildAccess > > children(getAllChildren());
1437             if (!children.empty()) {
1438                 return children.front();
1439             }
1440         }
1441         return rtl::Reference< ChildAccess >();
1442     }
1443     ModifiedChildren::iterator i(modifiedChildren_.find(name));
1444     return i == modifiedChildren_.end()
1445         ? getUnmodifiedChild(name) : getModifiedChild(i);
1446 }
1447 
1448 std::vector< rtl::Reference< ChildAccess > > Access::getAllChildren() {
1449     std::vector< rtl::Reference< ChildAccess > > vec;
1450     NodeMap const & members = getNode()->getMembers();
1451     for (auto const& member : members)
1452     {
1453         if (modifiedChildren_.find(member.first) == modifiedChildren_.end()) {
1454             vec.push_back(getUnmodifiedChild(member.first));
1455             assert(vec.back().is());
1456         }
1457     }
1458     for (ModifiedChildren::iterator i(modifiedChildren_.begin());
1459          i != modifiedChildren_.end(); ++i)
1460     {
1461         rtl::Reference< ChildAccess > child(getModifiedChild(i));
1462         if (child.is()) {
1463             vec.push_back(child);
1464         }
1465     }
1466     return vec;
1467 }
1468 
1469 void Access::checkValue(css::uno::Any const & value, Type type, bool nillable) {
1470     bool ok;
1471     switch (type) {
1472     case TYPE_ERROR:
1473         ok = false;
1474         break;
1475     case TYPE_ANY:
1476         switch (getDynamicType(value)) {
1477         case TYPE_ERROR:
1478             ok = false;
1479             break;
1480         case TYPE_NIL:
1481             ok = nillable;
1482             break;
1483         default:
1484             ok = true;
1485             break;
1486         case TYPE_ANY:
1487             for (;;) std::abort(); // cannot happen
1488         }
1489         break;
1490     default:
1491         ok = value.hasValue() ? value.isExtractableTo(mapType(type)) : nillable;
1492         break;
1493     case TYPE_NIL:
1494         for (;;) std::abort(); // cannot happen
1495     }
1496     if (!ok) {
1497         throw css::lang::IllegalArgumentException(
1498             "configmgr inappropriate property value",
1499             static_cast< cppu::OWeakObject * >(this), -1);
1500     }
1501 }
1502 
1503 void Access::insertLocalizedValueChild(
1504     OUString const & name, css::uno::Any const & value,
1505     Modifications * localModifications)
1506 {
1507     assert(localModifications != nullptr);
1508     LocalizedPropertyNode * locprop = static_cast< LocalizedPropertyNode * >(
1509         getNode().get());
1510     checkValue(value, locprop->getStaticType(), locprop->isNillable());
1511     rtl::Reference< ChildAccess > child(
1512         new ChildAccess(
1513             components_, getRootAccess(), this, name,
1514             new LocalizedValueNode(Data::NO_LAYER, value)));
1515     markChildAsModified(child);
1516     localModifications->add(child->getRelativePath());
1517 }
1518 
1519 void Access::reportChildChanges(
1520     std::vector< css::util::ElementChange > * changes)
1521 {
1522     assert(changes != nullptr);
1523     for (ModifiedChildren::iterator i(modifiedChildren_.begin());
1524          i != modifiedChildren_.end(); ++i)
1525     {
1526         rtl::Reference< ChildAccess > child(getModifiedChild(i));
1527         if (child.is()) {
1528             child->reportChildChanges(changes);
1529             changes->push_back(css::util::ElementChange());
1530                 //TODO: changed value and/or inserted node
1531         } else {
1532             changes->push_back(css::util::ElementChange()); //TODO: removed node
1533         }
1534     }
1535 }
1536 
1537 void Access::commitChildChanges(
1538     bool valid, Modifications * globalModifications)
1539 {
1540     assert(globalModifications != nullptr);
1541     while (!modifiedChildren_.empty()) {
1542         bool childValid = valid;
1543         ModifiedChildren::iterator i(modifiedChildren_.begin());
1544         rtl::Reference< ChildAccess > child(getModifiedChild(i));
1545         if (child.is()) {
1546             childValid = childValid && !child->isFinalized();
1547             child->commitChanges(childValid, globalModifications);
1548                 //TODO: currently, this is called here for directly inserted
1549                 // children as well as for children whose sub-children were
1550                 // modified (and should never be called for directly removed
1551                 // children); clarify what exactly should happen here for
1552                 // directly inserted children
1553         }
1554         NodeMap & members = getNode()->getMembers();
1555         NodeMap::iterator j(members.find(i->first));
1556         if (child.is()) {
1557             // Inserted:
1558             if (j != members.end()) {
1559                 childValid = childValid &&
1560                     j->second->getFinalized() == Data::NO_LAYER;
1561                 if (childValid) {
1562                     child->getNode()->setMandatory(j->second->getMandatory());
1563                 }
1564             }
1565             if (childValid) {
1566                 members[i->first] = child->getNode();
1567             }
1568         } else {
1569             // Removed:
1570             childValid = childValid && j != members.end() &&
1571                 j->second->getFinalized() == Data::NO_LAYER &&
1572                 j->second->getMandatory() == Data::NO_LAYER;
1573             if (childValid) {
1574                 members.erase(j);
1575             }
1576         }
1577         if (childValid && i->second.directlyModified) {
1578             std::vector<OUString> path(getAbsolutePath());
1579             path.push_back(i->first);
1580             components_.addModification(path);
1581             globalModifications->add(path);
1582         }
1583         i->second.child->committed();
1584         modifiedChildren_.erase(i);
1585     }
1586 }
1587 
1588 void Access::initBroadcasterAndChanges(
1589     Modifications::Node const & modifications, Broadcaster * broadcaster,
1590     std::vector< css::util::ElementChange > * allChanges)
1591 {
1592     assert(broadcaster != nullptr);
1593     std::vector< css::beans::PropertyChangeEvent > propChanges;
1594     bool collectPropChanges = !propertiesChangeListeners_.empty();
1595     for (const auto & i : modifications.children)
1596     {
1597         rtl::Reference< ChildAccess > child(getChild(i.first));
1598         if (child.is()) {
1599             switch (child->getNode()->kind()) {
1600             case Node::KIND_LOCALIZED_PROPERTY:
1601                 if (!i.second.children.empty()) {
1602                     if (Components::allLocales(getRootAccess()->getLocale())) {
1603                         child->initBroadcasterAndChanges(
1604                             i.second, broadcaster, allChanges);
1605                             //TODO: if allChanges==0, recurse only into children
1606                             // w/ listeners
1607                     } else {
1608                         //TODO: filter child mods that are irrelevant for
1609                         // locale:
1610                         for (auto const& containerListener : containerListeners_)
1611                         {
1612                             broadcaster->
1613                                 addContainerElementReplacedNotification(
1614                                     containerListener,
1615                                     css::container::ContainerEvent(
1616                                         static_cast< cppu::OWeakObject * >(
1617                                             this),
1618                                         css::uno::Any(i.first),
1619                                         css::uno::Any(), css::uno::Any()));
1620                                 //TODO: non-void Element, ReplacedElement
1621                         }
1622                         PropertyChangeListeners::iterator j(
1623                             propertyChangeListeners_.find(i.first));
1624                         if (j != propertyChangeListeners_.end()) {
1625                             for (auto const& propertyChangeListenerElement : j->second)
1626                             {
1627                                 broadcaster->addPropertyChangeNotification(
1628                                     propertyChangeListenerElement,
1629                                     css::beans::PropertyChangeEvent(
1630                                         static_cast< cppu::OWeakObject * >(
1631                                             this),
1632                                         i.first, false, -1, css::uno::Any(),
1633                                         css::uno::Any()));
1634                             }
1635                         }
1636                         j = propertyChangeListeners_.find("");
1637                         if (j != propertyChangeListeners_.end()) {
1638                             for (auto const& propertyChangeListenerElement : j->second)
1639                             {
1640                                 broadcaster->addPropertyChangeNotification(
1641                                     propertyChangeListenerElement,
1642                                     css::beans::PropertyChangeEvent(
1643                                         static_cast< cppu::OWeakObject * >(
1644                                             this),
1645                                         i.first, false, -1, css::uno::Any(),
1646                                         css::uno::Any()));
1647                             }
1648                         }
1649                         if (allChanges != nullptr) {
1650                             allChanges->push_back(
1651                                 css::util::ElementChange(
1652                                     css::uno::Any(
1653                                         child->getRelativePathRepresentation()),
1654                                     css::uno::Any(), css::uno::Any()));
1655                                 //TODO: non-void Element, ReplacedElement
1656                         }
1657                         if (collectPropChanges) {
1658                             propChanges.emplace_back(
1659                                     static_cast< cppu::OWeakObject * >(this),
1660                                     i.first, false, -1, css::uno::Any(),
1661                                     css::uno::Any());
1662                         }
1663                     }
1664                 }
1665                 // else: spurious Modifications::Node not representing a change
1666                 break;
1667             case Node::KIND_LOCALIZED_VALUE:
1668                 assert(Components::allLocales(getRootAccess()->getLocale()));
1669                 for (auto const& containerListener : containerListeners_)
1670                 {
1671                     broadcaster->addContainerElementReplacedNotification(
1672                         containerListener,
1673                         css::container::ContainerEvent(
1674                             static_cast< cppu::OWeakObject * >(this),
1675                             css::uno::Any(i.first), child->asValue(),
1676                             css::uno::Any()));
1677                         //TODO: distinguish add/modify; non-void ReplacedElement
1678                 }
1679                 if (allChanges != nullptr) {
1680                     allChanges->push_back(
1681                         css::util::ElementChange(
1682                             css::uno::Any(
1683                                 child->getRelativePathRepresentation()),
1684                             child->asValue(), css::uno::Any()));
1685                         //TODO: non-void ReplacedElement
1686                 }
1687                 assert(!collectPropChanges);
1688                 break;
1689             case Node::KIND_PROPERTY:
1690                 {
1691                     for (auto const& containerListener : containerListeners_)
1692                     {
1693                         broadcaster->addContainerElementReplacedNotification(
1694                             containerListener,
1695                             css::container::ContainerEvent(
1696                                 static_cast< cppu::OWeakObject * >(this),
1697                                 css::uno::Any(i.first), child->asValue(),
1698                                 css::uno::Any()));
1699                             //TODO: distinguish add/remove/modify; non-void
1700                             // ReplacedElement
1701                     }
1702                     PropertyChangeListeners::iterator j(
1703                         propertyChangeListeners_.find(i.first));
1704                     if (j != propertyChangeListeners_.end()) {
1705                         for (auto const& propertyChangeListenerElement : j->second)
1706                         {
1707                             broadcaster->addPropertyChangeNotification(
1708                                 propertyChangeListenerElement,
1709                                 css::beans::PropertyChangeEvent(
1710                                     static_cast< cppu::OWeakObject * >(this),
1711                                     i.first, false, -1, css::uno::Any(),
1712                                     css::uno::Any()));
1713                         }
1714                     }
1715                     j = propertyChangeListeners_.find("");
1716                     if (j != propertyChangeListeners_.end()) {
1717                         for (auto const& propertyChangeListenerElement : j->second)
1718                         {
1719                             broadcaster->addPropertyChangeNotification(
1720                                 propertyChangeListenerElement,
1721                                 css::beans::PropertyChangeEvent(
1722                                     static_cast< cppu::OWeakObject * >(this),
1723                                     i.first, false, -1, css::uno::Any(),
1724                                     css::uno::Any()));
1725                         }
1726                     }
1727                     if (allChanges != nullptr) {
1728                         allChanges->push_back(
1729                             css::util::ElementChange(
1730                                 css::uno::Any(
1731                                     child->getRelativePathRepresentation()),
1732                                 child->asValue(), css::uno::Any()));
1733                             //TODO: non-void ReplacedElement
1734                     }
1735                     if (collectPropChanges) {
1736                         propChanges.emplace_back(
1737                                 static_cast< cppu::OWeakObject * >(this),
1738                                 i.first, false, -1, css::uno::Any(),
1739                                 css::uno::Any());
1740                     }
1741                 }
1742                 break;
1743             case Node::KIND_GROUP:
1744             case Node::KIND_SET:
1745                 if (i.second.children.empty()) {
1746                     if (!child->getNode()->getTemplateName().isEmpty()) {
1747                         for (auto const& containerListener : containerListeners_)
1748                         {
1749                             broadcaster->
1750                                 addContainerElementInsertedNotification(
1751                                     containerListener,
1752                                     css::container::ContainerEvent(
1753                                         static_cast< cppu::OWeakObject * >(
1754                                             this),
1755                                         css::uno::Any(i.first),
1756                                         child->asValue(), css::uno::Any()));
1757                         }
1758                         if (allChanges != nullptr) {
1759                             allChanges->push_back(
1760                                 css::util::ElementChange(
1761                                     css::uno::Any(
1762                                         child->getRelativePathRepresentation()),
1763                                     css::uno::Any(), css::uno::Any()));
1764                                 //TODO: non-void Element, ReplacedElement
1765                         }
1766                     }
1767                     // else: spurious Modifications::Node not representing a
1768                     // change
1769                 } else {
1770                     child->initBroadcasterAndChanges(
1771                         i.second, broadcaster, allChanges);
1772                         //TODO: if allChanges==0, recurse only into children w/
1773                         // listeners
1774                 }
1775                 break;
1776             case Node::KIND_ROOT:
1777                 assert(false); // this cannot happen
1778                 break;
1779             }
1780         } else {
1781             switch (getNode()->kind()) {
1782             case Node::KIND_LOCALIZED_PROPERTY:
1783                 // Removed localized property value:
1784                 assert(Components::allLocales(getRootAccess()->getLocale()));
1785                 for (auto const& containerListener : containerListeners_)
1786                 {
1787                     broadcaster->addContainerElementRemovedNotification(
1788                         containerListener,
1789                         css::container::ContainerEvent(
1790                             static_cast< cppu::OWeakObject * >(this),
1791                             css::uno::Any(i.first), css::uno::Any(),
1792                             css::uno::Any()));
1793                         //TODO: non-void ReplacedElement
1794                 }
1795                 if (allChanges != nullptr) {
1796                     OUStringBuffer path(getRelativePathRepresentation());
1797                     if (!path.isEmpty()) {
1798                         path.append('/');
1799                     }
1800                     path.append(Data::createSegment("*", i.first));
1801                     allChanges->push_back(
1802                         css::util::ElementChange(
1803                             css::uno::Any(path.makeStringAndClear()),
1804                             css::uno::Any(), css::uno::Any()));
1805                         //TODO: non-void ReplacedElement
1806                 }
1807                 assert(!collectPropChanges);
1808                 break;
1809             case Node::KIND_GROUP:
1810                 {
1811                     // Removed (non-localized) extension property:
1812                     for (auto const& containerListener : containerListeners_)
1813                     {
1814                         broadcaster->addContainerElementRemovedNotification(
1815                             containerListener,
1816                             css::container::ContainerEvent(
1817                                 static_cast< cppu::OWeakObject * >(this),
1818                                 css::uno::Any(i.first), css::uno::Any(),
1819                                 css::uno::Any()));
1820                             //TODO: non-void ReplacedElement
1821                     }
1822                     PropertyChangeListeners::iterator j(
1823                         propertyChangeListeners_.find(i.first));
1824                     if (j != propertyChangeListeners_.end()) {
1825                         for (auto const& propertyChangeListenerElement : j->second)
1826                         {
1827                             broadcaster->addPropertyChangeNotification(
1828                                 propertyChangeListenerElement,
1829                                 css::beans::PropertyChangeEvent(
1830                                     static_cast< cppu::OWeakObject * >(this),
1831                                     i.first, false, -1, css::uno::Any(),
1832                                     css::uno::Any()));
1833                         }
1834                     }
1835                     j = propertyChangeListeners_.find("");
1836                     if (j != propertyChangeListeners_.end()) {
1837                         for (auto const& propertyChangeListenerElement : j->second)
1838                         {
1839                             broadcaster->addPropertyChangeNotification(
1840                                 propertyChangeListenerElement,
1841                                 css::beans::PropertyChangeEvent(
1842                                     static_cast< cppu::OWeakObject * >(this),
1843                                     i.first, false, -1, css::uno::Any(),
1844                                     css::uno::Any()));
1845                         }
1846                     }
1847                     if (allChanges != nullptr) {
1848                         OUStringBuffer path(
1849                             getRelativePathRepresentation());
1850                         if (!path.isEmpty()) {
1851                             path.append('/');
1852                         }
1853                         path.append(i.first);
1854                         allChanges->push_back(
1855                             css::util::ElementChange(
1856                                 css::uno::Any(path.makeStringAndClear()),
1857                                 css::uno::Any(), css::uno::Any()));
1858                             //TODO: non-void ReplacedElement
1859                     }
1860                     if (collectPropChanges) {
1861                         propChanges.emplace_back(
1862                                 static_cast< cppu::OWeakObject * >(this),
1863                                 i.first, false, -1, css::uno::Any(),
1864                                 css::uno::Any());
1865                     }
1866                 }
1867                 break;
1868             case Node::KIND_SET:
1869                 // Removed set member:
1870                 if (i.second.children.empty()) {
1871                     for (auto const& containerListener : containerListeners_)
1872                     {
1873                         broadcaster->addContainerElementRemovedNotification(
1874                             containerListener,
1875                             css::container::ContainerEvent(
1876                                 static_cast< cppu::OWeakObject * >(this),
1877                                 css::uno::Any(i.first),
1878                                 css::uno::Any(), css::uno::Any()));
1879                             //TODO: non-void ReplacedElement
1880                     }
1881                     if (allChanges != nullptr) {
1882                         OUStringBuffer path(
1883                             getRelativePathRepresentation());
1884                         if (!path.isEmpty()) {
1885                             path.append('/');
1886                         }
1887                         path.append(Data::createSegment("*", i.first));
1888                         allChanges->push_back(
1889                             css::util::ElementChange(
1890                                 css::uno::Any(path.makeStringAndClear()),
1891                                 css::uno::Any(), css::uno::Any()));
1892                             //TODO: non-void ReplacedElement
1893                     }
1894                 }
1895                 // else: spurious Modifications::Node not representing a change
1896                 break;
1897             default:
1898                 assert(false); // this cannot happen
1899                 break;
1900             }
1901         }
1902     }
1903     if (!propChanges.empty()) {
1904         css::uno::Sequence< css::beans::PropertyChangeEvent > seq(
1905             comphelper::containerToSequence(propChanges));
1906         for (auto const& propertyChangeListener : propertiesChangeListeners_)
1907         {
1908             broadcaster->addPropertiesChangeNotification(propertyChangeListener, seq);
1909         }
1910     }
1911 }
1912 
1913 
1914 Access::ModifiedChild::ModifiedChild():
1915     directlyModified(false)
1916 {}
1917 
1918 Access::ModifiedChild::ModifiedChild(
1919     rtl::Reference< ChildAccess > const & theChild, bool theDirectlyModified):
1920     child(theChild), directlyModified(theDirectlyModified)
1921 {}
1922 
1923 rtl::Reference< ChildAccess > Access::getModifiedChild(
1924     ModifiedChildren::iterator const & childIterator)
1925 {
1926     return (childIterator->second.child->getParentAccess() == this &&
1927             (childIterator->second.child->getNameInternal() ==
1928              childIterator->first))
1929         ? childIterator->second.child : rtl::Reference< ChildAccess >();
1930 }
1931 
1932 rtl::Reference< ChildAccess > Access::createUnmodifiedChild(
1933                 const OUString &name, const rtl::Reference< Node > &node)
1934 {
1935     rtl::Reference< ChildAccess > child(
1936         new ChildAccess(components_, getRootAccess(), this, name, node));
1937     cachedChildren_[name] = child.get();
1938     return child;
1939 }
1940 
1941 rtl::Reference< ChildAccess > Access::getUnmodifiedChild(
1942     OUString const & name)
1943 {
1944     assert(modifiedChildren_.find(name) == modifiedChildren_.end());
1945     rtl::Reference< Node > node(getNode()->getMember(name));
1946     if (!node.is()) {
1947         return rtl::Reference< ChildAccess >();
1948     }
1949     WeakChildMap::iterator i(cachedChildren_.find(name));
1950     if (i != cachedChildren_.end()) {
1951         rtl::Reference< ChildAccess > child;
1952         if (i->second->acquireCounting() > 1) {
1953             child.set(i->second); // must not throw
1954         }
1955         i->second->releaseNondeleting();
1956         if (child.is()) {
1957             child->setNode(node);
1958             return child;
1959         }
1960     }
1961     return createUnmodifiedChild(name,node);
1962 }
1963 
1964 rtl::Reference< ChildAccess > Access::getSubChild(OUString const & path) {
1965     sal_Int32 i = 0;
1966     // For backwards compatibility, allow absolute paths where meaningful:
1967     if( path.startsWith("/") ) {
1968         ++i;
1969         if (!getRootAccess().is()) {
1970             return rtl::Reference< ChildAccess >();
1971         }
1972         std::vector<OUString> abs(getAbsolutePath());
1973         for (auto const& elem : abs)
1974         {
1975             OUString name1;
1976             bool setElement1;
1977             OUString templateName1;
1978             i = Data::parseSegment(
1979                 path, i, &name1, &setElement1, &templateName1);
1980             if (i == -1 || (i != path.getLength() && path[i] != '/')) {
1981                 return rtl::Reference< ChildAccess >();
1982             }
1983             OUString name2;
1984             bool setElement2;
1985             OUString templateName2;
1986             Data::parseSegment(elem, 0, &name2, &setElement2, &templateName2);
1987             if (name1 != name2 || setElement1 != setElement2 ||
1988                 (setElement1 &&
1989                  !Data::equalTemplateNames(templateName1, templateName2)))
1990             {
1991                 return rtl::Reference< ChildAccess >();
1992             }
1993             if (i != path.getLength()) {
1994                 ++i;
1995             }
1996         }
1997     }
1998     for (rtl::Reference< Access > parent(this);;) {
1999         OUString name;
2000         bool setElement;
2001         OUString templateName;
2002         i = Data::parseSegment(path, i, &name, &setElement, &templateName);
2003         if (i == -1 || (i != path.getLength() && path[i] != '/')) {
2004             return rtl::Reference< ChildAccess >();
2005         }
2006         rtl::Reference< ChildAccess > child(parent->getChild(name));
2007         if (!child.is()) {
2008             return rtl::Reference< ChildAccess >();
2009         }
2010         if (setElement) {
2011             rtl::Reference< Node > p(parent->getNode());
2012             switch (p->kind()) {
2013             case Node::KIND_LOCALIZED_PROPERTY:
2014                 if (!Components::allLocales(getRootAccess()->getLocale()) ||
2015                     !templateName.isEmpty())
2016                 {
2017                     return rtl::Reference< ChildAccess >();
2018                 }
2019                 break;
2020             case Node::KIND_SET:
2021                 if (!templateName.isEmpty() &&
2022                     !static_cast< SetNode * >(p.get())->isValidTemplate(
2023                         templateName))
2024                 {
2025                     return rtl::Reference< ChildAccess >();
2026                 }
2027                 break;
2028             default:
2029                 return rtl::Reference< ChildAccess >();
2030             }
2031         }
2032         // For backwards compatibility, ignore a final slash after non-value
2033         // nodes:
2034         if (child->isValue()) {
2035             return i == path.getLength()
2036                 ? child : rtl::Reference< ChildAccess >();
2037         } else if (i >= path.getLength() - 1) {
2038             return child;
2039         }
2040         ++i;
2041         parent = child.get();
2042     }
2043 }
2044 
2045 bool Access::setChildProperty(
2046     OUString const & name, css::uno::Any const & value,
2047     Modifications * localModifications)
2048 {
2049     assert(localModifications != nullptr);
2050     rtl::Reference< ChildAccess > child(getChild(name));
2051     if (!child.is()) {
2052         return false;
2053     }
2054     child->checkFinalized();
2055     child->setProperty(value, localModifications);
2056     return true;
2057 }
2058 
2059 css::beans::Property Access::asProperty() {
2060     css::uno::Type type;
2061     bool nillable;
2062     bool removable;
2063     rtl::Reference< Node > p(getNode());
2064     switch (p->kind()) {
2065     case Node::KIND_PROPERTY:
2066         {
2067             PropertyNode * prop = static_cast< PropertyNode * >(p.get());
2068             type = mapType(prop->getStaticType());
2069             nillable = prop->isNillable();
2070             removable = prop->isExtension();
2071         }
2072         break;
2073     case Node::KIND_LOCALIZED_PROPERTY:
2074         {
2075             LocalizedPropertyNode * locprop =
2076                 static_cast< LocalizedPropertyNode *>(p.get());
2077             if (Components::allLocales(getRootAccess()->getLocale())) {
2078                 type = cppu::UnoType< css::uno::XInterface >::get();
2079                     //TODO: correct?
2080                 removable = false;
2081             } else {
2082                 type = mapType(locprop->getStaticType());
2083                 removable = false; //TODO ???
2084             }
2085             nillable = locprop->isNillable();
2086         }
2087         break;
2088     case Node::KIND_LOCALIZED_VALUE:
2089         {
2090             LocalizedPropertyNode * locprop =
2091                 static_cast< LocalizedPropertyNode * >(getParentNode().get());
2092             type = mapType(locprop->getStaticType());
2093             nillable = locprop->isNillable();
2094             removable = false; //TODO ???
2095         }
2096         break;
2097     default:
2098         type = cppu::UnoType< css::uno::XInterface >::get(); //TODO: correct?
2099         nillable = false;
2100         rtl::Reference< Node > parent(getParentNode());
2101         removable = parent.is() && parent->kind() == Node::KIND_SET;
2102         break;
2103     }
2104     return css::beans::Property(
2105         getNameInternal(), -1, type,
2106         (css::beans::PropertyAttribute::BOUND | //TODO: correct for group/set?
2107          css::beans::PropertyAttribute::CONSTRAINED |
2108          (nillable ? css::beans::PropertyAttribute::MAYBEVOID : 0) |
2109          (getRootAccess()->isUpdate() && removable
2110           ? css::beans::PropertyAttribute::REMOVABLE : 0) |
2111          (!getRootAccess()->isUpdate() || p->getFinalized() != Data::NO_LAYER
2112           ? css::beans::PropertyAttribute::READONLY : 0))); //TODO: MAYBEDEFAULT
2113 }
2114 
2115 void Access::checkFinalized() {
2116     if (isFinalized()) {
2117         throw css::lang::IllegalArgumentException(
2118             "configmgr modification of finalized item",
2119             static_cast< cppu::OWeakObject * >(this), -1);
2120     }
2121 }
2122 
2123 void Access::checkKnownProperty(OUString const & descriptor) {
2124     if (descriptor.isEmpty()) {
2125         return;
2126     }
2127     rtl::Reference< ChildAccess > child(getChild(descriptor));
2128     if (child.is()) {
2129         switch (child->getNode()->kind()) {
2130         case Node::KIND_PROPERTY:
2131             return;
2132         case Node::KIND_LOCALIZED_PROPERTY:
2133             if (!Components::allLocales(getRootAccess()->getLocale())) {
2134                 return;
2135             }
2136             break;
2137         case Node::KIND_LOCALIZED_VALUE:
2138             if (Components::allLocales(getRootAccess()->getLocale())) {
2139                 return;
2140             }
2141             break;
2142         default:
2143             break;
2144         }
2145     }
2146     throw css::beans::UnknownPropertyException(
2147         descriptor, static_cast< cppu::OWeakObject * >(this));
2148 }
2149 
2150 rtl::Reference< ChildAccess > Access::getFreeSetMember(
2151     css::uno::Any const & value)
2152 {
2153     rtl::Reference< ChildAccess > freeAcc;
2154     css::uno::Reference< css::lang::XUnoTunnel > tunnel;
2155     value >>= tunnel;
2156     if (tunnel.is()) {
2157         freeAcc.set(
2158             reinterpret_cast< ChildAccess * >(
2159                 tunnel->getSomething(ChildAccess::getTunnelId())));
2160     }
2161     if (!freeAcc.is() || freeAcc->getParentAccess().is() ||
2162         (freeAcc->isInTransaction() &&
2163          freeAcc->getRootAccess() != getRootAccess()))
2164     {
2165         throw css::lang::IllegalArgumentException(
2166             "configmgr inappropriate set element",
2167             static_cast< cppu::OWeakObject * >(this), 1);
2168     }
2169     assert(dynamic_cast< SetNode * >(getNode().get()) != nullptr);
2170     if (!static_cast< SetNode * >(getNode().get())->isValidTemplate(
2171             freeAcc->getNode()->getTemplateName()))
2172     {
2173         throw css::lang::IllegalArgumentException(
2174             "configmgr inappropriate set element",
2175             static_cast< cppu::OWeakObject * >(this), 1);
2176     }
2177     return freeAcc;
2178 }
2179 
2180 rtl::Reference< Access > Access::getNotificationRoot() {
2181     for (rtl::Reference< Access > p(this);;) {
2182         rtl::Reference< Access > parent(p->getParentAccess());
2183         if (!parent.is()) {
2184             return p;
2185         }
2186         p = parent;
2187     }
2188 }
2189 
2190 #if !defined NDEBUG
2191 bool Access::thisIs(int what) {
2192     osl::MutexGuard g(*lock_);
2193     rtl::Reference< Node > p(getNode());
2194     Node::Kind k(p->kind());
2195     return (k != Node::KIND_PROPERTY && k != Node::KIND_LOCALIZED_VALUE &&
2196         ((what & IS_GROUP) == 0 || k == Node::KIND_GROUP) &&
2197         ((what & IS_SET) == 0 || k == Node::KIND_SET) &&
2198         ((what & IS_EXTENSIBLE) == 0 || k != Node::KIND_GROUP ||
2199          static_cast< GroupNode * >(p.get())->isExtensible()) &&
2200         ((what & IS_GROUP_MEMBER) == 0 ||
2201          getParentNode()->kind() == Node::KIND_GROUP)) ||
2202         ((what & IS_SET_MEMBER) == 0 ||
2203          getParentNode()->kind() == Node::KIND_SET) ||
2204         ((what & IS_UPDATE) == 0 || getRootAccess()->isUpdate());
2205 }
2206 #endif
2207 
2208 }
2209 
2210 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2211