summaryrefslogtreecommitdiffstats
path: root/configmgr/source/access.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'configmgr/source/access.cxx')
-rw-r--r--configmgr/source/access.cxx2212
1 files changed, 2212 insertions, 0 deletions
diff --git a/configmgr/source/access.cxx b/configmgr/source/access.cxx
new file mode 100644
index 000000000..865d38300
--- /dev/null
+++ b/configmgr/source/access.cxx
@@ -0,0 +1,2212 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <cassert>
+#include <cstdlib>
+#include <utility>
+#include <vector>
+
+#include <com/sun/star/beans/Property.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/PropertyChangeEvent.hpp>
+#include <com/sun/star/beans/UnknownPropertyException.hpp>
+#include <com/sun/star/beans/XExactName.hpp>
+#include <com/sun/star/beans/XHierarchicalPropertySet.hpp>
+#include <com/sun/star/beans/XHierarchicalPropertySetInfo.hpp>
+#include <com/sun/star/beans/XMultiHierarchicalPropertySet.hpp>
+#include <com/sun/star/beans/XMultiPropertySet.hpp>
+#include <com/sun/star/beans/XPropertiesChangeListener.hpp>
+#include <com/sun/star/beans/XProperty.hpp>
+#include <com/sun/star/beans/XPropertyChangeListener.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XPropertySetInfo.hpp>
+#include <com/sun/star/beans/XVetoableChangeListener.hpp>
+#include <com/sun/star/container/ContainerEvent.hpp>
+#include <com/sun/star/container/NoSuchElementException.hpp>
+#include <com/sun/star/container/XContainer.hpp>
+#include <com/sun/star/container/XContainerListener.hpp>
+#include <com/sun/star/container/XElementAccess.hpp>
+#include <com/sun/star/container/XHierarchicalName.hpp>
+#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
+#include <com/sun/star/container/XHierarchicalNameReplace.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/EventObject.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/lang/XEventListener.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <com/sun/star/lang/XTypeProvider.hpp>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/uno/Type.hxx>
+#include <com/sun/star/uno/XInterface.hpp>
+#include <com/sun/star/uno/XWeak.hpp>
+#include <com/sun/star/util/ElementChange.hpp>
+#include <comphelper/sequence.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/lok.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <cppu/unotype.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/weak.hxx>
+#include <osl/interlck.h>
+#include <osl/mutex.hxx>
+#include <rtl/character.hxx>
+#include <rtl/ref.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <sal/types.h>
+
+#include "access.hxx"
+#include "broadcaster.hxx"
+#include "childaccess.hxx"
+#include "components.hxx"
+#include "data.hxx"
+#include "groupnode.hxx"
+#include "localizedpropertynode.hxx"
+#include "localizedvaluenode.hxx"
+#include "lock.hxx"
+#include "modifications.hxx"
+#include "node.hxx"
+#include "nodemap.hxx"
+#include "propertynode.hxx"
+#include "rootaccess.hxx"
+#include "setnode.hxx"
+#include "type.hxx"
+
+namespace configmgr {
+
+namespace {
+
+// Conservatively forbid what is either not an XML Char (including lone
+// surrogates, even though they should not appear in well-formed UNO OUString
+// instances anyway), or is a slash (as it causes problems in path syntax):
+bool isValidName(OUString const & name, bool setMember) {
+ for (sal_Int32 i = 0; i != name.getLength();) {
+ sal_uInt32 c = name.iterateCodePoints(&i);
+ if ((c < 0x20 && !(c == 0x09 || c == 0x0A || c == 0x0D))
+ || rtl::isSurrogate(c) || c == 0xFFFE || c == 0xFFFF
+ || (!setMember && c == '/'))
+ {
+ return false;
+ }
+ }
+ return !name.isEmpty();
+}
+
+}
+
+oslInterlockedCount Access::acquireCounting() {
+ return osl_atomic_increment(&m_refCount);
+}
+
+void Access::releaseNondeleting() {
+ osl_atomic_decrement(&m_refCount);
+}
+
+bool Access::isValue() {
+ rtl::Reference< Node > p(getNode());
+ switch (p->kind()) {
+ case Node::KIND_PROPERTY:
+ case Node::KIND_LOCALIZED_VALUE:
+ return true;
+ case Node::KIND_LOCALIZED_PROPERTY:
+ return !Components::allLocales(getRootAccess()->getLocale());
+ default:
+ return false;
+ }
+}
+
+void Access::markChildAsModified(rtl::Reference< ChildAccess > const & child) {
+ assert(child.is() && child->getParentAccess() == this);
+ modifiedChildren_[child->getNameInternal()] = ModifiedChild(child, true);
+ for (rtl::Reference< Access > p(this);;) {
+ rtl::Reference< Access > parent(p->getParentAccess());
+ if (!parent.is()) {
+ break;
+ }
+ assert(dynamic_cast< ChildAccess * >(p.get()) != nullptr);
+ parent->modifiedChildren_.emplace(
+ p->getNameInternal(),
+ ModifiedChild(static_cast< ChildAccess * >(p.get()), false));
+ p = parent;
+ }
+}
+
+void Access::releaseChild(OUString const & name) {
+ cachedChildren_.erase(name);
+}
+
+void Access::initBroadcaster(
+ Modifications::Node const & modifications, Broadcaster * broadcaster)
+{
+ initBroadcasterAndChanges(modifications, broadcaster, nullptr);
+}
+
+css::uno::Sequence< css::uno::Type > Access::getTypes()
+{
+ assert(thisIs(IS_ANY));
+ osl::MutexGuard g(*lock_);
+ checkLocalizedPropertyAccess();
+ std::vector< css::uno::Type > types { cppu::UnoType< css::uno::XInterface >::get(),
+ cppu::UnoType< css::uno::XWeak >::get(),
+ cppu::UnoType< css::lang::XTypeProvider >::get(),
+ cppu::UnoType< css::lang::XServiceInfo >::get(),
+ cppu::UnoType< css::lang::XComponent >::get(),
+ cppu::UnoType< css::container::XContainer >::get(),
+ cppu::UnoType< css::beans::XExactName >::get(),
+ cppu::UnoType< css::container::XHierarchicalName >::get(),
+ cppu::UnoType< css::container::XNamed >::get(),
+ cppu::UnoType< css::beans::XProperty >::get(),
+ cppu::UnoType< css::container::XElementAccess >::get(),
+ cppu::UnoType< css::container::XNameAccess >::get()
+ };
+ if (getNode()->kind() == Node::KIND_GROUP) {
+ types.push_back(cppu::UnoType< css::beans::XPropertySetInfo >::get());
+ types.push_back(cppu::UnoType< css::beans::XPropertySet >::get());
+ types.push_back(cppu::UnoType< css::beans::XMultiPropertySet >::get());
+ types.push_back(
+ cppu::UnoType< css::beans::XHierarchicalPropertySet >::get());
+ types.push_back(
+ cppu::UnoType< css::beans::XMultiHierarchicalPropertySet >::get());
+ types.push_back(
+ cppu::UnoType< css::beans::XHierarchicalPropertySetInfo >::get());
+ }
+ if (getRootAccess()->isUpdate()) {
+ types.push_back(cppu::UnoType< css::container::XNameReplace >::get());
+ types.push_back(
+ cppu::UnoType< css::container::XHierarchicalNameReplace >::get());
+ if (getNode()->kind() != Node::KIND_GROUP ||
+ static_cast< GroupNode * >(getNode().get())->isExtensible())
+ {
+ types.push_back(
+ cppu::UnoType< css::container::XNameContainer >::get());
+ }
+ if (getNode()->kind() == Node::KIND_SET) {
+ types.push_back(
+ cppu::UnoType< css::lang::XSingleServiceFactory >::get());
+ }
+ } else {
+ types.push_back(
+ cppu::UnoType< css::container::XHierarchicalNameAccess >::get());
+ }
+ addTypes(&types);
+ return comphelper::containerToSequence(types);
+}
+
+css::uno::Sequence< sal_Int8 > Access::getImplementationId()
+{
+ assert(thisIs(IS_ANY));
+ osl::MutexGuard g(*lock_);
+ checkLocalizedPropertyAccess();
+ return css::uno::Sequence< sal_Int8 >();
+}
+
+OUString Access::getImplementationName()
+{
+ assert(thisIs(IS_ANY));
+ osl::MutexGuard g(*lock_);
+ checkLocalizedPropertyAccess();
+ return "org.openoffice-configmgr::Access";
+}
+
+sal_Bool Access::supportsService(OUString const & ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence< OUString > Access::getSupportedServiceNames()
+{
+ assert(thisIs(IS_ANY));
+ osl::MutexGuard g(*lock_);
+ checkLocalizedPropertyAccess();
+ std::vector<OUString> services;
+ services.emplace_back("com.sun.star.configuration.ConfigurationAccess");
+ if (getRootAccess()->isUpdate()) {
+ services.emplace_back("com.sun.star.configuration.ConfigurationUpdateAccess");
+ }
+ services.emplace_back("com.sun.star.configuration.HierarchyAccess");
+ services.emplace_back("com.sun.star.configuration.HierarchyElement");
+ if (getNode()->kind() == Node::KIND_GROUP) {
+ services.emplace_back("com.sun.star.configuration.GroupAccess");
+ services.emplace_back("com.sun.star.configuration.PropertyHierarchy");
+ if (getRootAccess()->isUpdate()) {
+ services.emplace_back("com.sun.star.configuration.GroupUpdate");
+ }
+ } else {
+ services.emplace_back("com.sun.star.configuration.SetAccess");
+ services.emplace_back("com.sun.star.configuration.SimpleSetAccess");
+ if (getRootAccess()->isUpdate()) {
+ services.emplace_back("com.sun.star.configuration.SetUpdate");
+ services.emplace_back("com.sun.star.configuration.SimpleSetUpdate");
+ }
+ }
+ addSupportedServiceNames(&services);
+ return comphelper::containerToSequence(services);
+}
+
+void Access::dispose() {
+ assert(thisIs(IS_ANY));
+ Broadcaster bc;
+ {
+ osl::MutexGuard g(*lock_);
+ checkLocalizedPropertyAccess();
+ if (getParentAccess().is()) {
+ throw css::uno::RuntimeException(
+ "configmgr dispose inappropriate Access",
+ static_cast< cppu::OWeakObject * >(this));
+ }
+ if (disposed_) {
+ return;
+ }
+ initDisposeBroadcaster(&bc);
+ clearListeners();
+ disposed_ = true;
+ }
+ bc.send();
+}
+
+void Access::addEventListener(
+ css::uno::Reference< css::lang::XEventListener > const & xListener)
+{
+ assert(thisIs(IS_ANY));
+ {
+ osl::MutexGuard g(*lock_);
+ checkLocalizedPropertyAccess();
+ if (!xListener.is()) {
+ throw css::uno::RuntimeException(
+ "null listener", static_cast< cppu::OWeakObject * >(this));
+ }
+ if (!disposed_) {
+ disposeListeners_.insert(xListener);
+ return;
+ }
+ }
+ try {
+ xListener->disposing(
+ css::lang::EventObject(static_cast< cppu::OWeakObject * >(this)));
+ } catch (css::lang::DisposedException &) {}
+}
+
+void Access::removeEventListener(
+ css::uno::Reference< css::lang::XEventListener > const & aListener)
+{
+ assert(thisIs(IS_ANY));
+ osl::MutexGuard g(*lock_);
+ checkLocalizedPropertyAccess();
+ DisposeListeners::iterator i(disposeListeners_.find(aListener));
+ if (i != disposeListeners_.end()) {
+ disposeListeners_.erase(i);
+ }
+}
+
+css::uno::Type Access::getElementType() {
+ assert(thisIs(IS_ANY));
+ osl::MutexGuard g(*lock_);
+ checkLocalizedPropertyAccess();
+ rtl::Reference< Node > p(getNode());
+ switch (p->kind()) {
+ case Node::KIND_LOCALIZED_PROPERTY:
+ return mapType(
+ static_cast< LocalizedPropertyNode * >(p.get())->getStaticType());
+ case Node::KIND_GROUP:
+ //TODO: Should a specific type be returned for a non-extensible group
+ // with homogeneous members or for an extensible group that currently
+ // has only homogeneous members?
+ return cppu::UnoType<void>::get();
+ case Node::KIND_SET:
+ return cppu::UnoType<void>::get(); //TODO: correct?
+ default:
+ assert(false);
+ throw css::uno::RuntimeException(
+ "this cannot happen", static_cast< cppu::OWeakObject * >(this));
+ }
+}
+
+sal_Bool Access::hasElements() {
+ assert(thisIs(IS_ANY));
+ osl::MutexGuard g(*lock_);
+ checkLocalizedPropertyAccess();
+ return !getAllChildren().empty(); //TODO: optimize
+}
+
+bool Access::getByNameFast(const OUString & name, css::uno::Any & value)
+{
+ bool bGotValue = false;
+ rtl::Reference< ChildAccess > child;
+
+ if (getNode()->kind() != Node::KIND_LOCALIZED_PROPERTY)
+ { // try to get it directly
+ ModifiedChildren::iterator i(modifiedChildren_.find(name));
+ if (i != modifiedChildren_.end())
+ {
+ child = getModifiedChild(i);
+ if (child.is())
+ {
+ value = child->asValue();
+ bGotValue = true;
+ }
+ }
+ else
+ {
+ rtl::Reference< Node > node(getNode()->getMember(name));
+ if (!node.is())
+ return false;
+ bGotValue = ChildAccess::asSimpleValue(node, value, components_);
+ }
+ }
+
+ if (!bGotValue)
+ {
+ child = getChild(name);
+ if (!child.is())
+ return false;
+ value = child->asValue();
+ }
+ return true;
+}
+
+css::uno::Any Access::getByName(OUString const & aName)
+{
+ assert(thisIs(IS_ANY));
+ osl::MutexGuard g(*lock_);
+ checkLocalizedPropertyAccess();
+ css::uno::Any value;
+ if (!getByNameFast(aName, value))
+ throw css::container::NoSuchElementException(
+ aName, static_cast< cppu::OWeakObject * >(this));
+ return value;
+}
+
+css::uno::Sequence< OUString > Access::getElementNames()
+{
+ assert(thisIs(IS_ANY));
+ osl::MutexGuard g(*lock_);
+ checkLocalizedPropertyAccess();
+ std::vector< rtl::Reference< ChildAccess > > children(getAllChildren());
+ css::uno::Sequence<OUString> names(children.size());
+ OUString* pArray = names.getArray();
+ for (auto const& child : children)
+ {
+ *pArray++ = child->getNameInternal();
+ }
+ return names;
+}
+
+sal_Bool Access::hasByName(OUString const & aName)
+{
+ assert(thisIs(IS_ANY));
+ osl::MutexGuard g(*lock_);
+ checkLocalizedPropertyAccess();
+ return getChild(aName).is();
+}
+
+css::uno::Any Access::getByHierarchicalName(OUString const & aName)
+{
+ assert(thisIs(IS_ANY));
+ osl::MutexGuard g(*lock_);
+ checkLocalizedPropertyAccess();
+ rtl::Reference< ChildAccess > child(getSubChild(aName));
+ if (!child.is()) {
+ throw css::container::NoSuchElementException(
+ aName, static_cast< cppu::OWeakObject * >(this));
+ }
+ return child->asValue();
+}
+
+sal_Bool Access::hasByHierarchicalName(OUString const & aName)
+{
+ assert(thisIs(IS_ANY));
+ osl::MutexGuard g(*lock_);
+ checkLocalizedPropertyAccess();
+ return getSubChild(aName).is();
+}
+
+void Access::replaceByHierarchicalName(
+ OUString const & aName, css::uno::Any const & aElement)
+{
+ //TODO: Actually support sets and combine with replaceByName:
+ assert(thisIs(IS_UPDATE));
+ Broadcaster bc;
+ {
+ osl::MutexGuard g(*lock_);
+ checkLocalizedPropertyAccess();
+ rtl::Reference< ChildAccess > child(getSubChild(aName));
+ if (!child.is()) {
+ throw css::container::NoSuchElementException(
+ aName, static_cast< cppu::OWeakObject * >(this));
+ }
+ child->checkFinalized();
+ rtl::Reference< Node > parent(child->getParentNode());
+ assert(parent.is());
+ Modifications localMods;
+ switch (parent->kind()) {
+ case Node::KIND_LOCALIZED_PROPERTY:
+ case Node::KIND_GROUP:
+ child->setProperty(aElement, &localMods);
+ break;
+ case Node::KIND_SET:
+ throw css::lang::IllegalArgumentException(
+ ("configmgr::Access::replaceByHierarchicalName does not"
+ " currently support set members"),
+ static_cast< cppu::OWeakObject * >(this), 0);
+ case Node::KIND_ROOT:
+ throw css::lang::IllegalArgumentException(
+ ("configmgr::Access::replaceByHierarchicalName does not allow"
+ " changing component " + aName),
+ static_cast< cppu::OWeakObject * >(this), 0);
+ default:
+ assert(false); // this cannot happen
+ break;
+ }
+ getNotificationRoot()->initBroadcaster(localMods.getRoot(), &bc);
+ }
+ bc.send();
+}
+
+void Access::addContainerListener(
+ css::uno::Reference< css::container::XContainerListener > const & xListener)
+{
+ assert(thisIs(IS_ANY));
+ {
+ osl::MutexGuard g(*lock_);
+ checkLocalizedPropertyAccess();
+ if (!xListener.is()) {
+ throw css::uno::RuntimeException(
+ "null listener", static_cast< cppu::OWeakObject * >(this));
+ }
+ if (!disposed_) {
+ containerListeners_.insert(xListener);
+ return;
+ }
+ }
+ try {
+ xListener->disposing(
+ css::lang::EventObject(static_cast< cppu::OWeakObject * >(this)));
+ } catch (css::lang::DisposedException &) {}
+}
+
+void Access::removeContainerListener(
+ css::uno::Reference< css::container::XContainerListener > const & xListener)
+{
+ assert(thisIs(IS_ANY));
+ osl::MutexGuard g(*lock_);
+ checkLocalizedPropertyAccess();
+ ContainerListeners::iterator i(containerListeners_.find(xListener));
+ if (i != containerListeners_.end()) {
+ containerListeners_.erase(i);
+ }
+}
+
+OUString Access::getExactName(OUString const & aApproximateName)
+{
+ assert(thisIs(IS_ANY));
+ osl::MutexGuard g(*lock_);
+ checkLocalizedPropertyAccess();
+ return aApproximateName;
+}
+
+css::uno::Sequence< css::beans::Property > Access::getProperties()
+{
+ assert(thisIs(IS_GROUP));
+ osl::MutexGuard g(*lock_);
+ std::vector< rtl::Reference< ChildAccess > > children(getAllChildren());
+ std::vector< css::beans::Property > properties;
+ properties.reserve(children.size());
+ for (auto const& child : children)
+ {
+ properties.push_back(child->asProperty());
+ }
+ return comphelper::containerToSequence(properties);
+}
+
+css::beans::Property Access::getPropertyByName(OUString const & aName)
+{
+ assert(thisIs(IS_GROUP));
+ osl::MutexGuard g(*lock_);
+ rtl::Reference< ChildAccess > child(getChild(aName));
+ if (!child.is()) {
+ throw css::beans::UnknownPropertyException(
+ aName, static_cast< cppu::OWeakObject * >(this));
+ }
+ return child->asProperty();
+}
+
+sal_Bool Access::hasPropertyByName(OUString const & Name)
+{
+ assert(thisIs(IS_GROUP));
+ osl::MutexGuard g(*lock_);
+ return getChild(Name).is();
+}
+
+OUString Access::getHierarchicalName() {
+ assert(thisIs(IS_ANY));
+ osl::MutexGuard g(*lock_);
+ checkLocalizedPropertyAccess();
+ // For backwards compatibility, return an absolute path representation where
+ // available:
+ OUString rootPath;
+ rtl::Reference< RootAccess > root(getRootAccess());
+ if (root.is()) {
+ rootPath = root->getAbsolutePathRepresentation();
+ }
+ OUString rel(getRelativePathRepresentation());
+ OUStringBuffer path(rootPath);
+ if (!rootPath.isEmpty() && rootPath != "/" && !rel.isEmpty()) {
+ path.append('/');
+ }
+ path.append(rel);
+ return path.makeStringAndClear();
+}
+
+OUString Access::composeHierarchicalName(
+ OUString const & aRelativeName)
+{
+ assert(thisIs(IS_ANY));
+ osl::MutexGuard g(*lock_);
+ checkLocalizedPropertyAccess();
+ if (aRelativeName.isEmpty() || aRelativeName[0] == '/') {
+ throw css::lang::IllegalArgumentException(
+ "configmgr composeHierarchicalName inappropriate relative name",
+ static_cast< cppu::OWeakObject * >(this), -1);
+ }
+ OUStringBuffer path(getRelativePathRepresentation());
+ if (!path.isEmpty()) {
+ path.append('/');
+ }
+ path.append(aRelativeName);
+ return path.makeStringAndClear();
+}
+
+OUString Access::getName() {
+ assert(thisIs(IS_ANY));
+ osl::MutexGuard g(*lock_);
+ checkLocalizedPropertyAccess();
+ return getNameInternal();
+}
+
+void Access::setName(OUString const & aName)
+{
+ assert(thisIs(IS_ANY));
+ Broadcaster bc;
+ {
+ osl::MutexGuard g(*lock_);
+ checkLocalizedPropertyAccess();
+ checkFinalized();
+ Modifications localMods;
+ switch (getNode()->kind()) {
+ case Node::KIND_GROUP:
+ case Node::KIND_SET:
+ {
+ rtl::Reference< Access > parent(getParentAccess());
+ if (parent.is()) {
+ rtl::Reference< Node > node(getNode());
+ if (! node->getTemplateName().isEmpty()) {
+ rtl::Reference< ChildAccess > other(
+ parent->getChild(aName));
+ if (other.get() == this) {
+ break;
+ }
+ if (node->getMandatory() == Data::NO_LAYER &&
+ !(other.is() && other->isFinalized()))
+ {
+ if (!isValidName(aName, true)) {
+ throw css::uno::RuntimeException(
+ "invalid element name " + aName);
+ }
+ rtl::Reference< RootAccess > root(getRootAccess());
+ rtl::Reference< ChildAccess > childAccess(
+ static_cast< ChildAccess * >(this));
+ localMods.add(getRelativePath());
+ // unbind() modifies the parent chain that
+ // markChildAsModified() walks, so order is
+ // important:
+ parent->markChildAsModified(childAccess);
+ //TODO: must not throw
+ childAccess->unbind(); // must not throw
+ if (other.is()) {
+ other->unbind(); // must not throw
+ }
+ childAccess->bind(root, parent, aName);
+ // must not throw
+ parent->markChildAsModified(childAccess);
+ //TODO: must not throw
+ localMods.add(getRelativePath());
+ break;
+ }
+ }
+ }
+ }
+ [[fallthrough]];
+ case Node::KIND_LOCALIZED_PROPERTY:
+ // renaming a property could only work for an extension property,
+ // but a localized property is never an extension property
+ throw css::uno::RuntimeException(
+ "configmgr setName inappropriate node",
+ static_cast< cppu::OWeakObject * >(this));
+ default:
+ assert(false); // this cannot happen
+ break;
+ }
+ getNotificationRoot()->initBroadcaster(localMods.getRoot(), &bc);
+ }
+ bc.send();
+}
+
+css::beans::Property Access::getAsProperty()
+{
+ assert(thisIs(IS_ANY));
+ osl::MutexGuard g(*lock_);
+ checkLocalizedPropertyAccess();
+ return asProperty();
+}
+
+css::uno::Reference< css::beans::XPropertySetInfo > Access::getPropertySetInfo()
+{
+ assert(thisIs(IS_GROUP));
+ return this;
+}
+
+void Access::setPropertyValue(
+ OUString const & aPropertyName, css::uno::Any const & aValue)
+{
+ assert(thisIs(IS_GROUP));
+ Broadcaster bc;
+ {
+ osl::MutexGuard g(*lock_);
+ if (!getRootAccess()->isUpdate()) {
+ throw css::uno::RuntimeException(
+ "configmgr setPropertyValue on non-update access",
+ static_cast< cppu::OWeakObject * >(this));
+ }
+ Modifications localMods;
+ if (!setChildProperty(aPropertyName, aValue, &localMods)) {
+ throw css::beans::UnknownPropertyException(
+ aPropertyName, static_cast< cppu::OWeakObject * >(this));
+ }
+ getNotificationRoot()->initBroadcaster(localMods.getRoot(), &bc);
+ }
+ bc.send();
+}
+
+css::uno::Any Access::getPropertyValue(OUString const & PropertyName)
+{
+ assert(thisIs(IS_GROUP));
+ osl::MutexGuard g(*lock_);
+
+ css::uno::Any value;
+ if (!getByNameFast(PropertyName, value))
+ throw css::beans::UnknownPropertyException(
+ PropertyName, static_cast< cppu::OWeakObject * >(this));
+ return value;
+}
+
+void Access::addPropertyChangeListener(
+ OUString const & aPropertyName,
+ css::uno::Reference< css::beans::XPropertyChangeListener > const &
+ xListener)
+{
+ assert(thisIs(IS_GROUP));
+ {
+ osl::MutexGuard g(*lock_);
+ if (!xListener.is()) {
+ throw css::uno::RuntimeException(
+ "null listener", static_cast< cppu::OWeakObject * >(this));
+ }
+ checkKnownProperty(aPropertyName);
+ if (!disposed_) {
+ propertyChangeListeners_[aPropertyName].insert(xListener);
+ return;
+ }
+ }
+ try {
+ xListener->disposing(
+ css::lang::EventObject(static_cast< cppu::OWeakObject * >(this)));
+ } catch (css::lang::DisposedException &) {}
+}
+
+void Access::removePropertyChangeListener(
+ OUString const & aPropertyName,
+ css::uno::Reference< css::beans::XPropertyChangeListener > const &
+ aListener)
+{
+ assert(thisIs(IS_GROUP));
+ osl::MutexGuard g(*lock_);
+ checkKnownProperty(aPropertyName);
+ PropertyChangeListeners::iterator i(
+ propertyChangeListeners_.find(aPropertyName));
+ if (i != propertyChangeListeners_.end()) {
+ PropertyChangeListenersElement::iterator j(i->second.find(aListener));
+ if (j != i->second.end()) {
+ i->second.erase(j);
+ if (i->second.empty()) {
+ propertyChangeListeners_.erase(i);
+ }
+ }
+ }
+}
+
+void Access::addVetoableChangeListener(
+ OUString const & PropertyName,
+ css::uno::Reference< css::beans::XVetoableChangeListener > const &
+ aListener)
+{
+ assert(thisIs(IS_GROUP));
+ {
+ osl::MutexGuard g(*lock_);
+ if (!aListener.is()) {
+ throw css::uno::RuntimeException(
+ "null listener", static_cast< cppu::OWeakObject * >(this));
+ }
+ checkKnownProperty(PropertyName);
+ if (!disposed_) {
+ vetoableChangeListeners_[PropertyName].insert(aListener);
+ //TODO: actually call vetoableChangeListeners_
+ return;
+ }
+ }
+ try {
+ aListener->disposing(
+ css::lang::EventObject(static_cast< cppu::OWeakObject * >(this)));
+ } catch (css::lang::DisposedException &) {}
+}
+
+void Access::removeVetoableChangeListener(
+ OUString const & PropertyName,
+ css::uno::Reference< css::beans::XVetoableChangeListener > const &
+ aListener)
+{
+ assert(thisIs(IS_GROUP));
+ osl::MutexGuard g(*lock_);
+ checkKnownProperty(PropertyName);
+ VetoableChangeListeners::iterator i(
+ vetoableChangeListeners_.find(PropertyName));
+ if (i != vetoableChangeListeners_.end()) {
+ VetoableChangeListenersElement::iterator j(i->second.find(aListener));
+ if (j != i->second.end()) {
+ i->second.erase(j);
+ if (i->second.empty()) {
+ vetoableChangeListeners_.erase(i);
+ }
+ }
+ }
+}
+
+void Access::setPropertyValues(
+ css::uno::Sequence< OUString > const & aPropertyNames,
+ css::uno::Sequence< css::uno::Any > const & aValues)
+{
+ assert(thisIs(IS_GROUP));
+ Broadcaster bc;
+ {
+ osl::MutexGuard g(*lock_);
+ if (!getRootAccess()->isUpdate()) {
+ throw css::uno::RuntimeException(
+ "configmgr setPropertyValues on non-update access",
+ static_cast< cppu::OWeakObject * >(this));
+ }
+ if (aPropertyNames.getLength() != aValues.getLength()) {
+ throw css::lang::IllegalArgumentException(
+ ("configmgr setPropertyValues: aPropertyNames/aValues of"
+ " different length"),
+ static_cast< cppu::OWeakObject * >(this), -1);
+ }
+ Modifications localMods;
+ for (sal_Int32 i = 0; i < aPropertyNames.getLength(); ++i) {
+ if (!setChildProperty(aPropertyNames[i], aValues[i], &localMods)) {
+ throw css::lang::IllegalArgumentException(
+ "configmgr setPropertyValues inappropriate property name",
+ static_cast< cppu::OWeakObject * >(this), -1);
+ }
+ }
+ getNotificationRoot()->initBroadcaster(localMods.getRoot(), &bc);
+ }
+ bc.send();
+}
+
+css::uno::Sequence< css::uno::Any > Access::getPropertyValues(
+ css::uno::Sequence< OUString > const & aPropertyNames)
+{
+ assert(thisIs(IS_GROUP));
+ osl::MutexGuard g(*lock_);
+ css::uno::Sequence< css::uno::Any > vals(aPropertyNames.getLength());
+ auto aValsRange = asNonConstRange(vals);
+ for (sal_Int32 i = 0; i < aPropertyNames.getLength(); ++i)
+ {
+ if (!getByNameFast(aPropertyNames[i], aValsRange[i]))
+ throw css::uno::RuntimeException(
+ "configmgr getPropertyValues inappropriate property name",
+ static_cast< cppu::OWeakObject * >(this));
+ }
+
+ return vals;
+}
+
+void Access::addPropertiesChangeListener(
+ css::uno::Sequence< OUString > const &,
+ css::uno::Reference< css::beans::XPropertiesChangeListener > const &
+ xListener)
+{
+ assert(thisIs(IS_GROUP));
+ {
+ osl::MutexGuard g(*lock_);
+ if (!xListener.is()) {
+ throw css::uno::RuntimeException(
+ "null listener", static_cast< cppu::OWeakObject * >(this));
+ }
+ if (!disposed_) {
+ propertiesChangeListeners_.insert(xListener);
+ return;
+ }
+ }
+ try {
+ xListener->disposing(
+ css::lang::EventObject(static_cast< cppu::OWeakObject * >(this)));
+ } catch (css::lang::DisposedException &) {}
+}
+
+void Access::removePropertiesChangeListener(
+ css::uno::Reference< css::beans::XPropertiesChangeListener > const &
+ xListener)
+{
+ assert(thisIs(IS_GROUP));
+ osl::MutexGuard g(*lock_);
+ PropertiesChangeListeners::iterator i(
+ propertiesChangeListeners_.find(xListener));
+ if (i != propertiesChangeListeners_.end()) {
+ propertiesChangeListeners_.erase(i);
+ }
+}
+
+void Access::firePropertiesChangeEvent(
+ css::uno::Sequence< OUString > const & aPropertyNames,
+ css::uno::Reference< css::beans::XPropertiesChangeListener > const &
+ xListener)
+{
+ assert(thisIs(IS_GROUP));
+ css::uno::Sequence< css::beans::PropertyChangeEvent > events(
+ aPropertyNames.getLength());
+ auto aEventsRange = asNonConstRange(events);
+ for (sal_Int32 i = 0; i < events.getLength(); ++i) {
+ aEventsRange[i].Source = static_cast< cppu::OWeakObject * >(this);
+ aEventsRange[i].PropertyName = aPropertyNames[i];
+ aEventsRange[i].Further = false;
+ aEventsRange[i].PropertyHandle = -1;
+ }
+ xListener->propertiesChange(events);
+}
+
+css::uno::Reference< css::beans::XHierarchicalPropertySetInfo >
+Access::getHierarchicalPropertySetInfo() {
+ assert(thisIs(IS_GROUP));
+ return this;
+}
+
+void Access::setHierarchicalPropertyValue(
+ OUString const & aHierarchicalPropertyName,
+ css::uno::Any const & aValue)
+{
+ assert(thisIs(IS_GROUP));
+ Broadcaster bc;
+ {
+ osl::MutexGuard g(*lock_);
+ if (!getRootAccess()->isUpdate()) {
+ throw css::uno::RuntimeException(
+ "configmgr setHierarchicalPropertyName on non-update access",
+ static_cast< cppu::OWeakObject * >(this));
+ }
+ rtl::Reference< ChildAccess > child(
+ getSubChild(aHierarchicalPropertyName));
+ if (!child.is()) {
+ throw css::beans::UnknownPropertyException(
+ aHierarchicalPropertyName,
+ static_cast< cppu::OWeakObject * >(this));
+ }
+ child->checkFinalized();
+ Modifications localMods;
+ child->setProperty(aValue, &localMods);
+ getNotificationRoot()->initBroadcaster(localMods.getRoot(), &bc);
+ }
+ bc.send();
+}
+
+css::uno::Any Access::getHierarchicalPropertyValue(
+ OUString const & aHierarchicalPropertyName)
+{
+ assert(thisIs(IS_GROUP));
+ osl::MutexGuard g(*lock_);
+ rtl::Reference< ChildAccess > child(getSubChild(aHierarchicalPropertyName));
+ if (!child.is()) {
+ throw css::beans::UnknownPropertyException(
+ aHierarchicalPropertyName,
+ static_cast< cppu::OWeakObject * >(this));
+ }
+ return child->asValue();
+}
+
+void Access::setHierarchicalPropertyValues(
+ css::uno::Sequence< OUString > const & aHierarchicalPropertyNames,
+ css::uno::Sequence< css::uno::Any > const & Values)
+{
+ assert(thisIs(IS_GROUP));
+ Broadcaster bc;
+ {
+ osl::MutexGuard g(*lock_);
+ if (!getRootAccess()->isUpdate()) {
+ throw css::uno::RuntimeException(
+ "configmgr setPropertyValues on non-update access",
+ static_cast< cppu::OWeakObject * >(this));
+ }
+ if (aHierarchicalPropertyNames.getLength() != Values.getLength()) {
+ throw css::lang::IllegalArgumentException(
+ ("configmgr setHierarchicalPropertyValues:"
+ " aHierarchicalPropertyNames/Values of different length"),
+ static_cast< cppu::OWeakObject * >(this), -1);
+ }
+ Modifications localMods;
+ for (sal_Int32 i = 0; i < aHierarchicalPropertyNames.getLength(); ++i) {
+ rtl::Reference< ChildAccess > child(
+ getSubChild(aHierarchicalPropertyNames[i]));
+ if (!child.is()) {
+ throw css::lang::IllegalArgumentException(
+ ("configmgr setHierarchicalPropertyValues inappropriate"
+ " property name"),
+ static_cast< cppu::OWeakObject * >(this), -1);
+ }
+ child->checkFinalized();
+ child->setProperty(Values[i], &localMods);
+ }
+ getNotificationRoot()->initBroadcaster(localMods.getRoot(), &bc);
+ }
+ bc.send();
+}
+
+css::uno::Sequence< css::uno::Any > Access::getHierarchicalPropertyValues(
+ css::uno::Sequence< OUString > const & aHierarchicalPropertyNames)
+{
+ assert(thisIs(IS_GROUP));
+ osl::MutexGuard g(*lock_);
+ css::uno::Sequence< css::uno::Any > vals(
+ aHierarchicalPropertyNames.getLength());
+ auto aValsRange = asNonConstRange(vals);
+ for (sal_Int32 i = 0; i < aHierarchicalPropertyNames.getLength(); ++i) {
+ rtl::Reference< ChildAccess > child(
+ getSubChild(aHierarchicalPropertyNames[i]));
+ if (!child.is()) {
+ throw css::lang::IllegalArgumentException(
+ ("configmgr getHierarchicalPropertyValues inappropriate"
+ " hierarchical property name"),
+ static_cast< cppu::OWeakObject * >(this), -1);
+ }
+ aValsRange[i] = child->asValue();
+ }
+ return vals;
+}
+
+css::beans::Property Access::getPropertyByHierarchicalName(
+ OUString const & aHierarchicalName)
+{
+ assert(thisIs(IS_GROUP));
+ osl::MutexGuard g(*lock_);
+ rtl::Reference< ChildAccess > child(getSubChild(aHierarchicalName));
+ if (!child.is()) {
+ throw css::beans::UnknownPropertyException(
+ aHierarchicalName, static_cast< cppu::OWeakObject * >(this));
+ }
+ return child->asProperty();
+}
+
+sal_Bool Access::hasPropertyByHierarchicalName(
+ OUString const & aHierarchicalName)
+{
+ assert(thisIs(IS_GROUP));
+ osl::MutexGuard g(*lock_);
+ return getSubChild(aHierarchicalName).is();
+}
+
+void Access::replaceByName(
+ OUString const & aName, css::uno::Any const & aElement)
+{
+ assert(thisIs(IS_UPDATE));
+ Broadcaster bc;
+ {
+ osl::MutexGuard g(*lock_);
+ checkLocalizedPropertyAccess();
+ rtl::Reference< ChildAccess > child(getChild(aName));
+ if (!child.is()) {
+ throw css::container::NoSuchElementException(
+ aName, static_cast< cppu::OWeakObject * >(this));
+ }
+ child->checkFinalized();
+ Modifications localMods;
+ switch (getNode()->kind()) {
+ case Node::KIND_LOCALIZED_PROPERTY:
+ case Node::KIND_GROUP:
+ child->setProperty(aElement, &localMods);
+ break;
+ case Node::KIND_SET:
+ {
+ rtl::Reference< ChildAccess > freeAcc(
+ getFreeSetMember(aElement));
+ rtl::Reference< RootAccess > root(getRootAccess());
+ localMods.add(child->getRelativePath());
+ child->unbind(); // must not throw
+ freeAcc->bind(root, this, aName); // must not throw
+ markChildAsModified(freeAcc); //TODO: must not throw
+ }
+ break;
+ default:
+ assert(false); // this cannot happen
+ break;
+ }
+ getNotificationRoot()->initBroadcaster(localMods.getRoot(), &bc);
+ }
+ bc.send();
+}
+
+void Access::insertByName(
+ OUString const & aName, css::uno::Any const & aElement)
+{
+ assert(thisIs(IS_EXTENSIBLE|IS_UPDATE));
+ Broadcaster bc;
+ {
+ osl::MutexGuard g(*lock_);
+ checkLocalizedPropertyAccess();
+ checkFinalized();
+ if (getChild(aName).is()) {
+ throw css::container::ElementExistException(
+ aName, static_cast< cppu::OWeakObject * >(this));
+ }
+ Modifications localMods;
+ switch (getNode()->kind()) {
+ case Node::KIND_LOCALIZED_PROPERTY:
+ if (!isValidName(aName, false)) {
+ throw css::lang::IllegalArgumentException(
+ aName, static_cast<cppu::OWeakObject *>(this), 0);
+ }
+ insertLocalizedValueChild(aName, aElement, &localMods);
+ break;
+ case Node::KIND_GROUP:
+ {
+ if (!isValidName(aName, false)) {
+ throw css::lang::IllegalArgumentException(
+ aName, static_cast<cppu::OWeakObject *>(this), 0);
+ }
+ checkValue(aElement, TYPE_ANY, true);
+ rtl::Reference child(
+ new ChildAccess(
+ components_, getRootAccess(), this, aName,
+ new PropertyNode(
+ Data::NO_LAYER, TYPE_ANY, true, aElement, true)));
+ markChildAsModified(child);
+ localMods.add(child->getRelativePath());
+ }
+ break;
+ case Node::KIND_SET:
+ {
+ if (!isValidName(aName, true)) {
+ throw css::lang::IllegalArgumentException(
+ aName, static_cast<cppu::OWeakObject *>(this), 0);
+ }
+ rtl::Reference< ChildAccess > freeAcc(
+ getFreeSetMember(aElement));
+ freeAcc->bind(getRootAccess(), this, aName); // must not throw
+ markChildAsModified(freeAcc); //TODO: must not throw
+ localMods.add(freeAcc->getRelativePath());
+ }
+ break;
+ default:
+ assert(false); // this cannot happen
+ break;
+ }
+ getNotificationRoot()->initBroadcaster(localMods.getRoot(), &bc);
+ }
+ bc.send();
+}
+
+void Access::removeByName(OUString const & aName)
+{
+ assert(thisIs(IS_EXTENSIBLE|IS_UPDATE));
+ Broadcaster bc;
+ {
+ osl::MutexGuard g(*lock_);
+ checkLocalizedPropertyAccess();
+ rtl::Reference< ChildAccess > child(getChild(aName));
+ if (!child.is() || child->isFinalized() ||
+ child->getNode()->getMandatory() != Data::NO_LAYER)
+ {
+ throw css::container::NoSuchElementException(
+ aName, static_cast< cppu::OWeakObject * >(this));
+ }
+ if (getNode()->kind() == Node::KIND_GROUP) {
+ rtl::Reference< Node > p(child->getNode());
+ if (p->kind() != Node::KIND_PROPERTY ||
+ !static_cast< PropertyNode * >(p.get())->isExtension())
+ {
+ throw css::container::NoSuchElementException(
+ aName, static_cast< cppu::OWeakObject * >(this));
+ }
+ }
+ Modifications localMods;
+ localMods.add(child->getRelativePath());
+ // unbind() modifies the parent chain that markChildAsModified() walks,
+ // so order is important:
+ markChildAsModified(child); //TODO: must not throw
+ child->unbind();
+ getNotificationRoot()->initBroadcaster(localMods.getRoot(), &bc);
+ }
+ bc.send();
+}
+
+css::uno::Reference< css::uno::XInterface > Access::createInstance()
+{
+ assert(thisIs(IS_SET|IS_UPDATE));
+ OUString tmplName(
+ static_cast< SetNode * >(getNode().get())->getDefaultTemplateName());
+ rtl::Reference< Node > tmpl(
+ components_.getTemplate(tmplName));
+ if (!tmpl.is()) {
+ throw css::uno::Exception(
+ "unknown template " + tmplName,
+ static_cast< cppu::OWeakObject * >(this));
+ }
+ rtl::Reference< Node > node(tmpl->clone(true));
+ node->setLayer(Data::NO_LAYER);
+ return static_cast< cppu::OWeakObject * >(
+ new ChildAccess(components_, getRootAccess(), node));
+}
+
+css::uno::Reference< css::uno::XInterface > Access::createInstanceWithArguments(
+ css::uno::Sequence< css::uno::Any > const & aArguments)
+{
+ assert(thisIs(IS_SET|IS_UPDATE));
+ if (aArguments.hasElements()) {
+ throw css::uno::Exception(
+ ("configuration SimpleSetUpdate createInstanceWithArguments"
+ " must not specify any arguments"),
+ static_cast< cppu::OWeakObject * >(this));
+ }
+ return createInstance();
+}
+
+Access::Access(Components & components):
+ components_(components), disposed_(false), lock_( lock() )
+{
+}
+
+Access::~Access() {}
+
+void Access::initDisposeBroadcaster(Broadcaster * broadcaster) {
+ assert(broadcaster != nullptr);
+ for (auto const& disposeListener : disposeListeners_)
+ {
+ broadcaster->addDisposeNotification(
+ disposeListener,
+ css::lang::EventObject(static_cast< cppu::OWeakObject * >(this)));
+ }
+ for (auto const& containerListener : containerListeners_)
+ {
+ broadcaster->addDisposeNotification(
+ containerListener,
+ css::lang::EventObject(static_cast< cppu::OWeakObject * >(this)));
+ }
+ for (auto const& propertyChangeListener : propertyChangeListeners_)
+ {
+ for (auto const& propertyChangeListenerElement : propertyChangeListener.second)
+ {
+ broadcaster->addDisposeNotification(
+ propertyChangeListenerElement,
+ css::lang::EventObject(
+ static_cast< cppu::OWeakObject * >(this)));
+ }
+ }
+ for (auto const& vetoableChangeListener : vetoableChangeListeners_)
+ {
+ for (auto const& vetoableChangeListenerElement : vetoableChangeListener.second)
+ {
+ broadcaster->addDisposeNotification(
+ vetoableChangeListenerElement,
+ css::lang::EventObject(
+ static_cast< cppu::OWeakObject * >(this)));
+ }
+ }
+ for (auto const& propertiesChangeListener : propertiesChangeListeners_)
+ {
+ broadcaster->addDisposeNotification(
+ propertiesChangeListener,
+ css::lang::EventObject(static_cast< cppu::OWeakObject * >(this)));
+ }
+ //TODO: iterate over children w/ listeners (incl. unmodified ones):
+ for (ModifiedChildren::iterator i(modifiedChildren_.begin());
+ i != modifiedChildren_.end(); ++i)
+ {
+ rtl::Reference< ChildAccess > child(getModifiedChild(i));
+ if (child.is()) {
+ child->initDisposeBroadcaster(broadcaster);
+ }
+ }
+}
+
+void Access::clearListeners() noexcept {
+ disposeListeners_.clear();
+ containerListeners_.clear();
+ propertyChangeListeners_.clear();
+ vetoableChangeListeners_.clear();
+ propertiesChangeListeners_.clear();
+ //TODO: iterate over children w/ listeners (incl. unmodified ones):
+ for (ModifiedChildren::iterator i(modifiedChildren_.begin());
+ i != modifiedChildren_.end(); ++i)
+ {
+ rtl::Reference< ChildAccess > child(getModifiedChild(i));
+ if (child.is()) {
+ child->clearListeners();
+ }
+ }
+}
+
+css::uno::Any Access::queryInterface(css::uno::Type const & aType)
+{
+ css::uno::Any res(OWeakObject::queryInterface(aType));
+ if (res.hasValue()) {
+ return res;
+ }
+ res = cppu::queryInterface(
+ aType, static_cast< css::lang::XTypeProvider * >(this),
+ static_cast< css::lang::XServiceInfo * >(this),
+ static_cast< css::lang::XComponent * >(this),
+ static_cast< css::container::XHierarchicalNameAccess * >(this),
+ static_cast< css::container::XContainer * >(this),
+ static_cast< css::beans::XExactName * >(this),
+ static_cast< css::container::XHierarchicalName * >(this),
+ static_cast< css::container::XNamed * >(this),
+ static_cast< css::beans::XProperty * >(this),
+ static_cast< css::container::XElementAccess * >(this),
+ static_cast< css::container::XNameAccess * >(this));
+ if (res.hasValue()) {
+ return res;
+ }
+ if (getNode()->kind() == Node::KIND_GROUP) {
+ res = cppu::queryInterface(
+ aType, static_cast< css::beans::XPropertySetInfo * >(this),
+ static_cast< css::beans::XPropertySet * >(this),
+ static_cast< css::beans::XMultiPropertySet * >(this),
+ static_cast< css::beans::XHierarchicalPropertySet * >(this),
+ static_cast< css::beans::XMultiHierarchicalPropertySet * >(this),
+ static_cast< css::beans::XHierarchicalPropertySetInfo * >(this));
+ if (res.hasValue()) {
+ return res;
+ }
+ }
+ if (getRootAccess()->isUpdate()) {
+ res = cppu::queryInterface(
+ aType, static_cast< css::container::XNameReplace * >(this),
+ static_cast< css::container::XHierarchicalNameReplace * >(this));
+ if (res.hasValue()) {
+ return res;
+ }
+ if (getNode()->kind() != Node::KIND_GROUP ||
+ static_cast< GroupNode * >(getNode().get())->isExtensible())
+ {
+ res = cppu::queryInterface(
+ aType, static_cast< css::container::XNameContainer * >(this));
+ if (res.hasValue()) {
+ return res;
+ }
+ }
+ if (getNode()->kind() == Node::KIND_SET) {
+ res = cppu::queryInterface(
+ aType, static_cast< css::lang::XSingleServiceFactory * >(this));
+ }
+ }
+ return res;
+}
+
+
+void Access::checkLocalizedPropertyAccess() {
+ if (getNode()->kind() == Node::KIND_LOCALIZED_PROPERTY &&
+ !Components::allLocales(getRootAccess()->getLocale()))
+ {
+ throw css::uno::RuntimeException(
+ "configmgr Access to specialized LocalizedPropertyNode",
+ static_cast< cppu::OWeakObject * >(this));
+ }
+}
+
+rtl::Reference< Node > Access::getParentNode() {
+ rtl::Reference< Access > parent(getParentAccess());
+ return parent.is() ? parent->getNode() : rtl::Reference< Node >();
+}
+
+rtl::Reference< ChildAccess > Access::getChild(OUString const & name) {
+ OUString locale;
+ if (getNode()->kind() == Node::KIND_LOCALIZED_PROPERTY
+ && name.startsWith("*", &locale))
+ {
+ if (locale.startsWith("*")) {
+ SAL_WARN(
+ "configmgr",
+ ("access best-matching localized property value via"
+ " \"*<locale>\" with <locale> \"")
+ << locale << "\" recursively starting with \"*\"");
+ return getChild(locale);
+ }
+ SAL_WARN_IF(
+ locale.isEmpty(), "configmgr",
+ ("access best-matching localized property value via \"*<locale>\""
+ " with empty <locale>; falling back to defaults"));
+
+ // Since the locale given to us is the one used at initialization,
+ // here we override it with the actual current-user's language to
+ // support per-view localization in LOK.
+ if (comphelper::LibreOfficeKit::isActive())
+ locale = comphelper::LibreOfficeKit::getLanguageTag().getBcp47();
+
+ if (!locale.isEmpty()) {
+ // Try exact match first, avoiding all fallback overhead.
+ rtl::Reference<ChildAccess> directChild(getChild(locale));
+ if (directChild.is())
+ return directChild;
+
+ // Find the best match using the LanguageTag fallback mechanism,
+ // excluding the original tag.
+ std::vector<OUString> aFallbacks = LanguageTag(locale).getFallbackStrings(false);
+ for (const OUString& rFallback : aFallbacks)
+ {
+ rtl::Reference<ChildAccess> child(getChild(rFallback));
+ if (child.is())
+ return child;
+ }
+
+ // As a workaround for broken xcu data that does not use shortest
+ // xml:lang attributes, look for the first entry with the same first
+ // segment as the requested language tag before falling back to
+ // defaults (see fdo#33638):
+ if (aFallbacks.size() > 0)
+ locale = aFallbacks[aFallbacks.size() - 1];
+ assert(
+ !locale.isEmpty() && locale.indexOf('-') == -1 &&
+ locale.indexOf('_') == -1);
+
+ std::vector< rtl::Reference< ChildAccess > > children(
+ getAllChildren());
+ for (auto const& child : children)
+ {
+ const OUString & name2(child->getNameInternal());
+ if (name2.startsWith(locale) &&
+ (name2.getLength() == locale.getLength() ||
+ name2[locale.getLength()] == '-' ||
+ name2[locale.getLength()] == '_'))
+ {
+ return child;
+ }
+ }
+ }
+ // Defaults are the "en-US" locale, the "en" locale, the empty string locale, the first child (if
+ // any, and if the property is non-nillable), or a null ChildAccess, in that order:
+ rtl::Reference< ChildAccess > child(getChild("en-US"));
+ if (child.is()) {
+ return child;
+ }
+ child = getChild("en");
+ if (child.is()) {
+ return child;
+ }
+ child = getChild("");
+ if (child.is()) {
+ return child;
+ }
+ if (!static_cast<LocalizedPropertyNode *>(getNode().get())->isNillable()) {
+ std::vector< rtl::Reference< ChildAccess > > children(getAllChildren());
+ if (!children.empty()) {
+ return children.front();
+ }
+ }
+ return rtl::Reference< ChildAccess >();
+ }
+ ModifiedChildren::iterator i(modifiedChildren_.find(name));
+ return i == modifiedChildren_.end()
+ ? getUnmodifiedChild(name) : getModifiedChild(i);
+}
+
+std::vector< rtl::Reference< ChildAccess > > Access::getAllChildren() {
+ std::vector< rtl::Reference< ChildAccess > > vec;
+ NodeMap const & members = getNode()->getMembers();
+ for (auto const& member : members)
+ {
+ if (modifiedChildren_.find(member.first) == modifiedChildren_.end()) {
+ vec.push_back(getUnmodifiedChild(member.first));
+ assert(vec.back().is());
+ }
+ }
+ for (ModifiedChildren::iterator i(modifiedChildren_.begin());
+ i != modifiedChildren_.end(); ++i)
+ {
+ rtl::Reference< ChildAccess > child(getModifiedChild(i));
+ if (child.is()) {
+ vec.push_back(child);
+ }
+ }
+ return vec;
+}
+
+void Access::checkValue(css::uno::Any const & value, Type type, bool nillable) {
+ bool ok;
+ switch (type) {
+ case TYPE_ERROR:
+ ok = false;
+ break;
+ case TYPE_ANY:
+ switch (getDynamicType(value)) {
+ case TYPE_ERROR:
+ ok = false;
+ break;
+ case TYPE_NIL:
+ ok = nillable;
+ break;
+ default:
+ ok = true;
+ break;
+ case TYPE_ANY:
+ for (;;) std::abort(); // cannot happen
+ }
+ break;
+ default:
+ ok = value.hasValue() ? value.isExtractableTo(mapType(type)) : nillable;
+ break;
+ case TYPE_NIL:
+ for (;;) std::abort(); // cannot happen
+ }
+ if (!ok) {
+ throw css::lang::IllegalArgumentException(
+ "configmgr inappropriate property value",
+ static_cast< cppu::OWeakObject * >(this), -1);
+ }
+}
+
+void Access::insertLocalizedValueChild(
+ OUString const & name, css::uno::Any const & value,
+ Modifications * localModifications)
+{
+ assert(localModifications != nullptr);
+ LocalizedPropertyNode * locprop = static_cast< LocalizedPropertyNode * >(
+ getNode().get());
+ checkValue(value, locprop->getStaticType(), locprop->isNillable());
+ rtl::Reference child(
+ new ChildAccess(
+ components_, getRootAccess(), this, name,
+ new LocalizedValueNode(Data::NO_LAYER, value)));
+ markChildAsModified(child);
+ localModifications->add(child->getRelativePath());
+}
+
+void Access::reportChildChanges(
+ std::vector< css::util::ElementChange > * changes)
+{
+ assert(changes != nullptr);
+ for (ModifiedChildren::iterator i(modifiedChildren_.begin());
+ i != modifiedChildren_.end(); ++i)
+ {
+ rtl::Reference< ChildAccess > child(getModifiedChild(i));
+ if (child.is()) {
+ child->reportChildChanges(changes);
+ changes->push_back(css::util::ElementChange());
+ //TODO: changed value and/or inserted node
+ } else {
+ changes->push_back(css::util::ElementChange()); //TODO: removed node
+ }
+ }
+}
+
+void Access::commitChildChanges(
+ bool valid, Modifications * globalModifications)
+{
+ assert(globalModifications != nullptr);
+ while (!modifiedChildren_.empty()) {
+ bool childValid = valid;
+ ModifiedChildren::iterator i(modifiedChildren_.begin());
+ rtl::Reference< ChildAccess > child(getModifiedChild(i));
+ if (child.is()) {
+ childValid = childValid && !child->isFinalized();
+ child->commitChanges(childValid, globalModifications);
+ //TODO: currently, this is called here for directly inserted
+ // children as well as for children whose sub-children were
+ // modified (and should never be called for directly removed
+ // children); clarify what exactly should happen here for
+ // directly inserted children
+ }
+ NodeMap & members = getNode()->getMembers();
+ NodeMap::iterator j(members.find(i->first));
+ if (child.is()) {
+ // Inserted:
+ if (j != members.end()) {
+ childValid = childValid &&
+ j->second->getFinalized() == Data::NO_LAYER;
+ if (childValid) {
+ child->getNode()->setMandatory(j->second->getMandatory());
+ }
+ }
+ if (childValid) {
+ members[i->first] = child->getNode();
+ }
+ } else {
+ // Removed:
+ childValid = childValid && j != members.end() &&
+ j->second->getFinalized() == Data::NO_LAYER &&
+ j->second->getMandatory() == Data::NO_LAYER;
+ if (childValid) {
+ members.erase(j);
+ }
+ }
+ if (childValid && i->second.directlyModified) {
+ std::vector<OUString> path(getAbsolutePath());
+ path.push_back(i->first);
+ components_.addModification(path);
+ globalModifications->add(path);
+ }
+ i->second.child->committed();
+ modifiedChildren_.erase(i);
+ }
+}
+
+void Access::initBroadcasterAndChanges(
+ Modifications::Node const & modifications, Broadcaster * broadcaster,
+ std::vector< css::util::ElementChange > * allChanges)
+{
+ assert(broadcaster != nullptr);
+ std::vector< css::beans::PropertyChangeEvent > propChanges;
+ bool collectPropChanges = !propertiesChangeListeners_.empty();
+ for (const auto & i : modifications.children)
+ {
+ rtl::Reference< ChildAccess > child(getChild(i.first));
+ if (child.is()) {
+ switch (child->getNode()->kind()) {
+ case Node::KIND_LOCALIZED_PROPERTY:
+ if (!i.second.children.empty()) {
+ if (Components::allLocales(getRootAccess()->getLocale())) {
+ child->initBroadcasterAndChanges(
+ i.second, broadcaster, allChanges);
+ //TODO: if allChanges==0, recurse only into children
+ // w/ listeners
+ } else {
+ //TODO: filter child mods that are irrelevant for
+ // locale:
+ for (auto const& containerListener : containerListeners_)
+ {
+ broadcaster->
+ addContainerElementReplacedNotification(
+ containerListener,
+ css::container::ContainerEvent(
+ static_cast< cppu::OWeakObject * >(
+ this),
+ css::uno::Any(i.first),
+ css::uno::Any(), css::uno::Any()));
+ //TODO: non-void Element, ReplacedElement
+ }
+ PropertyChangeListeners::iterator j(
+ propertyChangeListeners_.find(i.first));
+ if (j != propertyChangeListeners_.end()) {
+ for (auto const& propertyChangeListenerElement : j->second)
+ {
+ broadcaster->addPropertyChangeNotification(
+ propertyChangeListenerElement,
+ css::beans::PropertyChangeEvent(
+ static_cast< cppu::OWeakObject * >(
+ this),
+ i.first, false, -1, css::uno::Any(),
+ css::uno::Any()));
+ }
+ }
+ j = propertyChangeListeners_.find("");
+ if (j != propertyChangeListeners_.end()) {
+ for (auto const& propertyChangeListenerElement : j->second)
+ {
+ broadcaster->addPropertyChangeNotification(
+ propertyChangeListenerElement,
+ css::beans::PropertyChangeEvent(
+ static_cast< cppu::OWeakObject * >(
+ this),
+ i.first, false, -1, css::uno::Any(),
+ css::uno::Any()));
+ }
+ }
+ if (allChanges != nullptr) {
+ allChanges->push_back(
+ css::util::ElementChange(
+ css::uno::Any(
+ child->getRelativePathRepresentation()),
+ css::uno::Any(), css::uno::Any()));
+ //TODO: non-void Element, ReplacedElement
+ }
+ if (collectPropChanges) {
+ propChanges.emplace_back(
+ static_cast< cppu::OWeakObject * >(this),
+ i.first, false, -1, css::uno::Any(),
+ css::uno::Any());
+ }
+ }
+ }
+ // else: spurious Modifications::Node not representing a change
+ break;
+ case Node::KIND_LOCALIZED_VALUE:
+ assert(Components::allLocales(getRootAccess()->getLocale()));
+ for (auto const& containerListener : containerListeners_)
+ {
+ broadcaster->addContainerElementReplacedNotification(
+ containerListener,
+ css::container::ContainerEvent(
+ static_cast< cppu::OWeakObject * >(this),
+ css::uno::Any(i.first), child->asValue(),
+ css::uno::Any()));
+ //TODO: distinguish add/modify; non-void ReplacedElement
+ }
+ if (allChanges != nullptr) {
+ allChanges->push_back(
+ css::util::ElementChange(
+ css::uno::Any(
+ child->getRelativePathRepresentation()),
+ child->asValue(), css::uno::Any()));
+ //TODO: non-void ReplacedElement
+ }
+ assert(!collectPropChanges);
+ break;
+ case Node::KIND_PROPERTY:
+ {
+ for (auto const& containerListener : containerListeners_)
+ {
+ broadcaster->addContainerElementReplacedNotification(
+ containerListener,
+ css::container::ContainerEvent(
+ static_cast< cppu::OWeakObject * >(this),
+ css::uno::Any(i.first), child->asValue(),
+ css::uno::Any()));
+ //TODO: distinguish add/remove/modify; non-void
+ // ReplacedElement
+ }
+ PropertyChangeListeners::iterator j(
+ propertyChangeListeners_.find(i.first));
+ if (j != propertyChangeListeners_.end()) {
+ for (auto const& propertyChangeListenerElement : j->second)
+ {
+ broadcaster->addPropertyChangeNotification(
+ propertyChangeListenerElement,
+ css::beans::PropertyChangeEvent(
+ static_cast< cppu::OWeakObject * >(this),
+ i.first, false, -1, css::uno::Any(),
+ css::uno::Any()));
+ }
+ }
+ j = propertyChangeListeners_.find("");
+ if (j != propertyChangeListeners_.end()) {
+ for (auto const& propertyChangeListenerElement : j->second)
+ {
+ broadcaster->addPropertyChangeNotification(
+ propertyChangeListenerElement,
+ css::beans::PropertyChangeEvent(
+ static_cast< cppu::OWeakObject * >(this),
+ i.first, false, -1, css::uno::Any(),
+ css::uno::Any()));
+ }
+ }
+ if (allChanges != nullptr) {
+ allChanges->push_back(
+ css::util::ElementChange(
+ css::uno::Any(
+ child->getRelativePathRepresentation()),
+ child->asValue(), css::uno::Any()));
+ //TODO: non-void ReplacedElement
+ }
+ if (collectPropChanges) {
+ propChanges.emplace_back(
+ static_cast< cppu::OWeakObject * >(this),
+ i.first, false, -1, css::uno::Any(),
+ css::uno::Any());
+ }
+ }
+ break;
+ case Node::KIND_GROUP:
+ case Node::KIND_SET:
+ if (i.second.children.empty()) {
+ if (!child->getNode()->getTemplateName().isEmpty()) {
+ for (auto const& containerListener : containerListeners_)
+ {
+ broadcaster->
+ addContainerElementInsertedNotification(
+ containerListener,
+ css::container::ContainerEvent(
+ static_cast< cppu::OWeakObject * >(
+ this),
+ css::uno::Any(i.first),
+ child->asValue(), css::uno::Any()));
+ }
+ if (allChanges != nullptr) {
+ allChanges->push_back(
+ css::util::ElementChange(
+ css::uno::Any(
+ child->getRelativePathRepresentation()),
+ css::uno::Any(), css::uno::Any()));
+ //TODO: non-void Element, ReplacedElement
+ }
+ }
+ // else: spurious Modifications::Node not representing a
+ // change
+ } else {
+ child->initBroadcasterAndChanges(
+ i.second, broadcaster, allChanges);
+ //TODO: if allChanges==0, recurse only into children w/
+ // listeners
+ }
+ break;
+ case Node::KIND_ROOT:
+ assert(false); // this cannot happen
+ break;
+ }
+ } else {
+ switch (getNode()->kind()) {
+ case Node::KIND_LOCALIZED_PROPERTY:
+ // Removed localized property value:
+ assert(Components::allLocales(getRootAccess()->getLocale()));
+ for (auto const& containerListener : containerListeners_)
+ {
+ broadcaster->addContainerElementRemovedNotification(
+ containerListener,
+ css::container::ContainerEvent(
+ static_cast< cppu::OWeakObject * >(this),
+ css::uno::Any(i.first), css::uno::Any(),
+ css::uno::Any()));
+ //TODO: non-void ReplacedElement
+ }
+ if (allChanges != nullptr) {
+ OUStringBuffer path(getRelativePathRepresentation());
+ if (!path.isEmpty()) {
+ path.append('/');
+ }
+ path.append(Data::createSegment(u"*", i.first));
+ allChanges->push_back(
+ css::util::ElementChange(
+ css::uno::Any(path.makeStringAndClear()),
+ css::uno::Any(), css::uno::Any()));
+ //TODO: non-void ReplacedElement
+ }
+ assert(!collectPropChanges);
+ break;
+ case Node::KIND_GROUP:
+ {
+ // Removed (non-localized) extension property:
+ for (auto const& containerListener : containerListeners_)
+ {
+ broadcaster->addContainerElementRemovedNotification(
+ containerListener,
+ css::container::ContainerEvent(
+ static_cast< cppu::OWeakObject * >(this),
+ css::uno::Any(i.first), css::uno::Any(),
+ css::uno::Any()));
+ //TODO: non-void ReplacedElement
+ }
+ PropertyChangeListeners::iterator j(
+ propertyChangeListeners_.find(i.first));
+ if (j != propertyChangeListeners_.end()) {
+ for (auto const& propertyChangeListenerElement : j->second)
+ {
+ broadcaster->addPropertyChangeNotification(
+ propertyChangeListenerElement,
+ css::beans::PropertyChangeEvent(
+ static_cast< cppu::OWeakObject * >(this),
+ i.first, false, -1, css::uno::Any(),
+ css::uno::Any()));
+ }
+ }
+ j = propertyChangeListeners_.find("");
+ if (j != propertyChangeListeners_.end()) {
+ for (auto const& propertyChangeListenerElement : j->second)
+ {
+ broadcaster->addPropertyChangeNotification(
+ propertyChangeListenerElement,
+ css::beans::PropertyChangeEvent(
+ static_cast< cppu::OWeakObject * >(this),
+ i.first, false, -1, css::uno::Any(),
+ css::uno::Any()));
+ }
+ }
+ if (allChanges != nullptr) {
+ OUStringBuffer path(
+ getRelativePathRepresentation());
+ if (!path.isEmpty()) {
+ path.append('/');
+ }
+ path.append(i.first);
+ allChanges->push_back(
+ css::util::ElementChange(
+ css::uno::Any(path.makeStringAndClear()),
+ css::uno::Any(), css::uno::Any()));
+ //TODO: non-void ReplacedElement
+ }
+ if (collectPropChanges) {
+ propChanges.emplace_back(
+ static_cast< cppu::OWeakObject * >(this),
+ i.first, false, -1, css::uno::Any(),
+ css::uno::Any());
+ }
+ }
+ break;
+ case Node::KIND_SET:
+ // Removed set member:
+ if (i.second.children.empty()) {
+ for (auto const& containerListener : containerListeners_)
+ {
+ broadcaster->addContainerElementRemovedNotification(
+ containerListener,
+ css::container::ContainerEvent(
+ static_cast< cppu::OWeakObject * >(this),
+ css::uno::Any(i.first),
+ css::uno::Any(), css::uno::Any()));
+ //TODO: non-void ReplacedElement
+ }
+ if (allChanges != nullptr) {
+ OUStringBuffer path(
+ getRelativePathRepresentation());
+ if (!path.isEmpty()) {
+ path.append('/');
+ }
+ path.append(Data::createSegment(u"*", i.first));
+ allChanges->push_back(
+ css::util::ElementChange(
+ css::uno::Any(path.makeStringAndClear()),
+ css::uno::Any(), css::uno::Any()));
+ //TODO: non-void ReplacedElement
+ }
+ }
+ // else: spurious Modifications::Node not representing a change
+ break;
+ default:
+ assert(false); // this cannot happen
+ break;
+ }
+ }
+ }
+ if (!propChanges.empty()) {
+ css::uno::Sequence< css::beans::PropertyChangeEvent > seq(
+ comphelper::containerToSequence(propChanges));
+ for (auto const& propertyChangeListener : propertiesChangeListeners_)
+ {
+ broadcaster->addPropertiesChangeNotification(propertyChangeListener, seq);
+ }
+ }
+}
+
+
+Access::ModifiedChild::ModifiedChild():
+ directlyModified(false)
+{}
+
+Access::ModifiedChild::ModifiedChild(
+ rtl::Reference< ChildAccess > theChild, bool theDirectlyModified):
+ child(std::move(theChild)), directlyModified(theDirectlyModified)
+{}
+
+rtl::Reference< ChildAccess > Access::getModifiedChild(
+ ModifiedChildren::iterator const & childIterator)
+{
+ return (childIterator->second.child->getParentAccess() == this &&
+ (childIterator->second.child->getNameInternal() ==
+ childIterator->first))
+ ? childIterator->second.child : rtl::Reference< ChildAccess >();
+}
+
+rtl::Reference< ChildAccess > Access::createUnmodifiedChild(
+ const OUString &name, const rtl::Reference< Node > &node)
+{
+ rtl::Reference child(
+ new ChildAccess(components_, getRootAccess(), this, name, node));
+ cachedChildren_[name] = child.get();
+ return child;
+}
+
+rtl::Reference< ChildAccess > Access::getUnmodifiedChild(
+ OUString const & name)
+{
+ assert(modifiedChildren_.find(name) == modifiedChildren_.end());
+ rtl::Reference< Node > node(getNode()->getMember(name));
+ if (!node.is()) {
+ return rtl::Reference< ChildAccess >();
+ }
+ WeakChildMap::iterator i(cachedChildren_.find(name));
+ if (i != cachedChildren_.end()) {
+ rtl::Reference< ChildAccess > child;
+ if (i->second->acquireCounting() > 1) {
+ child.set(i->second); // must not throw
+ }
+ i->second->releaseNondeleting();
+ if (child.is()) {
+ child->setNode(node);
+ return child;
+ }
+ }
+ return createUnmodifiedChild(name,node);
+}
+
+rtl::Reference< ChildAccess > Access::getSubChild(OUString const & path) {
+ sal_Int32 i = 0;
+ // For backwards compatibility, allow absolute paths where meaningful:
+ if( path.startsWith("/") ) {
+ ++i;
+ if (!getRootAccess().is()) {
+ return rtl::Reference< ChildAccess >();
+ }
+ std::vector<OUString> abs(getAbsolutePath());
+ for (auto const& elem : abs)
+ {
+ OUString name1;
+ bool setElement1;
+ OUString templateName1;
+ i = Data::parseSegment(
+ path, i, &name1, &setElement1, &templateName1);
+ if (i == -1 || (i != path.getLength() && path[i] != '/')) {
+ return rtl::Reference< ChildAccess >();
+ }
+ OUString name2;
+ bool setElement2;
+ OUString templateName2;
+ Data::parseSegment(elem, 0, &name2, &setElement2, &templateName2);
+ if (name1 != name2 || setElement1 != setElement2 ||
+ (setElement1 &&
+ !Data::equalTemplateNames(templateName1, templateName2)))
+ {
+ return rtl::Reference< ChildAccess >();
+ }
+ if (i != path.getLength()) {
+ ++i;
+ }
+ }
+ }
+ for (rtl::Reference< Access > parent(this);;) {
+ OUString name;
+ bool setElement;
+ OUString templateName;
+ i = Data::parseSegment(path, i, &name, &setElement, &templateName);
+ if (i == -1 || (i != path.getLength() && path[i] != '/')) {
+ return rtl::Reference< ChildAccess >();
+ }
+ rtl::Reference< ChildAccess > child(parent->getChild(name));
+ if (!child.is()) {
+ return rtl::Reference< ChildAccess >();
+ }
+ if (setElement) {
+ rtl::Reference< Node > p(parent->getNode());
+ switch (p->kind()) {
+ case Node::KIND_LOCALIZED_PROPERTY:
+ if (!Components::allLocales(getRootAccess()->getLocale()) ||
+ !templateName.isEmpty())
+ {
+ return rtl::Reference< ChildAccess >();
+ }
+ break;
+ case Node::KIND_SET:
+ if (!templateName.isEmpty() &&
+ !static_cast< SetNode * >(p.get())->isValidTemplate(
+ templateName))
+ {
+ return rtl::Reference< ChildAccess >();
+ }
+ break;
+ default:
+ return rtl::Reference< ChildAccess >();
+ }
+ }
+ // For backwards compatibility, ignore a final slash after non-value
+ // nodes:
+ if (child->isValue()) {
+ return i == path.getLength()
+ ? child : rtl::Reference< ChildAccess >();
+ } else if (i >= path.getLength() - 1) {
+ return child;
+ }
+ ++i;
+ parent = child.get();
+ }
+}
+
+bool Access::setChildProperty(
+ OUString const & name, css::uno::Any const & value,
+ Modifications * localModifications)
+{
+ assert(localModifications != nullptr);
+ rtl::Reference< ChildAccess > child(getChild(name));
+ if (!child.is()) {
+ return false;
+ }
+ child->checkFinalized();
+ child->setProperty(value, localModifications);
+ return true;
+}
+
+css::beans::Property Access::asProperty() {
+ css::uno::Type type;
+ bool nillable;
+ bool removable;
+ rtl::Reference< Node > p(getNode());
+ switch (p->kind()) {
+ case Node::KIND_PROPERTY:
+ {
+ PropertyNode * prop = static_cast< PropertyNode * >(p.get());
+ type = mapType(prop->getStaticType());
+ nillable = prop->isNillable();
+ removable = prop->isExtension();
+ }
+ break;
+ case Node::KIND_LOCALIZED_PROPERTY:
+ {
+ LocalizedPropertyNode * locprop =
+ static_cast< LocalizedPropertyNode *>(p.get());
+ if (Components::allLocales(getRootAccess()->getLocale())) {
+ type = cppu::UnoType< css::uno::XInterface >::get();
+ //TODO: correct?
+ removable = false;
+ } else {
+ type = mapType(locprop->getStaticType());
+ removable = false; //TODO ???
+ }
+ nillable = locprop->isNillable();
+ }
+ break;
+ case Node::KIND_LOCALIZED_VALUE:
+ {
+ LocalizedPropertyNode * locprop =
+ static_cast< LocalizedPropertyNode * >(getParentNode().get());
+ type = mapType(locprop->getStaticType());
+ nillable = locprop->isNillable();
+ removable = false; //TODO ???
+ }
+ break;
+ default:
+ type = cppu::UnoType< css::uno::XInterface >::get(); //TODO: correct?
+ nillable = false;
+ rtl::Reference< Node > parent(getParentNode());
+ removable = parent.is() && parent->kind() == Node::KIND_SET;
+ break;
+ }
+ return css::beans::Property(
+ getNameInternal(), -1, type,
+ (css::beans::PropertyAttribute::BOUND | //TODO: correct for group/set?
+ css::beans::PropertyAttribute::CONSTRAINED |
+ (nillable ? css::beans::PropertyAttribute::MAYBEVOID : 0) |
+ (getRootAccess()->isUpdate() && removable
+ ? css::beans::PropertyAttribute::REMOVABLE : 0) |
+ (!getRootAccess()->isUpdate() || p->getFinalized() != Data::NO_LAYER
+ ? css::beans::PropertyAttribute::READONLY : 0))); //TODO: MAYBEDEFAULT
+}
+
+void Access::checkFinalized() {
+ if (isFinalized()) {
+ throw css::lang::IllegalArgumentException(
+ "configmgr modification of finalized item",
+ static_cast< cppu::OWeakObject * >(this), -1);
+ }
+}
+
+void Access::checkKnownProperty(OUString const & descriptor) {
+ if (descriptor.isEmpty()) {
+ return;
+ }
+ rtl::Reference< ChildAccess > child(getChild(descriptor));
+ if (child.is()) {
+ switch (child->getNode()->kind()) {
+ case Node::KIND_PROPERTY:
+ return;
+ case Node::KIND_LOCALIZED_PROPERTY:
+ if (!Components::allLocales(getRootAccess()->getLocale())) {
+ return;
+ }
+ break;
+ case Node::KIND_LOCALIZED_VALUE:
+ if (Components::allLocales(getRootAccess()->getLocale())) {
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ throw css::beans::UnknownPropertyException(
+ descriptor, static_cast< cppu::OWeakObject * >(this));
+}
+
+rtl::Reference< ChildAccess > Access::getFreeSetMember(
+ css::uno::Any const & value)
+{
+ rtl::Reference< ChildAccess > freeAcc = comphelper::getFromUnoTunnel<ChildAccess>(value);
+ if (!freeAcc.is() || freeAcc->getParentAccess().is() ||
+ (freeAcc->isInTransaction() &&
+ freeAcc->getRootAccess() != getRootAccess()))
+ {
+ throw css::lang::IllegalArgumentException(
+ "configmgr inappropriate set element",
+ static_cast< cppu::OWeakObject * >(this), 1);
+ }
+ assert(dynamic_cast< SetNode * >(getNode().get()) != nullptr);
+ if (!static_cast< SetNode * >(getNode().get())->isValidTemplate(
+ freeAcc->getNode()->getTemplateName()))
+ {
+ throw css::lang::IllegalArgumentException(
+ "configmgr inappropriate set element",
+ static_cast< cppu::OWeakObject * >(this), 1);
+ }
+ return freeAcc;
+}
+
+rtl::Reference< Access > Access::getNotificationRoot() {
+ for (rtl::Reference< Access > p(this);;) {
+ rtl::Reference< Access > parent(p->getParentAccess());
+ if (!parent.is()) {
+ return p;
+ }
+ p = parent;
+ }
+}
+
+#if !defined NDEBUG
+bool Access::thisIs(int what) {
+ osl::MutexGuard g(*lock_);
+ rtl::Reference< Node > p(getNode());
+ Node::Kind k(p->kind());
+ return (k != Node::KIND_PROPERTY && k != Node::KIND_LOCALIZED_VALUE &&
+ ((what & IS_GROUP) == 0 || k == Node::KIND_GROUP) &&
+ ((what & IS_SET) == 0 || k == Node::KIND_SET) &&
+ ((what & IS_EXTENSIBLE) == 0 || k != Node::KIND_GROUP ||
+ static_cast< GroupNode * >(p.get())->isExtensible()) &&
+ ((what & IS_GROUP_MEMBER) == 0 ||
+ getParentNode()->kind() == Node::KIND_GROUP)) ||
+ ((what & IS_SET_MEMBER) == 0 ||
+ getParentNode()->kind() == Node::KIND_SET) ||
+ ((what & IS_UPDATE) == 0 || getRootAccess()->isUpdate());
+}
+#endif
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */