summaryrefslogtreecommitdiffstats
path: root/configmgr
diff options
context:
space:
mode:
Diffstat (limited to 'configmgr')
-rw-r--r--configmgr/CppunitTest_configmgr_unit.mk58
-rw-r--r--configmgr/IwyuFilter_configmgr.yaml56
-rw-r--r--configmgr/JunitTest_configmgr_unoapi.mk14
-rw-r--r--configmgr/Library_configmgr.mk67
-rw-r--r--configmgr/Makefile7
-rw-r--r--configmgr/Module_configmgr.mk24
-rw-r--r--configmgr/README.md137
-rw-r--r--configmgr/inc/pch/precompiled_configmgr.cxx12
-rw-r--r--configmgr/inc/pch/precompiled_configmgr.hxx87
-rw-r--r--configmgr/qa/unit/test.cxx470
-rw-r--r--configmgr/qa/unit/urebootstrap.ini20
-rw-r--r--configmgr/qa/unoapi/module.sce20
-rw-r--r--configmgr/source/access.cxx2212
-rw-r--r--configmgr/source/access.hxx450
-rw-r--r--configmgr/source/additions.hxx34
-rw-r--r--configmgr/source/broadcaster.cxx230
-rw-r--r--configmgr/source/broadcaster.hxx139
-rw-r--r--configmgr/source/childaccess.cxx350
-rw-r--r--configmgr/source/childaccess.hxx137
-rw-r--r--configmgr/source/components.cxx895
-rw-r--r--configmgr/source/components.hxx166
-rw-r--r--configmgr/source/config_map.hxx37
-rw-r--r--configmgr/source/configmgr.component49
-rw-r--r--configmgr/source/configurationprovider.cxx403
-rw-r--r--configmgr/source/configurationprovider.hxx41
-rw-r--r--configmgr/source/configurationregistry.cxx641
-rw-r--r--configmgr/source/data.cxx336
-rw-r--r--configmgr/source/data.hxx99
-rw-r--r--configmgr/source/dconf.cxx1598
-rw-r--r--configmgr/source/dconf.hxx27
-rw-r--r--configmgr/source/defaultprovider.cxx53
-rw-r--r--configmgr/source/defaultprovider.hxx33
-rw-r--r--configmgr/source/groupnode.cxx77
-rw-r--r--configmgr/source/groupnode.hxx63
-rw-r--r--configmgr/source/localizedpropertynode.cxx60
-rw-r--r--configmgr/source/localizedpropertynode.hxx59
-rw-r--r--configmgr/source/localizedvaluenode.cxx64
-rw-r--r--configmgr/source/localizedvaluenode.hxx61
-rw-r--r--configmgr/source/lock.cxx38
-rw-r--r--configmgr/source/lock.hxx31
-rw-r--r--configmgr/source/modifications.cxx77
-rw-r--r--configmgr/source/modifications.hxx67
-rw-r--r--configmgr/source/node.cxx79
-rw-r--r--configmgr/source/node.hxx68
-rw-r--r--configmgr/source/nodemap.cxx53
-rw-r--r--configmgr/source/nodemap.hxx68
-rw-r--r--configmgr/source/parsemanager.cxx91
-rw-r--r--configmgr/source/parsemanager.hxx56
-rw-r--r--configmgr/source/parser.hxx53
-rw-r--r--configmgr/source/partial.cxx134
-rw-r--r--configmgr/source/partial.hxx62
-rw-r--r--configmgr/source/propertynode.cxx92
-rw-r--r--configmgr/source/propertynode.hxx74
-rw-r--r--configmgr/source/readonlyaccess.cxx121
-rw-r--r--configmgr/source/readwriteaccess.cxx144
-rw-r--r--configmgr/source/rootaccess.cxx313
-rw-r--r--configmgr/source/rootaccess.hxx144
-rw-r--r--configmgr/source/rootnode.cxx39
-rw-r--r--configmgr/source/rootnode.hxx39
-rw-r--r--configmgr/source/setnode.cxx90
-rw-r--r--configmgr/source/setnode.hxx73
-rw-r--r--configmgr/source/type.cxx162
-rw-r--r--configmgr/source/type.hxx48
-rw-r--r--configmgr/source/update.cxx150
-rw-r--r--configmgr/source/valueparser.cxx454
-rw-r--r--configmgr/source/valueparser.hxx85
-rw-r--r--configmgr/source/winreg.cxx331
-rw-r--r--configmgr/source/winreg.hxx21
-rw-r--r--configmgr/source/writemodfile.cxx640
-rw-r--r--configmgr/source/writemodfile.hxx63
-rw-r--r--configmgr/source/xcdparser.cxx174
-rw-r--r--configmgr/source/xcdparser.hxx72
-rw-r--r--configmgr/source/xcsparser.cxx596
-rw-r--r--configmgr/source/xcsparser.hxx101
-rw-r--r--configmgr/source/xcuparser.cxx968
-rw-r--r--configmgr/source/xcuparser.hxx152
-rw-r--r--configmgr/source/xmldata.cxx133
-rw-r--r--configmgr/source/xmldata.hxx46
78 files changed, 15488 insertions, 0 deletions
diff --git a/configmgr/CppunitTest_configmgr_unit.mk b/configmgr/CppunitTest_configmgr_unit.mk
new file mode 100644
index 000000000..fa0718dc2
--- /dev/null
+++ b/configmgr/CppunitTest_configmgr_unit.mk
@@ -0,0 +1,58 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#*************************************************************************
+#
+# 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/.
+#
+#*************************************************************************
+
+$(eval $(call gb_CppunitTest_CppunitTest,configmgr_unit))
+
+$(eval $(call gb_CppunitTest_add_exception_objects,configmgr_unit, \
+ configmgr/qa/unit/test \
+))
+
+$(eval $(call gb_CppunitTest_set_componentfile,configmgr_unit,configmgr/source/configmgr))
+
+$(eval $(call gb_CppunitTest_use_library_objects,configmgr_unit,configmgr))
+
+$(eval $(call gb_CppunitTest_use_sdk_api,configmgr_unit,))
+
+$(eval $(call gb_CppunitTest_use_custom_headers,configmgr_unit,\
+ officecfg/registry \
+))
+
+$(eval $(call gb_CppunitTest_use_libraries,configmgr_unit, \
+ comphelper \
+ cppu \
+ cppuhelper \
+ sal \
+ salhelper \
+ test \
+ tl \
+ unotest \
+ xmlreader \
+ i18nlangtag \
+))
+
+$(eval $(call gb_CppunitTest_use_ure,configmgr_unit))
+
+$(eval $(call gb_CppunitTest_use_configuration,configmgr_unit))
+
+$(eval $(call gb_CppunitTest_use_components,configmgr_unit,\
+ i18npool/util/i18npool \
+ i18npool/source/search/i18nsearch \
+ sax/source/expatwrap/expwrap \
+ ucb/source/core/ucb1 \
+ ucb/source/ucp/file/ucpfile1 \
+ unoxml/source/service/unoxml \
+))
+
+$(eval $(call gb_CppunitTest_use_externals,configmgr_unit,\
+ boost_headers \
+ dconf \
+ icu_headers \
+))
diff --git a/configmgr/IwyuFilter_configmgr.yaml b/configmgr/IwyuFilter_configmgr.yaml
new file mode 100644
index 000000000..5cb79e6ae
--- /dev/null
+++ b/configmgr/IwyuFilter_configmgr.yaml
@@ -0,0 +1,56 @@
+---
+assumeFilename: configmgr/source/access.cxx
+excludelist:
+ configmgr/source/nodemap.hxx:
+ # Needed for direct member access
+ - node.hxx
+ configmgr/source/type.hxx:
+ # Needed for css shortcut
+ - sal/types.h
+ configmgr/source/access.cxx:
+ # Safer to keep for use in cppu:UnoType template
+ - com/sun/star/uno/XWeak.hpp
+ configmgr/source/dconf.cxx:
+ # Generic wrapper for dconf
+ - dconf/dconf.h
+ configmgr/source/broadcaster.cxx:
+ # Needed for template
+ - com/sun/star/beans/XPropertiesChangeListener.hpp
+ - com/sun/star/beans/XPropertyChangeListener.hpp
+ - com/sun/star/container/XContainerListener.hpp
+ - com/sun/star/lang/XEventListener.hpp
+ - com/sun/star/util/XChangesListener.hpp
+ configmgr/source/childaccess.cxx:
+ # Actually used
+ - vector
+ - com/sun/star/uno/XInterface.hpp
+ - com/sun/star/uno/Sequence.hxx
+ configmgr/source/configurationprovider.cxx:
+ # Actually used
+ - com/sun/star/uno/XInterface.hpp
+ configmgr/source/components.cxx:
+ # Actually used
+ - com/sun/star/uno/XComponentContext.hpp
+ configmgr/source/access.cxx:
+ # Actually used
+ - vector
+ - com/sun/star/beans/XPropertiesChangeListener.hpp
+ - com/sun/star/beans/XPropertyChangeListener.hpp
+ - com/sun/star/beans/XVetoableChangeListener.hpp
+ - com/sun/star/container/XContainerListener.hpp
+ - com/sun/star/lang/XEventListener.hpp
+ - com/sun/star/uno/XInterface.hpp
+ configmgr/source/parsemanager.cxx:
+ # Actually used
+ - set
+ configmgr/source/partial.cxx:
+ # Actually used
+ - set
+ configmgr/source/rootaccess.cxx:
+ # Actually used
+ - vector
+ - com/sun/star/util/ElementChange.hpp
+ - com/sun/star/util/XChangesListener.hpp
+ configmgr/source/xcdparser.cxx:
+ # Actually used
+ - set
diff --git a/configmgr/JunitTest_configmgr_unoapi.mk b/configmgr/JunitTest_configmgr_unoapi.mk
new file mode 100644
index 000000000..51dcc8a07
--- /dev/null
+++ b/configmgr/JunitTest_configmgr_unoapi.mk
@@ -0,0 +1,14 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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/.
+#
+
+$(eval $(call gb_JunitTest_JunitTest,configmgr_unoapi))
+
+$(eval $(call gb_JunitTest_set_unoapi_test_defaults,configmgr_unoapi,,module.sce))
+
+# vim: set noet sw=4 ts=4:
diff --git a/configmgr/Library_configmgr.mk b/configmgr/Library_configmgr.mk
new file mode 100644
index 000000000..11bba5f0f
--- /dev/null
+++ b/configmgr/Library_configmgr.mk
@@ -0,0 +1,67 @@
+#
+# 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/.
+#
+
+$(eval $(call gb_Library_Library,configmgr))
+
+$(eval $(call gb_Library_set_precompiled_header,configmgr,configmgr/inc/pch/precompiled_configmgr))
+
+$(eval $(call gb_Library_add_exception_objects,configmgr, \
+ configmgr/source/access \
+ configmgr/source/broadcaster \
+ configmgr/source/childaccess \
+ configmgr/source/components \
+ configmgr/source/configurationprovider \
+ configmgr/source/configurationregistry \
+ configmgr/source/data \
+ configmgr/source/defaultprovider \
+ configmgr/source/groupnode \
+ configmgr/source/localizedpropertynode \
+ configmgr/source/localizedvaluenode \
+ configmgr/source/lock \
+ configmgr/source/modifications \
+ configmgr/source/node \
+ configmgr/source/nodemap \
+ configmgr/source/parsemanager \
+ configmgr/source/partial \
+ configmgr/source/propertynode \
+ configmgr/source/readonlyaccess \
+ configmgr/source/readwriteaccess \
+ configmgr/source/rootaccess \
+ configmgr/source/rootnode \
+ configmgr/source/setnode \
+ configmgr/source/type \
+ configmgr/source/update \
+ configmgr/source/valueparser \
+ configmgr/source/writemodfile \
+ configmgr/source/xcdparser \
+ configmgr/source/xcsparser \
+ configmgr/source/xcuparser \
+ configmgr/source/xmldata \
+ $(if $(ENABLE_DCONF),configmgr/source/dconf) \
+ $(if $(filter $(OS),WNT),configmgr/source/winreg) \
+))
+
+$(eval $(call gb_Library_use_externals,configmgr, \
+ boost_headers \
+ dconf \
+))
+
+$(eval $(call gb_Library_use_sdk_api,configmgr))
+
+$(eval $(call gb_Library_use_libraries,configmgr, \
+ comphelper \
+ cppu \
+ cppuhelper \
+ sal \
+ salhelper \
+ tl \
+ xmlreader \
+ i18nlangtag \
+))
+
+$(eval $(call gb_Library_set_componentfile,configmgr,configmgr/source/configmgr,services))
diff --git a/configmgr/Makefile b/configmgr/Makefile
new file mode 100644
index 000000000..ccb1c85a0
--- /dev/null
+++ b/configmgr/Makefile
@@ -0,0 +1,7 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+
+module_directory:=$(dir $(realpath $(firstword $(MAKEFILE_LIST))))
+
+include $(module_directory)/../solenv/gbuild/partial_build.mk
+
+# vim: set noet sw=4 ts=4:
diff --git a/configmgr/Module_configmgr.mk b/configmgr/Module_configmgr.mk
new file mode 100644
index 000000000..09212d436
--- /dev/null
+++ b/configmgr/Module_configmgr.mk
@@ -0,0 +1,24 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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/.
+#
+
+$(eval $(call gb_Module_Module,configmgr))
+
+$(eval $(call gb_Module_add_targets,configmgr,\
+ Library_configmgr \
+))
+
+$(eval $(call gb_Module_add_subsequentcheck_targets,configmgr,\
+ JunitTest_configmgr_unoapi \
+))
+
+$(eval $(call gb_Module_add_check_targets,configmgr,\
+ CppunitTest_configmgr_unit \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/configmgr/README.md b/configmgr/README.md
new file mode 100644
index 000000000..0534b592e
--- /dev/null
+++ b/configmgr/README.md
@@ -0,0 +1,137 @@
+# UNO Services to Access the Configuration Database
+
+## Functional Overview
+
+This code parses the settings that are described in the `officecfg`
+directory, and provides a UNO API that code can use to set and get
+settings.
+
+## Source Overview
+
+ configurationprovider.cxx
+ configurationregistry.cxx
+ defaultprovider.cxx
+ services.cxx
+UNO service implementations.
+
+ access.cxx
+ childaccess.cxx
+ rootaccess.cxx
+UNO objects passed to clients.
+
+ components.cxx
+Central singleton that aggregates all data (reads in the XML files, manages
+modifications and global notifications).
+
+ groupnode.cxx
+ localizedpropertynode.cxx
+ localizedvaluenode.cxx
+ node.cxx
+ propertynode.cxx
+ setnode.cxx
+Internal representations of data nodes.
+
+ parsemanager.cxx
+ parser.hxx
+ valueparser.cxx
+ xcdparser.cxx
+ xcsparser.cxx
+ xcuparser.cxx
+ xmldata.cxx
+XML file reading.
+
+ modifications.cxx
+ writemodfile.cxx
+Modification management.
+
+ broadcaster.cxx
+Notification management.
+
+ additions.hxx
+ update.cxx
+Extension manager interface.
+
+ data.cxx
+ lock.cxx
+ nodemap.cxx
+ partial.cxx
+ path.hxx
+ type.cxx
+Utilities.
+
+
+## Some Implementation Notes
+
+### Mandatory Set Members
+
+- A set member can be marked as "mandatory," meaning that a member of that name
+must always be present in a set.
+
+- The above definition implies that calling replaceByName on a mandatory set
+member is OK.
+
+- The XCU format can contain `oor:mandatory` attributes on nodes. (The XCS format
+does not support them. In the `registrymodifications` file, `oor:mandatory`
+attributes should never be needed, as being mandatory cannot be controlled via
+the UNO API.) The XCU reading code only evaluates the `oor:mandatory` attribute
+for set members, and ignores it everywhere else.
+
+- Only true sets support mandatory members. A localized property for the "`*`"
+locale, though acting much like a set, does not support mandatory members.
+
+- The LibreOffice Registry Format document claims that group extension
+properties are implicitly mandatory, but at least the new configmgr code does
+not treat them like that (i.e., they can be removed again).
+
+- For simplicity, `setMandatory/getMandatory` are available as virtual functions
+at the base `Node`, even though they can only make sense for `GroupNodes` and
+SetNodes that are set members. The default `getMandatory` implementation returns
+`NO_LAYER`, meaning `oor:mandatory` is not set to true in any layer. (Returning
+`NO_LAYER` simplifies the code, e.g., `removeByName` does not have to check whether
+`getMandatory` is called on a member of a true set to decide whether to forbid
+removal.)
+
+- When committing changes (made through the UNO API), the "mandatory" status of
+inserted nodes must be updated (in case the insert is due to a `replaceByName`, or
+the "mandatory" flag was added by a concurrent modification of a lower layer).
+Also, for to-be-removed nodes, removal is ignored for (newly; due to concurrent
+modification of a lower layer) mandatory nodes (but still recorded in
+`registrymodifications`, so may take effect once the lower layer addition is
+removed again---whether or not that is a good idea).
+
+
+### XcuParser Modification Recording
+
+- `XcuParser` records modifications when reading user layer data
+(`valueParser_.getLayer() == Data::NO_LAYER`).
+
+- `oor:finalized and `oor:mandatory` attributes cannot be set via the UNO API, so
+it is assumed that user layer data does not contain them (for one, they are not
+written by `writeModFile`; for another, the logic to record modifications expects
+a `locprop(modify,fuse)` to be followed by one or more `value(fuse,remove)`, which
+would not necessarily be true if the `locprop` were only present in the user layer
+data to flag it as finalized).
+
+- The logic to record modifications considers the top of the XML element stack.
+In the following list of all possible cases, items marked with an asterisk are
+recorded:
+
+ ... group(modify,fuse) - group(modify,fuse) - ...
+ ... group(modify,fuse) - set(modify,fuse) - ...
+ ... group(modify,fuse) - *prop(modify,fuse,replace) - value(fuse)
+ ... group(modify,fuse) - *prop(remove)
+ ... group(modify,fuse) - locprop(modify,fuse) - *value(fuse)
+ ... group(modify,fuse) - locprop(modify,fuse) - *value(remove)
+ ... group(modify,fuse) - *locprop(replace) ...
+ ... set(modify,fuse,replace) - group(modify/fuse) - ...
+ ... set(modify,fuse,replace) - *group(replace/fuse) - ...
+ ... set(modify,fuse,replace) - *group(remove)
+ ... set(modify,fuse,replace) - set(modify/fuse) - ...
+ ... set(modify,fuse,replace) - *set(replace/fuse) - ...
+ ... set(modify,fuse,replace) - *set(remove)
+ Legend:
+ "...": zero or more further items
+ "- ...": one or more further items
+ "modify,fuse" etc.: any of those operations
+ "modify/fuse": a modify or a fuse on an existing member
+ "replace/fuse": a replace or a fuse on a non-existing member
diff --git a/configmgr/inc/pch/precompiled_configmgr.cxx b/configmgr/inc/pch/precompiled_configmgr.cxx
new file mode 100644
index 000000000..0d7480118
--- /dev/null
+++ b/configmgr/inc/pch/precompiled_configmgr.cxx
@@ -0,0 +1,12 @@
+/* -*- 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/.
+ */
+
+#include "precompiled_configmgr.hxx"
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/inc/pch/precompiled_configmgr.hxx b/configmgr/inc/pch/precompiled_configmgr.hxx
new file mode 100644
index 000000000..4de416427
--- /dev/null
+++ b/configmgr/inc/pch/precompiled_configmgr.hxx
@@ -0,0 +1,87 @@
+/* -*- 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 has been autogenerated by update_pch.sh. It is possible to edit it
+ manually (such as when an include file has been moved/renamed/removed). All such
+ manual changes will be rewritten by the next run of update_pch.sh (which presumably
+ also fixes all possible problems, so it's usually better to use it).
+
+ Generated on 2021-04-08 13:55:35 using:
+ ./bin/update_pch configmgr configmgr --cutoff=6 --exclude:system --include:module --include:local
+
+ If after updating build fails, use the following command to locate conflicting headers:
+ ./bin/update_pch_bisect ./configmgr/inc/pch/precompiled_configmgr.hxx "make configmgr.build" --find-conflicts
+*/
+
+#include <sal/config.h>
+#if PCH_LEVEL >= 1
+#include <cassert>
+#include <chrono>
+#include <cstddef>
+#include <limits>
+#include <new>
+#include <ostream>
+#include <set>
+#include <string.h>
+#include <string_view>
+#include <type_traits>
+#include <utility>
+#include <vector>
+#endif // PCH_LEVEL >= 1
+#if PCH_LEVEL >= 2
+#include <osl/conditn.hxx>
+#include <osl/diagnose.h>
+#include <osl/file.hxx>
+#include <osl/interlck.h>
+#include <osl/mutex.hxx>
+#include <osl/thread.h>
+#include <osl/time.h>
+#include <rtl/alloc.h>
+#include <rtl/bootstrap.hxx>
+#include <rtl/character.hxx>
+#include <rtl/locale.h>
+#include <rtl/ref.hxx>
+#include <rtl/strbuf.hxx>
+#include <rtl/string.h>
+#include <rtl/textcvt.h>
+#include <rtl/textenc.h>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/ustring.h>
+#include <rtl/ustring.hxx>
+#include <rtl/uuid.h>
+#include <sal/detail/log.h>
+#include <sal/log.hxx>
+#include <sal/saldllapi.h>
+#include <sal/types.h>
+#endif // PCH_LEVEL >= 2
+#if PCH_LEVEL >= 3
+#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.h>
+#include <com/sun/star/uno/TypeClass.hdl>
+#include <com/sun/star/uno/XInterface.hpp>
+#include <cppu/cppudllapi.h>
+#include <cppu/unotype.hxx>
+#include <cppuhelper/cppuhelperdllapi.h>
+#include <cppuhelper/weak.hxx>
+#include <salhelper/thread.hxx>
+#include <typelib/typeclass.h>
+#include <typelib/typedescription.h>
+#include <typelib/uik.h>
+#include <uno/data.h>
+#include <xmlreader/span.hxx>
+#include <xmlreader/xmlreader.hxx>
+#endif // PCH_LEVEL >= 3
+#if PCH_LEVEL >= 4
+#endif // PCH_LEVEL >= 4
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/qa/unit/test.cxx b/configmgr/qa/unit/test.cxx
new file mode 100644
index 000000000..daa207008
--- /dev/null
+++ b/configmgr/qa/unit/test.cxx
@@ -0,0 +1,470 @@
+/* -*- 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 <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/beans/PropertyChangeEvent.hpp>
+#include <com/sun/star/beans/XPropertyChangeListener.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XPropertyState.hpp>
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/container/XNameReplace.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/lang/EventObject.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/uno/XInterface.hpp>
+#include <com/sun/star/util/XChangesBatch.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <osl/time.h>
+#include <rtl/ref.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/configuration.hxx>
+#include <comphelper/configurationlistener.hxx>
+#include <cppunit/TestAssert.h>
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/plugin/TestPlugIn.h>
+#include <officecfg/Office/Math.hxx>
+
+namespace {
+
+class Test: public CppUnit::TestFixture {
+public:
+ virtual void setUp() override;
+
+ void testKeyFetch();
+ void testKeySet();
+ void testKeyReset();
+ void testSetSetMemberName();
+ void testInsertSetMember();
+ void testReadCommands();
+ void testListener();
+ void testRecursive();
+ void testCrossThreads();
+
+ css::uno::Any getKey(
+ OUString const & path, OUString const & relative) const;
+
+ void setKey(
+ OUString const & path, OUString const & name,
+ css::uno::Any const & value) const;
+
+ bool resetKey(OUString const & path, OUString const & name) const;
+
+ css::uno::Reference< css::uno::XInterface > createViewAccess(
+ OUString const & path) const;
+
+ css::uno::Reference< css::uno::XInterface > createUpdateAccess(
+ OUString const & path) const;
+
+ CPPUNIT_TEST_SUITE(Test);
+ CPPUNIT_TEST(testKeyFetch);
+ CPPUNIT_TEST(testKeySet);
+ CPPUNIT_TEST(testKeyReset);
+ CPPUNIT_TEST(testSetSetMemberName);
+ CPPUNIT_TEST(testInsertSetMember);
+ CPPUNIT_TEST(testReadCommands);
+ CPPUNIT_TEST(testListener);
+ CPPUNIT_TEST(testRecursive);
+ CPPUNIT_TEST(testCrossThreads);
+ CPPUNIT_TEST_SUITE_END();
+
+private:
+ css::uno::Reference< css::lang::XMultiServiceFactory > provider_;
+};
+
+class RecursiveTest:
+ public cppu::WeakImplHelper< css::beans::XPropertyChangeListener >
+{
+public:
+ RecursiveTest(Test const & theTest, int count, bool * destroyed);
+
+ void test();
+
+protected:
+ virtual ~RecursiveTest() override;
+
+ virtual void step() const = 0;
+
+ Test const & test_;
+
+private:
+ virtual void SAL_CALL disposing(css::lang::EventObject const &) override;
+
+ virtual void SAL_CALL propertyChange(
+ css::beans::PropertyChangeEvent const &) override;
+
+ int count_;
+ bool * destroyed_;
+ css::uno::Reference< css::beans::XPropertySet > properties_;
+};
+
+RecursiveTest::RecursiveTest(
+ Test const & theTest, int count, bool * destroyed):
+ test_(theTest), count_(count), destroyed_(destroyed)
+{}
+
+void RecursiveTest::test()
+{
+ properties_.set(
+ test_.createUpdateAccess(
+ "/org.openoffice.Office.UI.GenericCommands/UserInterface/Commands/"
+ ".uno:WebHtml"),
+ css::uno::UNO_QUERY_THROW);
+ properties_->addPropertyChangeListener("Label", this);
+ step();
+ CPPUNIT_ASSERT_EQUAL(0, count_);
+ css::uno::Reference< css::lang::XComponent >(
+ properties_, css::uno::UNO_QUERY_THROW)->dispose();
+}
+
+RecursiveTest::~RecursiveTest()
+{
+ *destroyed_ = true;
+}
+
+void RecursiveTest::disposing(css::lang::EventObject const & Source)
+{
+ CPPUNIT_ASSERT(properties_.is());
+ CPPUNIT_ASSERT_EQUAL(
+ css::uno::Reference<css::uno::XInterface>(
+ properties_, css::uno::UNO_QUERY_THROW),
+ Source.Source);
+ properties_.clear();
+}
+
+void RecursiveTest::propertyChange(css::beans::PropertyChangeEvent const & evt)
+{
+ CPPUNIT_ASSERT_EQUAL(
+ css::uno::Reference<css::uno::XInterface>(
+ properties_, css::uno::UNO_QUERY_THROW),
+ evt.Source);
+ CPPUNIT_ASSERT_EQUAL( OUString("Label"), evt.PropertyName );
+ if (count_ > 0) {
+ --count_;
+ step();
+ }
+}
+
+class SimpleRecursiveTest: public RecursiveTest {
+public:
+ SimpleRecursiveTest(Test const & theTest, int count, bool * destroyed);
+
+private:
+ virtual void step() const override;
+};
+
+SimpleRecursiveTest::SimpleRecursiveTest(
+ Test const & theTest, int count, bool * destroyed):
+ RecursiveTest(theTest, count, destroyed)
+{}
+
+void SimpleRecursiveTest::step() const
+{
+ test_.setKey(
+ "/org.openoffice.Office.UI.GenericCommands/UserInterface/Commands/"
+ ".uno:WebHtml",
+ "Label",
+ css::uno::Any(OUString("step")));
+}
+
+void Test::setUp()
+{
+ provider_ = css::configuration::theDefaultProvider::get(
+ comphelper::getProcessComponentContext() );
+}
+
+void Test::testKeyFetch()
+{
+ OUString s;
+ CPPUNIT_ASSERT(
+ getKey(
+ "/org.openoffice.System",
+ "L10N/Locale") >>=
+ s);
+}
+
+void Test::testKeySet()
+{
+ setKey(
+ "/org.openoffice.System/L10N",
+ "Locale",
+ css::uno::Any(OUString("com.sun.star.configuration.backend.LocaleBackend UILocale")));
+ OUString s;
+ CPPUNIT_ASSERT(
+ getKey(
+ "/org.openoffice.System/L10N",
+ "Locale") >>=
+ s);
+ CPPUNIT_ASSERT_EQUAL( OUString("com.sun.star.configuration.backend.LocaleBackend UILocale"), s );
+}
+
+void Test::testKeyReset()
+{
+ if (resetKey(
+ "/org.openoffice.System/L10N",
+ "Locale"))
+ {
+ OUString s;
+ CPPUNIT_ASSERT(
+ getKey(
+ "/org.openoffice.System/L10N",
+ "Locale") >>=
+ s);
+ CPPUNIT_ASSERT_EQUAL( OUString("com.sun.star.configuration.backend.LocaleBackend Locale"), s );
+ }
+}
+
+void Test::testSetSetMemberName()
+{
+ OUString s;
+ CPPUNIT_ASSERT(
+ getKey(
+ "/org.openoffice.Office.UI.GenericCommands/UserInterface/Commands/"
+ ".uno:FontworkShapeType",
+ "Label") >>=
+ s);
+ CPPUNIT_ASSERT_EQUAL( OUString("Fontwork Shape"), s );
+
+ css::uno::Reference< css::container::XNameAccess > access(
+ createUpdateAccess(
+ "/org.openoffice.Office.UI.GenericCommands/UserInterface/"
+ "Commands"),
+ css::uno::UNO_QUERY_THROW);
+ css::uno::Reference< css::container::XNamed > member;
+ access->getByName(".uno:FontworkGalleryFloater") >>= member;
+ CPPUNIT_ASSERT(member.is());
+ member->setName(".uno:FontworkShapeType");
+ css::uno::Reference< css::util::XChangesBatch >(
+ access, css::uno::UNO_QUERY_THROW)->commitChanges();
+ css::uno::Reference< css::lang::XComponent >(
+ access, css::uno::UNO_QUERY_THROW)->dispose();
+
+ CPPUNIT_ASSERT(
+ getKey(
+ "/org.openoffice.Office.UI.GenericCommands/UserInterface/Commands/"
+ ".uno:FontworkShapeType",
+ "Label") >>=
+ s);
+ CPPUNIT_ASSERT_EQUAL( OUString("Insert Fontwork"), s );
+}
+
+void Test::testInsertSetMember() {
+ css::uno::Reference<css::container::XNameContainer> access(
+ createUpdateAccess(
+ "/org.openoffice.Office.UI.GenericCommands/UserInterface/Commands"),
+ css::uno::UNO_QUERY_THROW);
+ css::uno::Reference<css::uno::XInterface> member;
+ member.set(
+ css::uno::Reference<css::lang::XSingleServiceFactory>(
+ access, css::uno::UNO_QUERY_THROW)->createInstance());
+ CPPUNIT_ASSERT(member.is());
+ access->insertByName("A", css::uno::Any(member));
+ member.set(
+ css::uno::Reference<css::lang::XSingleServiceFactory>(
+ access, css::uno::UNO_QUERY_THROW)->createInstance());
+ CPPUNIT_ASSERT(member.is());
+ try {
+ access->insertByName("", css::uno::Any(member));
+ CPPUNIT_FAIL("expected IllegalArgumentException");
+ } catch (css::lang::IllegalArgumentException &) {}
+ try {
+ access->insertByName("\x01", css::uno::Any(member));
+ CPPUNIT_FAIL("expected IllegalArgumentException");
+ } catch (css::lang::IllegalArgumentException &) {}
+ try {
+ access->insertByName("a/b", css::uno::Any(member));
+ } catch (css::lang::IllegalArgumentException &) {
+ CPPUNIT_FAIL("unexpected IllegalArgumentException");
+ }
+ css::uno::Reference<css::util::XChangesBatch>(
+ access, css::uno::UNO_QUERY_THROW)->commitChanges();
+ css::uno::Reference<css::lang::XComponent>(
+ access, css::uno::UNO_QUERY_THROW)->dispose();
+}
+
+void Test::testReadCommands()
+{
+ css::uno::Reference< css::container::XNameAccess > access(
+ createViewAccess(
+ "/org.openoffice.Office.UI.GenericCommands/UserInterface/"
+ "Commands"),
+ css::uno::UNO_QUERY_THROW);
+ const css::uno::Sequence< OUString > names(access->getElementNames());
+
+ /*CPPUNIT_ASSERT_EQUAL(749, names.getLength());*/
+ // testSetSetMemberName() already removed ".uno:FontworkGalleryFloater"
+ sal_uInt32 n = osl_getGlobalTimer();
+ for (int i = 0; i < 8; ++i) {
+ for (OUString const & childName : names) {
+ css::uno::Reference< css::container::XNameAccess > child;
+ if (access->getByName(childName) >>= child) {
+ CPPUNIT_ASSERT(child.is());
+ child->getByName("Label");
+ child->getByName("ContextLabel");
+ child->getByName("Properties");
+ }
+ }
+ }
+ n = osl_getGlobalTimer() - n;
+ printf("Reading elements took %" SAL_PRIuUINT32 " ms\n", n);
+ css::uno::Reference< css::lang::XComponent >(
+ access, css::uno::UNO_QUERY_THROW)->dispose();
+}
+
+void Test::testListener()
+{
+ OUString aRandomPath = "/org.openoffice.Office.Math/View";
+
+ // test with no props.
+ {
+ rtl::Reference xListener(
+ new comphelper::ConfigurationListener(aRandomPath));
+ xListener->dispose();
+ }
+
+ // test some changes
+ {
+ rtl::Reference xListener(
+ new comphelper::ConfigurationListener(aRandomPath));
+
+ comphelper::ConfigurationListenerProperty<bool> aSetting(xListener, "AutoRedraw");
+ CPPUNIT_ASSERT_MESSAGE("check AutoRedraw defaults to true", aSetting.get());
+
+ // set to false
+ {
+ std::shared_ptr< comphelper::ConfigurationChanges > xChanges(
+ comphelper::ConfigurationChanges::create());
+ officecfg::Office::Math::View::AutoRedraw::set(false, xChanges);
+ xChanges->commit();
+ }
+ CPPUNIT_ASSERT_MESSAGE("listener failed to trigger", !aSetting.get());
+
+ // set to true
+ {
+ std::shared_ptr< comphelper::ConfigurationChanges > xChanges(
+ comphelper::ConfigurationChanges::create());
+ officecfg::Office::Math::View::AutoRedraw::set(true, xChanges);
+ xChanges->commit();
+ }
+ CPPUNIT_ASSERT_MESSAGE("listener failed to trigger", aSetting.get());
+
+ xListener->dispose();
+ }
+}
+
+void Test::testRecursive()
+{
+ bool destroyed = false;
+ rtl::Reference< RecursiveTest >(
+ new SimpleRecursiveTest(*this, 100, &destroyed))->test();
+ CPPUNIT_ASSERT(destroyed);
+}
+
+void Test::testCrossThreads()
+{
+ bool destroyed = false;
+ rtl::Reference< RecursiveTest >(
+ new SimpleRecursiveTest(*this, 10, &destroyed))->test();
+ CPPUNIT_ASSERT(destroyed);
+}
+
+css::uno::Any Test::getKey(
+ OUString const & path, OUString const & relative) const
+{
+ css::uno::Reference< css::container::XHierarchicalNameAccess > access(
+ createViewAccess(path), css::uno::UNO_QUERY_THROW);
+ css::uno::Any value(access->getByHierarchicalName(relative));
+ css::uno::Reference< css::lang::XComponent >(
+ access, css::uno::UNO_QUERY_THROW)->dispose();
+ return value;
+}
+
+void Test::setKey(
+ OUString const & path, OUString const & name,
+ css::uno::Any const & value) const
+{
+ css::uno::Reference< css::container::XNameReplace > access(
+ createUpdateAccess(path), css::uno::UNO_QUERY_THROW);
+ access->replaceByName(name, value);
+ css::uno::Reference< css::util::XChangesBatch >(
+ access, css::uno::UNO_QUERY_THROW)->commitChanges();
+ css::uno::Reference< css::lang::XComponent >(
+ access, css::uno::UNO_QUERY_THROW)->dispose();
+}
+
+bool Test::resetKey(OUString const & path, OUString const & name)
+ const
+{
+ //TODO: support setPropertyToDefault
+ css::uno::Reference< css::util::XChangesBatch > access(
+ createUpdateAccess(path), css::uno::UNO_QUERY_THROW);
+ css::uno::Reference< css::beans::XPropertyState > state(
+ access, css::uno::UNO_QUERY);
+ if (!state.is()) {
+ return false;
+ }
+ state->setPropertyToDefault(name);
+ access->commitChanges();
+ css::uno::Reference< css::lang::XComponent >(
+ access, css::uno::UNO_QUERY_THROW)->dispose();
+ return true;
+}
+
+css::uno::Reference< css::uno::XInterface > Test::createViewAccess(
+ OUString const & path) const
+{
+ css::uno::Any arg(
+ css::beans::NamedValue(
+ "nodepath",
+ css::uno::Any(path)));
+ return provider_->createInstanceWithArguments(
+ "com.sun.star.configuration.ConfigurationAccess",
+ css::uno::Sequence< css::uno::Any >(&arg, 1));
+}
+
+css::uno::Reference< css::uno::XInterface > Test::createUpdateAccess(
+ OUString const & path) const
+{
+ css::uno::Any arg(
+ css::beans::NamedValue(
+ "nodepath",
+ css::uno::Any(path)));
+ return provider_->createInstanceWithArguments(
+ "com.sun.star.configuration.ConfigurationUpdateAccess",
+ css::uno::Sequence< css::uno::Any >(&arg, 1));
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(Test);
+
+}
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/qa/unit/urebootstrap.ini b/configmgr/qa/unit/urebootstrap.ini
new file mode 100644
index 000000000..24b5e0f71
--- /dev/null
+++ b/configmgr/qa/unit/urebootstrap.ini
@@ -0,0 +1,20 @@
+#
+# 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 .
+#
+
+[Bootstrap]
+BRAND_BASE_DIR = $ORIGIN/brand
diff --git a/configmgr/qa/unoapi/module.sce b/configmgr/qa/unoapi/module.sce
new file mode 100644
index 000000000..1dd452554
--- /dev/null
+++ b/configmgr/qa/unoapi/module.sce
@@ -0,0 +1,20 @@
+#
+# 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 .
+#
+
+-o configmgr.ConfigurationProvider
+-o configmgr.DefaultProvider
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: */
diff --git a/configmgr/source/access.hxx b/configmgr/source/access.hxx
new file mode 100644
index 000000000..51e43d5bc
--- /dev/null
+++ b/configmgr/source/access.hxx
@@ -0,0 +1,450 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <set>
+#include <vector>
+#include "config_map.hxx"
+
+#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/XProperty.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XPropertySetInfo.hpp>
+#include <com/sun/star/container/XContainer.hpp>
+#include <com/sun/star/container/XHierarchicalName.hpp>
+#include <com/sun/star/container/XHierarchicalNameReplace.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XTypeProvider.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <cppuhelper/weak.hxx>
+#include <osl/interlck.h>
+#include <rtl/ref.hxx>
+#include <sal/types.h>
+
+#include "modifications.hxx"
+#include "type.hxx"
+
+namespace com::sun::star {
+ namespace beans {
+ class XHierarchicalPropertySetInfo;
+ class XPropertiesChangeListener;
+ class XPropertyChangeListener;
+ class XVetoableChangeListener;
+ struct Property;
+ }
+ namespace container { class XContainerListener; }
+ namespace lang { class XEventListener; }
+ namespace uno {
+ class Any;
+ class Type;
+ class XInterface;
+ }
+ namespace util { struct ElementChange; }
+}
+
+namespace configmgr {
+
+class Broadcaster;
+class ChildAccess;
+class Components;
+class Node;
+class RootAccess;
+
+class Access:
+ public cppu::OWeakObject, public css::lang::XTypeProvider,
+ public css::lang::XServiceInfo,
+ public css::lang::XComponent,
+ public css::container::XHierarchicalNameReplace,
+ public css::container::XContainer,
+ public css::beans::XExactName,
+ public css::beans::XPropertySetInfo,
+ public css::container::XHierarchicalName,
+ public css::container::XNamed,
+ public css::beans::XProperty,
+ public css::beans::XPropertySet,
+ public css::beans::XMultiPropertySet,
+ public css::beans::XHierarchicalPropertySet,
+ public css::beans::XMultiHierarchicalPropertySet,
+ public css::beans::XHierarchicalPropertySetInfo,
+ public css::container::XNameContainer,
+ public css::lang::XSingleServiceFactory
+{
+public:
+ oslInterlockedCount acquireCounting();
+
+ void releaseNondeleting();
+
+ bool isValue();
+
+ void markChildAsModified(rtl::Reference< ChildAccess > const & child);
+ void releaseChild(OUString const & name);
+
+ virtual std::vector<OUString> getAbsolutePath() = 0;
+ virtual std::vector<OUString> getRelativePath() = 0;
+
+ virtual OUString getRelativePathRepresentation() = 0;
+ virtual rtl::Reference< Node > getNode() = 0;
+
+ virtual bool isFinalized() = 0;
+
+ virtual void initBroadcaster(
+ Modifications::Node const & modifications, Broadcaster * broadcaster);
+
+ using OWeakObject::acquire;
+ using OWeakObject::release;
+
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL
+ getTypes() override;
+
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL
+ getImplementationId() override;
+
+ virtual OUString SAL_CALL getImplementationName() override;
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getSupportedServiceNames() override;
+
+ virtual void SAL_CALL dispose() override;
+
+ virtual void SAL_CALL addEventListener(
+ css::uno::Reference< css::lang::XEventListener >
+ const & xListener) override;
+
+ virtual void SAL_CALL removeEventListener(
+ css::uno::Reference< css::lang::XEventListener >
+ const & aListener) override;
+
+ virtual css::uno::Type SAL_CALL getElementType() override;
+
+ virtual sal_Bool SAL_CALL hasElements() override;
+
+ virtual css::uno::Any SAL_CALL getByName(
+ OUString const & aName) override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getElementNames() override;
+
+ virtual sal_Bool SAL_CALL hasByName(OUString const & aName) override;
+
+ virtual css::uno::Any SAL_CALL getByHierarchicalName(
+ OUString const & aName) override;
+
+ virtual sal_Bool SAL_CALL hasByHierarchicalName(OUString const & aName) override;
+
+ virtual void SAL_CALL replaceByHierarchicalName(
+ OUString const & aName, css::uno::Any const & aElement) override;
+
+ virtual void SAL_CALL addContainerListener(
+ css::uno::Reference<
+ css::container::XContainerListener > const & xListener) override;
+
+ virtual void SAL_CALL removeContainerListener(
+ css::uno::Reference<
+ css::container::XContainerListener > const & xListener) override;
+
+ virtual OUString SAL_CALL getExactName(
+ OUString const & aApproximateName) override;
+
+ virtual css::uno::Sequence< css::beans::Property >
+ SAL_CALL getProperties() override;
+
+ virtual css::beans::Property SAL_CALL getPropertyByName(
+ OUString const & aName) override;
+
+ virtual sal_Bool SAL_CALL hasPropertyByName(OUString const & Name) override;
+
+ virtual OUString SAL_CALL getHierarchicalName() override;
+
+ virtual OUString SAL_CALL composeHierarchicalName(
+ OUString const & aRelativeName) override;
+
+ virtual OUString SAL_CALL getName() override;
+
+ virtual void SAL_CALL setName(OUString const & aName) override;
+
+ virtual css::beans::Property SAL_CALL getAsProperty() override;
+
+ virtual
+ css::uno::Reference< css::beans::XPropertySetInfo >
+ SAL_CALL getPropertySetInfo() override;
+
+ virtual void SAL_CALL setPropertyValue(
+ OUString const & aPropertyName,
+ css::uno::Any const & aValue) override;
+
+ virtual css::uno::Any SAL_CALL getPropertyValue(
+ OUString const & PropertyName) override;
+
+ virtual void SAL_CALL addPropertyChangeListener(
+ OUString const & aPropertyName,
+ css::uno::Reference<
+ css::beans::XPropertyChangeListener > const & xListener) override;
+
+ virtual void SAL_CALL removePropertyChangeListener(
+ OUString const & aPropertyName,
+ css::uno::Reference<
+ css::beans::XPropertyChangeListener > const & aListener) override;
+
+ virtual void SAL_CALL addVetoableChangeListener(
+ OUString const & PropertyName,
+ css::uno::Reference<
+ css::beans::XVetoableChangeListener > const & aListener) override;
+
+ virtual void SAL_CALL removeVetoableChangeListener(
+ OUString const & PropertyName,
+ css::uno::Reference<
+ css::beans::XVetoableChangeListener > const & aListener) override;
+
+ virtual void SAL_CALL setPropertyValues(
+ css::uno::Sequence< OUString > const & aPropertyNames,
+ css::uno::Sequence< css::uno::Any > const &
+ aValues) override;
+
+ virtual css::uno::Sequence< css::uno::Any > SAL_CALL
+ getPropertyValues(
+ css::uno::Sequence< OUString > const & aPropertyNames) override;
+
+ virtual void SAL_CALL addPropertiesChangeListener(
+ css::uno::Sequence< OUString > const & aPropertyNames,
+ css::uno::Reference<
+ css::beans::XPropertiesChangeListener > const &
+ xListener) override;
+
+ virtual void SAL_CALL removePropertiesChangeListener(
+ css::uno::Reference<
+ css::beans::XPropertiesChangeListener > const &
+ xListener) override;
+
+ virtual void SAL_CALL firePropertiesChangeEvent(
+ css::uno::Sequence< OUString > const & aPropertyNames,
+ css::uno::Reference<
+ css::beans::XPropertiesChangeListener > const &
+ xListener) override;
+
+ virtual
+ css::uno::Reference<
+ css::beans::XHierarchicalPropertySetInfo > SAL_CALL
+ getHierarchicalPropertySetInfo() override;
+
+ virtual void SAL_CALL setHierarchicalPropertyValue(
+ OUString const & aHierarchicalPropertyName,
+ css::uno::Any const & aValue) override;
+
+ virtual css::uno::Any SAL_CALL getHierarchicalPropertyValue(
+ OUString const & aHierarchicalPropertyName) override;
+
+ virtual void SAL_CALL setHierarchicalPropertyValues(
+ css::uno::Sequence< OUString > const &
+ aHierarchicalPropertyNames,
+ css::uno::Sequence< css::uno::Any > const &
+ Values) override;
+
+ virtual css::uno::Sequence< css::uno::Any > SAL_CALL
+ getHierarchicalPropertyValues(
+ css::uno::Sequence< OUString > const &
+ aHierarchicalPropertyNames) override;
+
+ virtual css::beans::Property SAL_CALL
+ getPropertyByHierarchicalName(OUString const & aHierarchicalName) override;
+
+ virtual sal_Bool SAL_CALL hasPropertyByHierarchicalName(
+ OUString const & aHierarchicalName) override;
+
+ virtual void SAL_CALL replaceByName(
+ OUString const & aName, css::uno::Any const & aElement) override;
+
+ virtual void SAL_CALL insertByName(
+ OUString const & aName, css::uno::Any const & aElement) override;
+
+ virtual void SAL_CALL removeByName(OUString const & aName) override;
+
+ virtual css::uno::Reference< css::uno::XInterface >
+ SAL_CALL createInstance() override;
+
+ virtual css::uno::Reference< css::uno::XInterface >
+ SAL_CALL createInstanceWithArguments(
+ css::uno::Sequence< css::uno::Any > const &
+ aArguments) override;
+
+protected:
+ explicit Access(Components & components);
+
+ virtual ~Access() override;
+
+ virtual const OUString & getNameInternal() = 0;
+ virtual rtl::Reference< RootAccess > getRootAccess() = 0;
+ virtual rtl::Reference< Access > getParentAccess() = 0;
+
+ virtual void addTypes(std::vector< css::uno::Type > * types)
+ const = 0;
+
+ virtual void addSupportedServiceNames(
+ std::vector<OUString> * services) = 0;
+
+ virtual void initDisposeBroadcaster(Broadcaster * broadcaster);
+ virtual void clearListeners() noexcept;
+
+ virtual css::uno::Any SAL_CALL queryInterface(
+ css::uno::Type const & aType) override;
+
+ Components & getComponents() const { return components_;}
+
+ void checkLocalizedPropertyAccess();
+
+ rtl::Reference< Node > getParentNode();
+ rtl::Reference< ChildAccess > getChild(OUString const & name);
+ std::vector< rtl::Reference< ChildAccess > > getAllChildren();
+
+ void checkValue(
+ css::uno::Any const & value, Type type, bool nillable);
+
+ void insertLocalizedValueChild(
+ OUString const & name, css::uno::Any const & value,
+ Modifications * localModifications);
+
+ void reportChildChanges(
+ std::vector< css::util::ElementChange > * changes);
+
+ void commitChildChanges(bool valid, Modifications * globalModifications);
+
+ void initBroadcasterAndChanges(
+ Modifications::Node const & modifications, Broadcaster * broadcaster,
+ std::vector< css::util::ElementChange > * changes);
+
+ bool isDisposed() const { return disposed_;}
+
+private:
+ Access(const Access&) = delete;
+ Access& operator=(const Access&) = delete;
+
+ struct ModifiedChild {
+ rtl::Reference< ChildAccess > child;
+ bool directlyModified;
+
+ ModifiedChild();
+
+ ModifiedChild(
+ rtl::Reference< ChildAccess > theChild,
+ bool theDirectlyModified);
+ };
+
+ typedef config_map< ModifiedChild > ModifiedChildren;
+
+ rtl::Reference< ChildAccess > getModifiedChild(
+ ModifiedChildren::iterator const & childIterator);
+
+ rtl::Reference< ChildAccess > getUnmodifiedChild(
+ OUString const & name);
+
+ rtl::Reference< ChildAccess > getSubChild(OUString const & path);
+
+ bool setChildProperty(
+ OUString const & name, css::uno::Any const & value,
+ Modifications * localModifications);
+
+ css::beans::Property asProperty();
+
+ bool getByNameFast(const OUString & name, css::uno::Any & value);
+ rtl::Reference< ChildAccess > createUnmodifiedChild(const OUString &name,
+ const rtl::Reference< Node > &node);
+
+ void checkFinalized();
+
+ void checkKnownProperty(OUString const & descriptor);
+
+ rtl::Reference< ChildAccess > getFreeSetMember( css::uno::Any const & value);
+
+ rtl::Reference< Access > getNotificationRoot();
+
+ typedef config_map< ChildAccess * > WeakChildMap;
+
+ typedef
+ std::multiset<
+ css::uno::Reference<
+ css::lang::XEventListener > >
+ DisposeListeners;
+
+ typedef
+ std::multiset<
+ css::uno::Reference<
+ css::container::XContainerListener > >
+ ContainerListeners;
+
+ typedef
+ std::multiset<
+ css::uno::Reference<
+ css::beans::XPropertyChangeListener > >
+ PropertyChangeListenersElement;
+
+ typedef config_map< PropertyChangeListenersElement >
+ PropertyChangeListeners;
+
+ typedef
+ std::multiset<
+ css::uno::Reference<
+ css::beans::XVetoableChangeListener > >
+ VetoableChangeListenersElement;
+
+ typedef config_map< VetoableChangeListenersElement >
+ VetoableChangeListeners;
+
+ typedef
+ std::multiset<
+ css::uno::Reference<
+ css::beans::XPropertiesChangeListener > >
+ PropertiesChangeListeners;
+
+ Components & components_;
+ ModifiedChildren modifiedChildren_;
+ WeakChildMap cachedChildren_;
+ DisposeListeners disposeListeners_;
+ ContainerListeners containerListeners_;
+ PropertyChangeListeners propertyChangeListeners_;
+ VetoableChangeListeners vetoableChangeListeners_;
+ PropertiesChangeListeners propertiesChangeListeners_;
+ bool disposed_;
+
+ std::shared_ptr<osl::Mutex> lock_;
+
+#if !defined NDEBUG
+protected:
+ enum {
+ IS_ANY = 0, IS_GROUP = 0x01, IS_SET = 0x02, IS_EXTENSIBLE = 0x04,
+ IS_GROUP_MEMBER = 0x08, IS_SET_MEMBER = 0x10, IS_UPDATE = 0x20 };
+ bool thisIs(int what);
+#endif
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/additions.hxx b/configmgr/source/additions.hxx
new file mode 100644
index 000000000..9f50f1878
--- /dev/null
+++ b/configmgr/source/additions.hxx
@@ -0,0 +1,34 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <rtl/ustring.hxx>
+
+#include <vector>
+
+namespace configmgr
+{
+// Additions is a list of configuration node paths
+typedef std::vector<std::vector<OUString>> Additions;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/broadcaster.cxx b/configmgr/source/broadcaster.cxx
new file mode 100644
index 000000000..f1830ee81
--- /dev/null
+++ b/configmgr/source/broadcaster.cxx
@@ -0,0 +1,230 @@
+/* -*- 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 <com/sun/star/beans/XPropertiesChangeListener.hpp>
+#include <com/sun/star/beans/XPropertyChangeListener.hpp>
+#include <com/sun/star/container/XContainerListener.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/lang/XEventListener.hpp>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Exception.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/XInterface.hpp>
+#include <com/sun/star/util/XChangesListener.hpp>
+#include <cppuhelper/exc_hlp.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/ustring.hxx>
+#include <utility>
+
+#include "broadcaster.hxx"
+
+namespace configmgr {
+
+namespace {
+
+void appendMessage(
+ OUStringBuffer & buffer, css::uno::Exception const & exception)
+{
+ buffer.append("; ");
+ buffer.append(exception.Message);
+}
+
+}
+
+void Broadcaster::addDisposeNotification(
+ css::uno::Reference< css::lang::XEventListener > const & listener,
+ css::lang::EventObject const & event)
+{
+ disposeNotifications_.emplace_back(listener, event);
+}
+
+void Broadcaster::addContainerElementReplacedNotification(
+ css::uno::Reference< css::container::XContainerListener > const & listener,
+ css::container::ContainerEvent const & event)
+{
+ containerElementReplacedNotifications_.emplace_back(listener, event);
+}
+
+void Broadcaster::addContainerElementInsertedNotification(
+ css::uno::Reference< css::container::XContainerListener > const & listener,
+ css::container::ContainerEvent const & event)
+{
+ containerElementInsertedNotifications_.emplace_back(listener, event);
+}
+
+void Broadcaster::addContainerElementRemovedNotification(
+ css::uno::Reference< css::container::XContainerListener > const & listener,
+ css::container::ContainerEvent const & event)
+{
+ containerElementRemovedNotifications_.emplace_back(listener, event);
+}
+
+void Broadcaster::addPropertyChangeNotification(
+ css::uno::Reference< css::beans::XPropertyChangeListener > const & listener,
+ css::beans::PropertyChangeEvent const & event)
+{
+ propertyChangeNotifications_.emplace_back(listener, event);
+}
+
+void Broadcaster::addPropertiesChangeNotification(
+ css::uno::Reference< css::beans::XPropertiesChangeListener > const &
+ listener,
+ css::uno::Sequence< css::beans::PropertyChangeEvent > const & event)
+{
+ propertiesChangeNotifications_.emplace_back(listener, event);
+}
+
+void Broadcaster::addChangesNotification(
+ css::uno::Reference< css::util::XChangesListener > const & listener,
+ css::util::ChangesEvent const & event)
+{
+ changesNotifications_.emplace_back(listener, event);
+}
+
+void Broadcaster::send() {
+ css::uno::Any exception;
+ OUStringBuffer messages;
+ for (auto& rNotification : disposeNotifications_) {
+ try {
+ rNotification.listener->disposing(rNotification.event);
+ } catch (css::lang::DisposedException &) {
+ } catch (css::uno::Exception & e) {
+ exception = cppu::getCaughtException();
+ appendMessage(messages, e);
+ }
+ }
+ for (auto& rNotification : containerElementInsertedNotifications_)
+ {
+ try {
+ rNotification.listener->elementInserted(rNotification.event);
+ } catch (css::lang::DisposedException &) {
+ } catch (css::uno::Exception & e) {
+ exception = cppu::getCaughtException();
+ appendMessage(messages, e);
+ }
+ }
+ for (auto& rNotification : containerElementRemovedNotifications_)
+ {
+ try {
+ rNotification.listener->elementRemoved(rNotification.event);
+ } catch (css::lang::DisposedException &) {
+ } catch (css::uno::Exception & e) {
+ exception = cppu::getCaughtException();
+ appendMessage(messages, e);
+ }
+ }
+ for (auto& rNotification : containerElementReplacedNotifications_)
+ {
+ try {
+ rNotification.listener->elementReplaced(rNotification.event);
+ } catch (css::lang::DisposedException &) {
+ } catch (css::uno::Exception & e) {
+ exception = cppu::getCaughtException();
+ appendMessage(messages, e);
+ }
+ }
+ for (auto& rNotification : propertyChangeNotifications_)
+ {
+ try {
+ rNotification.listener->propertyChange(rNotification.event);
+ } catch (css::lang::DisposedException &) {
+ } catch (css::uno::Exception & e) {
+ exception = cppu::getCaughtException();
+ appendMessage(messages, e);
+ }
+ }
+ for (auto& rNotification : propertiesChangeNotifications_)
+ {
+ try {
+ rNotification.listener->propertiesChange(rNotification.event);
+ } catch (css::lang::DisposedException &) {
+ } catch (css::uno::Exception & e) {
+ exception = cppu::getCaughtException();
+ appendMessage(messages, e);
+ }
+ }
+ for (auto& rNotification : changesNotifications_) {
+ try {
+ rNotification.listener->changesOccurred(rNotification.event);
+ } catch (css::lang::DisposedException &) {
+ } catch (css::uno::Exception & e) {
+ exception = cppu::getCaughtException();
+ appendMessage(messages, e);
+ }
+ }
+ if (exception.hasValue()) {
+ throw css::lang::WrappedTargetRuntimeException(
+ ("configmgr exceptions during listener notification" +
+ messages.makeStringAndClear()),
+ css::uno::Reference< css::uno::XInterface >(),
+ exception);
+ }
+}
+
+Broadcaster::DisposeNotification::DisposeNotification(
+ css::uno::Reference< css::lang::XEventListener > const & theListener,
+ css::lang::EventObject theEvent):
+ listener(theListener), event(std::move(theEvent))
+{
+ assert(theListener.is());
+}
+
+Broadcaster::ContainerNotification::ContainerNotification(
+ css::uno::Reference< css::container::XContainerListener > const &
+ theListener,
+ css::container::ContainerEvent theEvent):
+ listener(theListener), event(std::move(theEvent))
+{
+ assert(theListener.is());
+}
+
+Broadcaster::PropertyChangeNotification::PropertyChangeNotification(
+ css::uno::Reference< css::beans::XPropertyChangeListener > const &
+ theListener,
+ css::beans::PropertyChangeEvent theEvent):
+ listener(theListener), event(std::move(theEvent))
+{
+ assert(theListener.is());
+}
+
+Broadcaster::PropertiesChangeNotification::PropertiesChangeNotification(
+ css::uno::Reference< css::beans::XPropertiesChangeListener > const &
+ theListener,
+ css::uno::Sequence< css::beans::PropertyChangeEvent > const & theEvent):
+ listener(theListener), event(theEvent)
+{
+ assert(theListener.is());
+}
+
+Broadcaster::ChangesNotification::ChangesNotification(
+ css::uno::Reference< css::util::XChangesListener > const & theListener,
+ css::util::ChangesEvent theEvent):
+ listener(theListener), event(std::move(theEvent))
+{
+ assert(theListener.is());
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/broadcaster.hxx b/configmgr/source/broadcaster.hxx
new file mode 100644
index 000000000..d0c461e73
--- /dev/null
+++ b/configmgr/source/broadcaster.hxx
@@ -0,0 +1,139 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <vector>
+
+#include <com/sun/star/beans/PropertyChangeEvent.hpp>
+#include <com/sun/star/container/ContainerEvent.hpp>
+#include <com/sun/star/lang/EventObject.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/util/ChangesEvent.hpp>
+
+namespace com::sun::star {
+ namespace beans {
+ class XPropertiesChangeListener;
+ class XPropertyChangeListener;
+ }
+ namespace container { class XContainerListener; }
+ namespace lang { class XEventListener; }
+ namespace util { class XChangesListener; }
+}
+
+namespace configmgr {
+
+class Broadcaster {
+public:
+ Broadcaster() {}
+
+ void addDisposeNotification(
+ css::uno::Reference< css::lang::XEventListener > const & listener,
+ css::lang::EventObject const & event);
+
+ void addContainerElementInsertedNotification(
+ css::uno::Reference< css::container::XContainerListener > const & listener,
+ css::container::ContainerEvent const & event);
+
+ void addContainerElementRemovedNotification(
+ css::uno::Reference< css::container::XContainerListener > const & listener,
+ css::container::ContainerEvent const & event);
+
+ void addContainerElementReplacedNotification(
+ css::uno::Reference< css::container::XContainerListener > const & listener,
+ css::container::ContainerEvent const & event);
+
+ void addPropertyChangeNotification(
+ css::uno::Reference< css::beans::XPropertyChangeListener > const & listener,
+ css::beans::PropertyChangeEvent const & event);
+
+ void addPropertiesChangeNotification(
+ css::uno::Reference< css::beans::XPropertiesChangeListener > const & listener,
+ css::uno::Sequence< css::beans::PropertyChangeEvent > const & event);
+
+ void addChangesNotification(
+ css::uno::Reference< css::util::XChangesListener > const & listener,
+ css::util::ChangesEvent const & event);
+
+ void send();
+
+private:
+ Broadcaster(const Broadcaster&) = delete;
+ Broadcaster& operator=(const Broadcaster&) = delete;
+
+ struct DisposeNotification {
+ css::uno::Reference< css::lang::XEventListener > listener;
+ css::lang::EventObject event;
+
+ DisposeNotification(
+ css::uno::Reference< css::lang::XEventListener > const & theListener,
+ css::lang::EventObject theEvent);
+ };
+
+ struct ContainerNotification {
+ css::uno::Reference< css::container::XContainerListener > listener;
+ css::container::ContainerEvent event;
+
+ ContainerNotification(
+ css::uno::Reference< css::container::XContainerListener > const & theListener,
+ css::container::ContainerEvent theEvent);
+ };
+
+ struct PropertyChangeNotification {
+ css::uno::Reference< css::beans::XPropertyChangeListener > listener;
+ css::beans::PropertyChangeEvent event;
+
+ PropertyChangeNotification(
+ css::uno::Reference< css::beans::XPropertyChangeListener > const & theListener,
+ css::beans::PropertyChangeEvent theEvent);
+ };
+
+ struct PropertiesChangeNotification {
+ css::uno::Reference< css::beans::XPropertiesChangeListener > listener;
+ css::uno::Sequence< css::beans::PropertyChangeEvent > event;
+
+ PropertiesChangeNotification(
+ css::uno::Reference< css::beans::XPropertiesChangeListener > const & theListener,
+ css::uno::Sequence< css::beans::PropertyChangeEvent > const & theEvent);
+ };
+
+ struct ChangesNotification {
+ css::uno::Reference< css::util::XChangesListener > listener;
+ css::util::ChangesEvent event;
+
+ ChangesNotification(
+ css::uno::Reference< css::util::XChangesListener > const & theListener,
+ css::util::ChangesEvent theEvent);
+ };
+
+ std::vector< DisposeNotification > disposeNotifications_;
+ std::vector< ContainerNotification > containerElementInsertedNotifications_;
+ std::vector< ContainerNotification > containerElementRemovedNotifications_;
+ std::vector< ContainerNotification > containerElementReplacedNotifications_;
+ std::vector< PropertyChangeNotification > propertyChangeNotifications_;
+ std::vector< PropertiesChangeNotification > propertiesChangeNotifications_;
+ std::vector< ChangesNotification > changesNotifications_;
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/childaccess.cxx b/configmgr/source/childaccess.cxx
new file mode 100644
index 000000000..60a11006b
--- /dev/null
+++ b/configmgr/source/childaccess.cxx
@@ -0,0 +1,350 @@
+/* -*- 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 <utility>
+#include <vector>
+
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/lang/NoSupportException.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/uno/Type.hxx>
+#include <com/sun/star/uno/XInterface.hpp>
+#include <cppu/unotype.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <cppuhelper/weak.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <osl/mutex.hxx>
+#include <rtl/ref.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+
+#include "access.hxx"
+#include "childaccess.hxx"
+#include "components.hxx"
+#include "data.hxx"
+#include "localizedpropertynode.hxx"
+#include "localizedvaluenode.hxx"
+#include "lock.hxx"
+#include "modifications.hxx"
+#include "node.hxx"
+#include "propertynode.hxx"
+#include "rootaccess.hxx"
+#include "type.hxx"
+
+namespace configmgr {
+
+css::uno::Sequence< sal_Int8 > const & ChildAccess::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit theChildAccessUnoTunnelId;
+ return theChildAccessUnoTunnelId.getSeq();
+}
+
+ChildAccess::ChildAccess(
+ Components & components, rtl::Reference< RootAccess > const & root,
+ rtl::Reference< Access > const & parent, OUString name,
+ rtl::Reference< Node > const & node):
+ Access(components), root_(root), parent_(parent), name_(std::move(name)), node_(node),
+ inTransaction_(false),
+ lock_( lock() )
+{
+ assert(root.is() && parent.is() && node.is());
+}
+
+ChildAccess::ChildAccess(
+ Components & components, rtl::Reference< RootAccess > const & root,
+ rtl::Reference< Node > const & node):
+ Access(components), root_(root), node_(node), inTransaction_(false),
+ lock_( lock() )
+{
+ assert(root.is() && node.is());
+}
+
+std::vector<OUString> ChildAccess::getAbsolutePath() {
+ rtl::Reference< Access > parent(getParentAccess());
+ assert(parent.is());
+ std::vector<OUString> path(parent->getAbsolutePath());
+ path.push_back(name_);
+ return path;
+}
+
+std::vector<OUString> ChildAccess::getRelativePath() {
+ std::vector<OUString> path;
+ rtl::Reference< Access > parent(getParentAccess());
+ if (parent.is()) {
+ path = parent->getRelativePath();
+ }
+ path.push_back(name_);
+ return path;
+}
+
+OUString ChildAccess::getRelativePathRepresentation() {
+ OUStringBuffer path(128);
+ rtl::Reference< Access > parent(getParentAccess());
+ if (parent.is()) {
+ path.append(parent->getRelativePathRepresentation());
+ if (!path.isEmpty()) {
+ path.append('/');
+ }
+ }
+ path.append(Data::createSegment(node_->getTemplateName(), name_));
+ return path.makeStringAndClear();
+}
+
+rtl::Reference< Node > ChildAccess::getNode() {
+ return node_;
+}
+
+bool ChildAccess::isFinalized() {
+ return node_->getFinalized() != Data::NO_LAYER ||
+ (parent_.is() && parent_->isFinalized());
+}
+
+const OUString & ChildAccess::getNameInternal() {
+ return name_;
+}
+
+rtl::Reference< RootAccess > ChildAccess::getRootAccess() {
+ return root_;
+}
+
+rtl::Reference< Access > ChildAccess::getParentAccess() {
+ return parent_;
+}
+
+void ChildAccess::acquire() noexcept {
+ Access::acquire();
+}
+
+void ChildAccess::release() noexcept {
+ Access::release();
+}
+
+css::uno::Reference< css::uno::XInterface > ChildAccess::getParent()
+{
+ assert(thisIs(IS_ANY));
+ osl::MutexGuard g(*lock_);
+ checkLocalizedPropertyAccess();
+ return static_cast< cppu::OWeakObject * >(parent_.get());
+}
+
+void ChildAccess::setParent(css::uno::Reference< css::uno::XInterface > const &)
+{
+ assert(thisIs(IS_ANY));
+ osl::MutexGuard g(*lock_);
+ checkLocalizedPropertyAccess();
+ throw css::lang::NoSupportException(
+ "setParent", static_cast< cppu::OWeakObject * >(this));
+}
+
+sal_Int64 ChildAccess::getSomething(
+ css::uno::Sequence< sal_Int8 > const & aIdentifier)
+{
+ assert(thisIs(IS_ANY));
+ osl::MutexGuard g(*lock_);
+ checkLocalizedPropertyAccess();
+ return comphelper::getSomethingImpl(aIdentifier, this);
+}
+
+void ChildAccess::bind(
+ rtl::Reference< RootAccess > const & root,
+ rtl::Reference< Access > const & parent, OUString const & name)
+ noexcept
+{
+ assert(!parent_.is() && root.is() && parent.is() && !name.isEmpty());
+ root_ = root;
+ parent_ = parent;
+ name_ = name;
+}
+
+void ChildAccess::unbind() noexcept {
+ assert(parent_.is());
+ parent_->releaseChild(name_);
+ parent_.clear();
+ inTransaction_ = true;
+}
+
+void ChildAccess::committed() {
+ inTransaction_ = false;
+}
+
+void ChildAccess::setNode(rtl::Reference< Node > const & node) {
+ node_ = node;
+}
+
+void ChildAccess::setProperty(
+ css::uno::Any const & value, Modifications * localModifications)
+{
+ assert(localModifications != nullptr);
+ Type type = TYPE_ERROR;
+ bool isNillable = false;
+ switch (node_->kind()) {
+ case Node::KIND_PROPERTY:
+ {
+ PropertyNode * prop = static_cast< PropertyNode * >(node_.get());
+ type = prop->getStaticType();
+ isNillable = prop->isNillable();
+ }
+ break;
+ case Node::KIND_LOCALIZED_PROPERTY:
+ {
+ OUString locale(getRootAccess()->getLocale());
+ if (!Components::allLocales(locale)) {
+ rtl::Reference< ChildAccess > child(getChild(locale));
+ if (child.is()) {
+ child->setProperty(value, localModifications);
+ } else {
+ insertLocalizedValueChild(
+ locale, value, localModifications);
+ }
+ return;
+ }
+ }
+ break;
+ case Node::KIND_LOCALIZED_VALUE:
+ {
+ LocalizedPropertyNode * locprop =
+ static_cast< LocalizedPropertyNode * >(getParentNode().get());
+ type = locprop->getStaticType();
+ isNillable = locprop->isNillable();
+ }
+ break;
+ default:
+ break;
+ }
+ checkValue(value, type, isNillable);
+ getParentAccess()->markChildAsModified(this);
+ changedValue_.emplace(value);
+ localModifications->add(getRelativePath());
+}
+
+
+css::uno::Any ChildAccess::asValue()
+{
+ if (changedValue_)
+ {
+ return *changedValue_;
+ }
+ css::uno::Any value;
+ if (!asSimpleValue(node_, value, getComponents()))
+ {
+ if (node_->kind() == Node::KIND_LOCALIZED_PROPERTY)
+ {
+ OUString locale(getRootAccess()->getLocale());
+ if (!Components::allLocales(locale)) {
+ rtl::Reference< ChildAccess > child(getChild("*" + locale));
+ // As a last resort, return a nil value even though it may be
+ // illegal for the given property:
+ return child.is() ? child->asValue() : css::uno::Any();
+ }
+ }
+ value <<= css::uno::Reference< css::uno::XInterface >(
+ static_cast< cppu::OWeakObject * >(this));
+ }
+ return value;
+}
+
+/// Can we quickly extract a simple value into value ? if so returns true
+bool ChildAccess::asSimpleValue(const rtl::Reference< Node > &rNode,
+ css::uno::Any &value,
+ Components &components)
+{
+ switch (rNode->kind()) {
+ case Node::KIND_PROPERTY:
+ value = static_cast< PropertyNode * >(rNode.get())->getValue(components);
+ return true;
+ case Node::KIND_LOCALIZED_VALUE:
+ value = static_cast< LocalizedValueNode * >(rNode.get())->getValue();
+ return true;
+ default:
+ return false;
+ }
+}
+
+void ChildAccess::commitChanges(bool valid, Modifications * globalModifications)
+{
+ assert(globalModifications != nullptr);
+ commitChildChanges(valid, globalModifications);
+ if (valid && changedValue_)
+ {
+ std::vector<OUString> path(getAbsolutePath());
+ getComponents().addModification(path);
+ globalModifications->add(path);
+ switch (node_->kind()) {
+ case Node::KIND_PROPERTY:
+ static_cast< PropertyNode * >(node_.get())->setValue(
+ Data::NO_LAYER, *changedValue_);
+ break;
+ case Node::KIND_LOCALIZED_VALUE:
+ static_cast< LocalizedValueNode * >(node_.get())->setValue(
+ Data::NO_LAYER, *changedValue_);
+ break;
+ default:
+ assert(false); // this cannot happen
+ break;
+ }
+ }
+ changedValue_.reset();
+}
+
+ChildAccess::~ChildAccess() {
+ osl::MutexGuard g(*lock_);
+ if (parent_.is()) {
+ parent_->releaseChild(name_);
+ }
+}
+
+void ChildAccess::addTypes(std::vector< css::uno::Type > * types) const {
+ assert(types != nullptr);
+ types->push_back(cppu::UnoType< css::container::XChild >::get());
+ types->push_back(cppu::UnoType< css::lang::XUnoTunnel >::get());
+}
+
+void ChildAccess::addSupportedServiceNames(
+ std::vector<OUString> * services)
+{
+ assert(services != nullptr);
+ services->push_back(
+ getParentNode()->kind() == Node::KIND_GROUP
+ ? OUString("com.sun.star.configuration.GroupElement")
+ : OUString("com.sun.star.configuration.SetElement"));
+}
+
+css::uno::Any ChildAccess::queryInterface(css::uno::Type const & aType)
+{
+ assert(thisIs(IS_ANY));
+ osl::MutexGuard g(*lock_);
+ checkLocalizedPropertyAccess();
+ css::uno::Any res(Access::queryInterface(aType));
+ return res.hasValue()
+ ? res
+ : cppu::queryInterface(
+ aType, static_cast< css::container::XChild * >(this),
+ static_cast< css::lang::XUnoTunnel * >(this));
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/childaccess.hxx b/configmgr/source/childaccess.hxx
new file mode 100644
index 000000000..3b7a5a541
--- /dev/null
+++ b/configmgr/source/childaccess.hxx
@@ -0,0 +1,137 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <memory>
+#include <optional>
+#include <vector>
+
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <rtl/ref.hxx>
+#include <sal/types.h>
+
+#include "access.hxx"
+
+namespace com::sun::star::uno {
+ class Any;
+ class Type;
+ class XInterface;
+}
+
+namespace configmgr {
+
+class Components;
+class Modifications;
+class Node;
+class RootAccess;
+
+class ChildAccess:
+ public Access, public css::container::XChild,
+ public css::lang::XUnoTunnel
+{
+public:
+ static css::uno::Sequence< sal_Int8 > const & getUnoTunnelId();
+
+ ChildAccess(
+ Components & components, rtl::Reference< RootAccess > const & root,
+ rtl::Reference< Access > const & parent, OUString name,
+ rtl::Reference< Node > const & node);
+
+ ChildAccess(
+ Components & components, rtl::Reference< RootAccess > const & root,
+ rtl::Reference< Node > const & node);
+
+ virtual std::vector<OUString> getAbsolutePath() override;
+ virtual std::vector<OUString> getRelativePath() override;
+
+ virtual OUString getRelativePathRepresentation() override;
+ virtual rtl::Reference< Node > getNode() override;
+
+ virtual bool isFinalized() override;
+
+ virtual const OUString & getNameInternal() override;
+
+ virtual rtl::Reference< RootAccess > getRootAccess() override;
+ virtual rtl::Reference< Access > getParentAccess() override;
+
+ virtual void SAL_CALL acquire() noexcept override;
+ virtual void SAL_CALL release() noexcept override;
+
+ virtual css::uno::Reference< css::uno::XInterface >
+ SAL_CALL getParent() override;
+
+ virtual void SAL_CALL setParent(
+ css::uno::Reference< css::uno::XInterface > const &) override;
+
+ virtual sal_Int64 SAL_CALL getSomething(
+ css::uno::Sequence< sal_Int8 > const & aIdentifier) override;
+
+ void bind(
+ rtl::Reference< RootAccess > const & root,
+ rtl::Reference< Access > const & parent, OUString const & name)
+ noexcept;
+
+ void unbind() noexcept;
+
+ bool isInTransaction() const { return inTransaction_; }
+ void committed();
+ void setNode(rtl::Reference< Node > const & node);
+
+ void setProperty(
+ css::uno::Any const & value,
+ Modifications * localModifications);
+
+ css::uno::Any asValue();
+ static bool asSimpleValue(const rtl::Reference< Node > &rNode,
+ css::uno::Any &value,
+ Components &components);
+
+ void commitChanges(bool valid, Modifications * globalModifications);
+
+private:
+ virtual ~ChildAccess() override;
+
+ virtual void addTypes(
+ std::vector< css::uno::Type > * types) const override;
+
+ virtual void addSupportedServiceNames(
+ std::vector<OUString> * services) override;
+
+ virtual css::uno::Any SAL_CALL queryInterface(
+ css::uno::Type const & aType) override;
+
+ rtl::Reference< RootAccess > root_;
+ rtl::Reference< Access > parent_; // null if free node
+ OUString name_;
+ rtl::Reference< Node > node_;
+ std::optional< css::uno::Any > changedValue_;
+ bool inTransaction_;
+ // to determine if a free node can be inserted underneath some root
+ std::shared_ptr<osl::Mutex> lock_;
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/components.cxx b/configmgr/source/components.cxx
new file mode 100644
index 000000000..0693ec26b
--- /dev/null
+++ b/configmgr/source/components.cxx
@@ -0,0 +1,895 @@
+/* -*- 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 <chrono>
+#include <utility>
+#include <vector>
+#include <set>
+
+#include <com/sun/star/beans/Optional.hpp>
+#include <com/sun/star/beans/UnknownPropertyException.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/NoSuchElementException.hpp>
+#include <com/sun/star/lang/WrappedTargetException.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Exception.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/uno/XInterface.hpp>
+#include <cppuhelper/exc_hlp.hxx>
+#include <config_dconf.h>
+#include <config_folders.h>
+#include <osl/conditn.hxx>
+#include <osl/file.hxx>
+#include <osl/mutex.hxx>
+#include <rtl/bootstrap.hxx>
+#include <rtl/ref.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <sal/types.h>
+#include <salhelper/thread.hxx>
+#include <tools/diagnose_ex.h>
+#include <comphelper/backupfilehelper.hxx>
+#include <o3tl/string_view.hxx>
+
+#include "additions.hxx"
+#include "components.hxx"
+#include "data.hxx"
+#include "lock.hxx"
+#include "modifications.hxx"
+#include "node.hxx"
+#include "nodemap.hxx"
+#include "parsemanager.hxx"
+#include "partial.hxx"
+#include "rootaccess.hxx"
+#include "writemodfile.hxx"
+#include "xcdparser.hxx"
+#include "xcuparser.hxx"
+#include "xcsparser.hxx"
+
+#if ENABLE_DCONF
+#include "dconf.hxx"
+#endif
+
+#if defined(_WIN32)
+#include "winreg.hxx"
+#endif
+
+namespace configmgr {
+
+namespace {
+
+struct UnresolvedVectorItem {
+ OUString name;
+ rtl::Reference< ParseManager > manager;
+
+ UnresolvedVectorItem(
+ OUString theName,
+ rtl::Reference< ParseManager > theManager):
+ name(std::move(theName)), manager(std::move(theManager)) {}
+};
+
+typedef std::vector< UnresolvedVectorItem > UnresolvedVector;
+
+void parseXcsFile(
+ OUString const & url, int layer, Data & data, Partial const * partial,
+ Modifications * modifications, Additions * additions)
+{
+ assert(partial == nullptr && modifications == nullptr && additions == nullptr);
+ (void) partial; (void) modifications; (void) additions;
+ bool ok = rtl::Reference< ParseManager >(
+ new ParseManager(url, new XcsParser(layer, data)))->parse(nullptr);
+ assert(ok);
+ (void) ok; // avoid warnings
+}
+
+void parseXcuFile(
+ OUString const & url, int layer, Data & data, Partial const * partial,
+ Modifications * modifications, Additions * additions)
+{
+ bool ok = rtl::Reference< ParseManager >(
+ new ParseManager(
+ url,
+ new XcuParser(layer, data, partial, modifications, additions)))->
+ parse(nullptr);
+ assert(ok);
+ (void) ok; // avoid warnings
+}
+
+OUString expand(OUString const & str) {
+ OUString s(str);
+ rtl::Bootstrap::expandMacros(s); //TODO: detect failure
+ return s;
+}
+
+bool canRemoveFromLayer(int layer, rtl::Reference< Node > const & node) {
+ assert(node.is());
+ if (node->getLayer() > layer && node->getLayer() < Data::NO_LAYER) {
+ return false;
+ }
+ switch (node->kind()) {
+ case Node::KIND_LOCALIZED_PROPERTY:
+ case Node::KIND_GROUP:
+ for (auto const& member : node->getMembers())
+ {
+ if (!canRemoveFromLayer(layer, member.second)) {
+ return false;
+ }
+ }
+ return true;
+ case Node::KIND_SET:
+ return node->getMembers().empty();
+ default: // Node::KIND_PROPERTY, Node::KIND_LOCALIZED_VALUE
+ return true;
+ }
+}
+
+}
+
+class Components::WriteThread: public salhelper::Thread {
+public:
+ WriteThread(
+ rtl::Reference< WriteThread > * reference, Components & components,
+ OUString url, Data const & data);
+
+ void flush() { delay_.set(); }
+
+private:
+ virtual ~WriteThread() override {}
+
+ virtual void execute() override;
+
+ rtl::Reference< WriteThread > * reference_;
+ Components & components_;
+ OUString url_;
+ Data const & data_;
+ osl::Condition delay_;
+ std::shared_ptr<osl::Mutex> lock_;
+};
+
+Components::WriteThread::WriteThread(
+ rtl::Reference< WriteThread > * reference, Components & components,
+ OUString url, Data const & data):
+ Thread("configmgrWriter"), reference_(reference), components_(components),
+ url_(std::move(url)), data_(data),
+ lock_( lock() )
+{
+ assert(reference != nullptr);
+}
+
+void Components::WriteThread::execute() {
+ delay_.wait(std::chrono::seconds(1)); // must not throw; result_error is harmless and ignored
+ osl::MutexGuard g(*lock_); // must not throw
+ try {
+ try {
+ writeModFile(components_, url_, data_);
+ } catch (css::uno::RuntimeException &) {
+ // Ignore write errors, instead of aborting:
+ TOOLS_WARN_EXCEPTION("configmgr", "error writing modifications");
+ }
+ } catch (...) {
+ reference_->clear();
+ throw;
+ }
+ reference_->clear();
+}
+
+Components & Components::getSingleton(
+ css::uno::Reference< css::uno::XComponentContext > const & context)
+{
+ assert(context.is());
+ static Components singleton(context);
+ return singleton;
+}
+
+bool Components::allLocales(std::u16string_view locale) {
+ return locale == u"*";
+}
+
+rtl::Reference< Node > Components::resolvePathRepresentation(
+ OUString const & pathRepresentation,
+ OUString * canonicRepresentation, std::vector<OUString> * path, int * finalizedLayer)
+ const
+{
+ return data_.resolvePathRepresentation(
+ pathRepresentation, canonicRepresentation, path, finalizedLayer);
+}
+
+rtl::Reference< Node > Components::getTemplate(OUString const & fullName) const
+{
+ return data_.getTemplate(Data::NO_LAYER, fullName);
+}
+
+void Components::addRootAccess(rtl::Reference< RootAccess > const & access) {
+ roots_.insert(access.get());
+}
+
+void Components::removeRootAccess(RootAccess * access) {
+ roots_.erase(access);
+}
+
+void Components::initGlobalBroadcaster(
+ Modifications const & modifications,
+ rtl::Reference< RootAccess > const & exclude, Broadcaster * broadcaster)
+{
+ //TODO: Iterate only over roots w/ listeners:
+ for (auto const& elemRoot : roots_)
+ {
+ rtl::Reference< RootAccess > root;
+ if (elemRoot->acquireCounting() > 1) {
+ root.set(elemRoot); // must not throw
+ }
+ elemRoot->releaseNondeleting();
+ if (root.is()) {
+ if (root != exclude) {
+ std::vector<OUString> path(root->getAbsolutePath());
+ Modifications::Node const * mods = &modifications.getRoot();
+ for (auto const& pathElem : path)
+ {
+ Modifications::Node::Children::const_iterator k(
+ mods->children.find(pathElem));
+ if (k == mods->children.end()) {
+ mods = nullptr;
+ break;
+ }
+ mods = &k->second;
+ }
+ //TODO: If the complete tree of which root is a part is deleted,
+ // or replaced, mods will be null, but some of the listeners
+ // from within root should probably fire nonetheless:
+ if (mods != nullptr) {
+ root->initBroadcaster(*mods, broadcaster);
+ }
+ }
+ }
+ }
+}
+
+void Components::addModification(std::vector<OUString> const & path) {
+ data_.modifications.add(path);
+}
+
+void Components::writeModifications() {
+
+ if (data_.modifications.empty())
+ return;
+
+ switch (modificationTarget_) {
+ case ModificationTarget::None:
+ break;
+ case ModificationTarget::File:
+ if (!writeThread_.is()) {
+ writeThread_ = new WriteThread(
+ &writeThread_, *this, modificationFileUrl_, data_);
+ writeThread_->launch();
+ }
+ break;
+ case ModificationTarget::Dconf:
+#if ENABLE_DCONF
+ dconf::writeModifications(*this, data_);
+#endif
+ break;
+ }
+}
+
+void Components::flushModifications() {
+ rtl::Reference< WriteThread > thread;
+ {
+ osl::MutexGuard g(*lock_);
+ thread = writeThread_;
+ }
+ if (thread.is()) {
+ thread->flush();
+ thread->join();
+ }
+}
+
+void Components::insertExtensionXcsFile(
+ bool shared, OUString const & fileUri)
+{
+ int layer = getExtensionLayer(shared);
+ try {
+ parseXcsFile(fileUri, layer, data_, nullptr, nullptr, nullptr);
+ } catch (css::container::NoSuchElementException & e) {
+ throw css::uno::RuntimeException(
+ "insertExtensionXcsFile does not exist: " + e.Message);
+ }
+}
+
+void Components::insertExtensionXcuFile(
+ bool shared, OUString const & fileUri, Modifications * modifications)
+{
+ assert(modifications != nullptr);
+ int layer = getExtensionLayer(shared) + 1;
+ Additions * adds = data_.addExtensionXcuAdditions(fileUri, layer);
+ try {
+ parseXcuFile(fileUri, layer, data_, nullptr, modifications, adds);
+ } catch (css::container::NoSuchElementException & e) {
+ data_.removeExtensionXcuAdditions(fileUri);
+ throw css::uno::RuntimeException(
+ "insertExtensionXcuFile does not exist: " + e.Message);
+ }
+}
+
+void Components::removeExtensionXcuFile(
+ OUString const & fileUri, Modifications * modifications)
+{
+ //TODO: Ideally, exactly the data coming from the specified xcu file would
+ // be removed. However, not enough information is recorded in the in-memory
+ // data structures to do so. So, as a workaround, all those set elements
+ // that were freshly added by the xcu and have afterwards been left
+ // unchanged or have only had their properties changed in the user layer are
+ // removed (and nothing else). The heuristic to determine
+ // whether a node has been left unchanged is to check the layer ID (as
+ // usual) and additionally to check that the node does not recursively
+ // contain any non-empty sets (multiple extension xcu files are merged into
+ // one layer, so checking layer ID alone is not enough). Since
+ // item->additions records all additions of set members in textual order,
+ // the latter check works well when iterating through item->additions in
+ // reverse order.
+ assert(modifications != nullptr);
+ rtl::Reference< Data::ExtensionXcu > item(
+ data_.removeExtensionXcuAdditions(fileUri));
+ if (!item.is())
+ return;
+
+ for (Additions::reverse_iterator i(item->additions.rbegin());
+ i != item->additions.rend(); ++i)
+ {
+ rtl::Reference< Node > parent;
+ NodeMap const * map = &data_.getComponents();
+ rtl::Reference< Node > node;
+ for (auto const& j : *i)
+ {
+ parent = node;
+ node = map->findNode(Data::NO_LAYER, j);
+ if (!node.is()) {
+ break;
+ }
+ map = &node->getMembers();
+ }
+ if (node.is()) {
+ assert(parent.is());
+ if (parent->kind() == Node::KIND_SET) {
+ assert(
+ node->kind() == Node::KIND_GROUP ||
+ node->kind() == Node::KIND_SET);
+ if (canRemoveFromLayer(item->layer, node)) {
+ parent->getMembers().erase(i->back());
+ data_.modifications.remove(*i);
+ modifications->add(*i);
+ }
+ }
+ }
+ }
+ writeModifications();
+}
+
+void Components::insertModificationXcuFile(
+ OUString const & fileUri,
+ std::set< OUString > const & includedPaths,
+ std::set< OUString > const & excludedPaths,
+ Modifications * modifications)
+{
+ assert(modifications != nullptr);
+ Partial part(includedPaths, excludedPaths);
+ try {
+ parseFileLeniently(
+ &parseXcuFile, fileUri, Data::NO_LAYER, &part, modifications, nullptr);
+ } catch (const css::container::NoSuchElementException &) {
+ TOOLS_WARN_EXCEPTION(
+ "configmgr",
+ "error inserting non-existing \"" << fileUri << "\"");
+ }
+}
+
+css::beans::Optional< css::uno::Any > Components::getExternalValue(
+ std::u16string_view descriptor)
+{
+ size_t i = descriptor.find(' ');
+ if (i == 0 || i == std::u16string_view::npos) {
+ throw css::uno::RuntimeException(
+ OUString::Concat("bad external value descriptor ") + descriptor);
+ }
+ //TODO: Do not make calls with mutex locked:
+ OUString name(descriptor.substr(0, i));
+ ExternalServices::iterator j(externalServices_.find(name));
+ if (j == externalServices_.end()) {
+ css::uno::Reference< css::uno::XInterface > service;
+ try {
+ service = context_->getServiceManager()->createInstanceWithContext(
+ name, context_);
+ } catch (const css::uno::RuntimeException &) {
+ // Assuming these exceptions are real errors:
+ throw;
+ } catch (const css::uno::Exception &) {
+ // Assuming these exceptions indicate that the service is not
+ // installed:
+ TOOLS_WARN_EXCEPTION(
+ "configmgr",
+ "createInstance(" << name << ") failed");
+ }
+ css::uno::Reference< css::beans::XPropertySet > propset;
+ if (service.is()) {
+ propset.set( service, css::uno::UNO_QUERY_THROW);
+ }
+ j = externalServices_.emplace(name, propset).first;
+ }
+ css::beans::Optional< css::uno::Any > value;
+ if (j->second.is()) {
+ try {
+ if (!(j->second->getPropertyValue(OUString(descriptor.substr(i + 1))) >>=
+ value))
+ {
+ throw css::uno::RuntimeException(
+ OUString::Concat("cannot obtain external value through ") + descriptor);
+ }
+ } catch (css::beans::UnknownPropertyException & e) {
+ throw css::uno::RuntimeException(
+ "unknown external value descriptor ID: " + e.Message);
+ } catch (css::lang::WrappedTargetException & e) {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException(
+ "cannot obtain external value: " + e.Message,
+ nullptr, anyEx );
+ }
+ }
+ return value;
+}
+
+Components::Components(
+ css::uno::Reference< css::uno::XComponentContext > const & context):
+ context_(context), sharedExtensionLayer_(-1), userExtensionLayer_(-1),
+ modificationTarget_(ModificationTarget::None)
+{
+ assert(context.is());
+ lock_ = lock();
+ OUString conf(expand("${CONFIGURATION_LAYERS}"));
+ int layer = 0;
+ for (sal_Int32 i = 0;;) {
+ while (i != conf.getLength() && conf[i] == ' ') {
+ ++i;
+ }
+ if (i == conf.getLength()) {
+ break;
+ }
+ if (modificationTarget_ != ModificationTarget::None) {
+ throw css::uno::RuntimeException(
+ "CONFIGURATION_LAYERS: modification target layer followed by"
+ " further layers");
+ }
+ sal_Int32 c = i;
+ for (;; ++c) {
+ if (c == conf.getLength() || conf[c] == ' ') {
+ throw css::uno::RuntimeException(
+ "CONFIGURATION_LAYERS: missing ':' in \"" + conf + "\"");
+ }
+ if (conf[c] == ':') {
+ break;
+ }
+ }
+ sal_Int32 n = conf.indexOf(' ', c + 1);
+ if (n == -1) {
+ n = conf.getLength();
+ }
+ OUString type(conf.copy(i, c - i));
+ OUString url(conf.copy(c + 1, n - c - 1));
+ if (type == "xcsxcu") {
+ sal_uInt32 nStartTime = osl_getGlobalTimer();
+ parseXcsXcuLayer(layer, url);
+ SAL_INFO("configmgr", "parseXcsXcuLayer() took " << (osl_getGlobalTimer() - nStartTime) << " ms");
+ layer += 2; //TODO: overflow
+ } else if (type == "bundledext") {
+ parseXcsXcuIniLayer(layer, url, false);
+ layer += 2; //TODO: overflow
+ } else if (type == "sharedext") {
+ if (sharedExtensionLayer_ != -1) {
+ throw css::uno::RuntimeException(
+ "CONFIGURATION_LAYERS: multiple \"sharedext\" layers");
+ }
+ sharedExtensionLayer_ = layer;
+ parseXcsXcuIniLayer(layer, url, true);
+ layer += 2; //TODO: overflow
+ } else if (type == "userext") {
+ if (userExtensionLayer_ != -1) {
+ throw css::uno::RuntimeException(
+ "CONFIGURATION_LAYERS: multiple \"userext\" layers");
+ }
+ userExtensionLayer_ = layer;
+ parseXcsXcuIniLayer(layer, url, true);
+ layer += 2; //TODO: overflow
+ } else if (type == "res") {
+ sal_uInt32 nStartTime = osl_getGlobalTimer();
+ parseResLayer(layer, url);
+ SAL_INFO("configmgr", "parseResLayer() took " << (osl_getGlobalTimer() - nStartTime) << " ms");
+ ++layer; //TODO: overflow
+#if ENABLE_DCONF
+ } else if (type == "dconf") {
+ if (url == "!") {
+ modificationTarget_ = ModificationTarget::Dconf;
+ dconf::readLayer(data_, Data::NO_LAYER);
+ } else if (url == "*") {
+ dconf::readLayer(data_, layer);
+ } else {
+ throw css::uno::RuntimeException(
+ "CONFIGURATION_LAYERS: unknown \"dconf\" kind \"" + url
+ + "\"");
+ }
+ ++layer; //TODO: overflow
+#endif
+#if defined(_WIN32)
+ } else if (type == "winreg") {
+ WinRegType eType;
+ if (url == "LOCAL_MACHINE" || url.isEmpty()/*backwards comp.*/) {
+ eType = WinRegType::LOCAL_MACHINE;
+ } else if (url == "CURRENT_USER") {
+ eType = WinRegType::CURRENT_USER;
+ } else {
+ throw css::uno::RuntimeException(
+ "CONFIGURATION_LAYERS: unknown \"winreg\" kind \"" + url
+ + "\"");
+ }
+ OUString aTempFileURL;
+ if (dumpWindowsRegistry(&aTempFileURL, eType)) {
+ parseFileLeniently(&parseXcuFile, aTempFileURL, layer, nullptr, nullptr, nullptr);
+ if (!getenv("SAL_CONFIG_WINREG_RETAIN_TMP"))
+ osl::File::remove(aTempFileURL);
+ }
+ ++layer; //TODO: overflow
+#endif
+ } else if (type == "user") {
+ bool write;
+ if (url.startsWith("!", &url)) {
+ write = true;
+ } else if (url.startsWith("*", &url)) {
+ write = false;
+ } else {
+ write = true; // for backwards compatibility
+ }
+ if (url.isEmpty()) {
+ throw css::uno::RuntimeException(
+ "CONFIGURATION_LAYERS: empty \"user\" URL");
+ }
+ bool ignore = false;
+#if ENABLE_DCONF
+ if (write) {
+ OUString token(
+ expand("${SYSUSERCONFIG}/libreoffice/dconfwrite"));
+ osl::DirectoryItem it;
+ osl::FileBase::RC e = osl::DirectoryItem::get(token, it);
+ ignore = e == osl::FileBase::E_None;
+ SAL_INFO(
+ "configmgr",
+ "dconf write (<" << token << "> " << +e << "): "
+ << int(ignore));
+ if (ignore) {
+ modificationTarget_ = ModificationTarget::Dconf;
+ }
+ }
+#endif
+ if (!ignore) {
+ if (write) {
+ modificationTarget_ = ModificationTarget::File;
+ modificationFileUrl_ = url;
+ }
+ parseModificationLayer(write ? Data::NO_LAYER : layer, url);
+ }
+ ++layer; //TODO: overflow
+ } else {
+ throw css::uno::RuntimeException(
+ "CONFIGURATION_LAYERS: unknown layer type \"" + type + "\"");
+ }
+ i = n;
+ }
+}
+
+Components::~Components()
+{
+ // get flag if _exit was already called which is a sign to not secure user config.
+ // this is used for win only currently where calling _exit() unfortunately still
+ // calls destructors (what is not wanted). May be needed for other systems, too
+ // (unknown yet) but can do no harm
+ const bool bExitWasCalled(comphelper::BackupFileHelper::getExitWasCalled());
+
+#ifndef _WIN32
+ // we can add a SAL_WARN here for other systems where the destructor gets called after
+ // an _exit() call. Still safe - the getExitWasCalled() is used, but a hint that _exit
+ // behaves different on a system
+ SAL_WARN_IF(bExitWasCalled, "configmgr", "Components::~Components() called after _exit() call");
+#endif
+
+ if (bExitWasCalled)
+ {
+ // do not write, re-join threads
+ osl::MutexGuard g(*lock_);
+
+ if (writeThread_.is())
+ {
+ writeThread_->join();
+ }
+ }
+ else
+ {
+ // write changes
+ flushModifications();
+ }
+
+ for (auto const& rootElem : roots_)
+ {
+ rootElem->setAlive(false);
+ }
+}
+
+void Components::parseFileLeniently(
+ FileParser * parseFile, OUString const & url, int layer,
+ Partial const * partial, Modifications * modifications,
+ Additions * additions)
+{
+ assert(parseFile != nullptr);
+ try {
+ (*parseFile)(url, layer, data_, partial, modifications, additions);
+ } catch (const css::container::NoSuchElementException &) {
+ throw;
+ } catch (const css::uno::Exception &) { //TODO: more specific exception catching
+ // Ignore invalid XML files, instead of completely preventing OOo from
+ // starting:
+ TOOLS_WARN_EXCEPTION(
+ "configmgr",
+ "error reading \"" << url << "\"");
+ }
+}
+
+void Components::parseFiles(
+ int layer, OUString const & extension, FileParser * parseFile,
+ OUString const & url, bool recursive)
+{
+ osl::Directory dir(url);
+ switch (dir.open()) {
+ case osl::FileBase::E_None:
+ break;
+ case osl::FileBase::E_NOENT:
+ if (!recursive) {
+ return;
+ }
+ [[fallthrough]];
+ default:
+ throw css::uno::RuntimeException(
+ "cannot open directory " + url);
+ }
+ for (;;) {
+ osl::DirectoryItem i;
+ osl::FileBase::RC rc = dir.getNextItem(i, SAL_MAX_UINT32);
+ if (rc == osl::FileBase::E_NOENT) {
+ break;
+ }
+ if (rc != osl::FileBase::E_None) {
+ throw css::uno::RuntimeException(
+ "cannot iterate directory " + url);
+ }
+ osl::FileStatus stat(
+ osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileName |
+ osl_FileStatus_Mask_FileURL);
+ if (i.getFileStatus(stat) != osl::FileBase::E_None) {
+ throw css::uno::RuntimeException(
+ "cannot stat in directory " + url);
+ }
+ if (stat.getFileType() == osl::FileStatus::Directory) { //TODO: symlinks
+ parseFiles(layer, extension, parseFile, stat.getFileURL(), true);
+ } else {
+ OUString file(stat.getFileName());
+ if (file.endsWith(extension)) {
+ try {
+ parseFileLeniently(
+ parseFile, stat.getFileURL(), layer, nullptr, nullptr, nullptr);
+ } catch (css::container::NoSuchElementException & e) {
+ if (stat.getFileType() == osl::FileStatus::Link) {
+ SAL_WARN("configmgr", "dangling link <" << stat.getFileURL() << ">");
+ continue;
+ }
+ throw css::uno::RuntimeException(
+ "stat'ed file does not exist: " + e.Message);
+ }
+ }
+ }
+ }
+}
+
+void Components::parseFileList(
+ int layer, FileParser * parseFile, std::u16string_view urls,
+ bool recordAdditions)
+{
+ for (sal_Int32 i = 0;;) {
+ OUString url(o3tl::getToken(urls, 0, ' ', i));
+ if (!url.isEmpty()) {
+ Additions * adds = nullptr;
+ if (recordAdditions) {
+ adds = data_.addExtensionXcuAdditions(url, layer);
+ }
+ try {
+ parseFileLeniently(parseFile, url, layer, nullptr, nullptr, adds);
+ } catch (const css::container::NoSuchElementException &) {
+ TOOLS_WARN_EXCEPTION("configmgr", "file does not exist");
+ if (adds != nullptr) {
+ data_.removeExtensionXcuAdditions(url);
+ }
+ }
+ }
+ if (i == -1) {
+ break;
+ }
+ }
+}
+
+void Components::parseXcdFiles(int layer, OUString const & url) {
+ osl::Directory dir(url);
+ switch (dir.open()) {
+ case osl::FileBase::E_None:
+ break;
+ case osl::FileBase::E_NOENT:
+ return;
+ default:
+ throw css::uno::RuntimeException(
+ "cannot open directory " + url);
+ }
+ UnresolvedVector unres;
+ std::set< OUString > existingDeps;
+ std::set< OUString > processedDeps;
+ for (;;) {
+ osl::DirectoryItem i;
+ osl::FileBase::RC rc = dir.getNextItem(i, SAL_MAX_UINT32);
+ if (rc == osl::FileBase::E_NOENT) {
+ break;
+ }
+ if (rc != osl::FileBase::E_None) {
+ throw css::uno::RuntimeException(
+ "cannot iterate directory " + url);
+ }
+ osl::FileStatus stat(
+ osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileName |
+ osl_FileStatus_Mask_FileURL);
+ if (i.getFileStatus(stat) != osl::FileBase::E_None) {
+ throw css::uno::RuntimeException(
+ "cannot stat in directory " + url);
+ }
+ if (stat.getFileType() != osl::FileStatus::Directory) { //TODO: symlinks
+ OUString file(stat.getFileName());
+ OUString name;
+ if (file.endsWith(".xcd", &name)) {
+ existingDeps.insert(name);
+ rtl::Reference< ParseManager > manager;
+ try {
+ manager = new ParseManager(
+ stat.getFileURL(),
+ new XcdParser(layer, processedDeps, data_));
+ } catch (css::container::NoSuchElementException & e) {
+ if (stat.getFileType() == osl::FileStatus::Link) {
+ SAL_WARN("configmgr", "dangling link <" << stat.getFileURL() << ">");
+ continue;
+ }
+ throw css::uno::RuntimeException(
+ "stat'ed file does not exist: " + e.Message);
+ }
+ if (manager->parse(nullptr)) {
+ processedDeps.insert(name);
+ } else {
+ unres.emplace_back(name, manager);
+ }
+ }
+ }
+ }
+ while (!unres.empty()) {
+ bool resolved = false;
+ for (UnresolvedVector::iterator i(unres.begin()); i != unres.end();) {
+ if (i->manager->parse(&existingDeps)) {
+ processedDeps.insert(i->name);
+ i = unres.erase(i);
+ resolved = true;
+ } else {
+ ++i;
+ }
+ }
+ if (!resolved) {
+ throw css::uno::RuntimeException(
+ "xcd: unresolved dependencies in " + url);
+ }
+ }
+}
+
+void Components::parseXcsXcuLayer(int layer, OUString const & url) {
+ parseXcdFiles(layer, url);
+ parseFiles(layer, ".xcs", &parseXcsFile, url + "/schema", false);
+ parseFiles(layer + 1, ".xcu", &parseXcuFile, url + "/data", false);
+}
+
+void Components::parseXcsXcuIniLayer(
+ int layer, OUString const & url, bool recordAdditions)
+{
+ // Check if ini file exists (otherwise .override would still read global
+ // SCHEMA/DATA variables, which could interfere with unrelated environment
+ // variables):
+ if (rtl::Bootstrap(url).getHandle() == nullptr) return;
+
+ OUStringBuffer prefix("${.override:");
+ for (sal_Int32 i = 0; i != url.getLength(); ++i) {
+ sal_Unicode c = url[i];
+ switch (c) {
+ case '$':
+ case ':':
+ case '\\':
+ prefix.append('\\');
+ [[fallthrough]];
+ default:
+ prefix.append(c);
+ }
+ }
+ prefix.append(':');
+ OUString urls(prefix + "SCHEMA}");
+ rtl::Bootstrap::expandMacros(urls);
+ if (!urls.isEmpty()) {
+ parseFileList(layer, &parseXcsFile, urls, false);
+ }
+ urls = prefix.makeStringAndClear() + "DATA}";
+ rtl::Bootstrap::expandMacros(urls);
+ if (!urls.isEmpty()) {
+ parseFileList(layer + 1, &parseXcuFile, urls, recordAdditions);
+ }
+}
+
+void Components::parseResLayer(int layer, std::u16string_view url) {
+ OUString resUrl(OUString::Concat(url) + "/res");
+ parseXcdFiles(layer, resUrl);
+ parseFiles(layer, ".xcu", &parseXcuFile, resUrl, false);
+}
+
+void Components::parseModificationLayer(int layer, OUString const & url) {
+ try {
+ parseFileLeniently(&parseXcuFile, url, layer, nullptr, nullptr, nullptr);
+ } catch (css::container::NoSuchElementException &) {
+ SAL_INFO(
+ "configmgr", "user registrymodifications.xcu does not (yet) exist");
+ // Migrate old user layer data (can be removed once migration is no
+ // longer relevant, probably OOo 4; also see hack for xsi namespace in
+ // xmlreader::XmlReader::registerNamespaceIri):
+ parseFiles(
+ layer, ".xcu", &parseXcuFile,
+ expand(
+ "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap")
+ ":UserInstallation}/user/registry/data"),
+ false);
+ }
+}
+
+int Components::getExtensionLayer(bool shared) const {
+ int layer = shared ? sharedExtensionLayer_ : userExtensionLayer_;
+ if (layer == -1) {
+ throw css::uno::RuntimeException(
+ "insert extension xcs/xcu file into undefined layer");
+ }
+ return layer;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/components.hxx b/configmgr/source/components.hxx
new file mode 100644
index 000000000..5d7b6b596
--- /dev/null
+++ b/configmgr/source/components.hxx
@@ -0,0 +1,166 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <set>
+#include <string_view>
+
+#include <com/sun/star/beans/Optional.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <rtl/ref.hxx>
+#include <o3tl/sorted_vector.hxx>
+
+#include "additions.hxx"
+#include "data.hxx"
+#include "modifications.hxx"
+
+namespace com::sun::star {
+ namespace beans { class XPropertySet; }
+ namespace uno {
+ class Any;
+ class XComponentContext;
+ }
+}
+
+namespace configmgr {
+
+class Broadcaster;
+class Node;
+class Partial;
+class RootAccess;
+
+class Components {
+public:
+ static Components & getSingleton(
+ css::uno::Reference< css::uno::XComponentContext > const & context);
+
+ static bool allLocales(std::u16string_view locale);
+
+ rtl::Reference< Node > resolvePathRepresentation(
+ OUString const & pathRepresentation,
+ OUString * canonicRepresentation, std::vector<OUString> * path, int * finalizedLayer)
+ const;
+
+ rtl::Reference< Node > getTemplate( OUString const & fullName) const;
+
+ void addRootAccess(rtl::Reference< RootAccess > const & access);
+
+ void removeRootAccess(RootAccess * access);
+
+ void initGlobalBroadcaster(
+ Modifications const & modifications,
+ rtl::Reference< RootAccess > const & exclude,
+ Broadcaster * broadcaster);
+
+ void addModification(std::vector<OUString> const & path);
+
+ void writeModifications();
+
+ void flushModifications();
+ // must be called with configmgr::lock unacquired; must be called before
+ // shutdown if writeModifications has ever been called (probably
+ // indirectly, via removeExtensionXcuFile)
+
+ void insertExtensionXcsFile(bool shared, OUString const & fileUri);
+
+ void insertExtensionXcuFile(
+ bool shared, OUString const & fileUri,
+ Modifications * modifications);
+
+ void removeExtensionXcuFile(
+ OUString const & fileUri, Modifications * modifications);
+
+ void insertModificationXcuFile(
+ OUString const & fileUri,
+ std::set< OUString > const & includedPaths,
+ std::set< OUString > const & excludedPaths,
+ Modifications * modifications);
+
+ css::beans::Optional< css::uno::Any >
+ getExternalValue(std::u16string_view descriptor);
+
+private:
+ Components(const Components&) = delete;
+ Components& operator=(const Components&) = delete;
+
+ typedef void FileParser(
+ OUString const &, int, Data &, Partial const *, Modifications *,
+ Additions *);
+public:
+ explicit Components(
+ css::uno::Reference< css::uno::XComponentContext > const & context);
+
+ ~Components();
+private:
+
+ void parseFileLeniently(
+ FileParser * parseFile, OUString const & url, int layer,
+ Partial const * partial, Modifications * modifications,
+ Additions * additions);
+
+ void parseFiles(
+ int layer, OUString const & extension, FileParser * parseFile,
+ OUString const & url, bool recursive);
+
+ void parseFileList(
+ int layer, FileParser * parseFile, std::u16string_view urls,
+ bool recordAdditions);
+
+ void parseXcdFiles(int layer, OUString const & url);
+
+ void parseXcsXcuLayer(int layer, OUString const & url);
+
+ void parseXcsXcuIniLayer(
+ int layer, OUString const & url, bool recordAdditions);
+
+ void parseResLayer(int layer, std::u16string_view url);
+
+ void parseModificationLayer(int layer, OUString const & url);
+
+ int getExtensionLayer(bool shared) const;
+
+ typedef
+ config_map<
+ css::uno::Reference<
+ css::beans::XPropertySet > >
+ ExternalServices;
+
+ class WriteThread;
+
+ enum class ModificationTarget { None, File, Dconf };
+
+ css::uno::Reference< css::uno::XComponentContext >
+ context_;
+ Data data_;
+ o3tl::sorted_vector< RootAccess * > roots_;
+ ExternalServices externalServices_;
+ rtl::Reference< WriteThread > writeThread_;
+ int sharedExtensionLayer_;
+ int userExtensionLayer_;
+ ModificationTarget modificationTarget_;
+ OUString modificationFileUrl_;
+ std::shared_ptr<osl::Mutex> lock_;
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/config_map.hxx b/configmgr/source/config_map.hxx
new file mode 100644
index 000000000..5d2990d5a
--- /dev/null
+++ b/configmgr/source/config_map.hxx
@@ -0,0 +1,37 @@
+/* -*- 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/.
+ */
+#ifndef CONFIG_MAP_HXX
+#define CONFIG_MAP_HXX
+
+#include <map>
+#include <rtl/ustring.hxx>
+
+// The realisation here is that while a map is a reasonably compact
+// representation, there is often no need to have it completely
+// sorted, so we can use a fast in-line length comparison as the
+// initial compare, rather than sorting of sub string contents.
+
+struct LengthContentsCompare
+{
+ bool operator()(std::u16string_view a, std::u16string_view b) const
+ {
+ if (a.size() == b.size())
+ return a < b;
+ else
+ return a.size() < b.size();
+ }
+};
+
+template <class T> struct config_map : public std::map<OUString, T, LengthContentsCompare>
+{
+};
+
+#endif // CONFIG_MAP_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/configmgr.component b/configmgr/source/configmgr.component
new file mode 100644
index 000000000..ff46b6070
--- /dev/null
+++ b/configmgr/source/configmgr.component
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * 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 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.comp.configuration.ConfigurationProvider"
+ constructor="com_sun_star_comp_configuration_ConfigurationProvider_get_implementation">
+ <service name="com.sun.star.configuration.ConfigurationProvider"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.configuration.ConfigurationRegistry"
+ constructor="com_sun_star_comp_configuration_ConfigurationRegistry_get_implementation">
+ <service name="com.sun.star.configuration.ConfigurationRegistry"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.configuration.DefaultProvider"
+ constructor="com_sun_star_comp_configuration_DefaultProvider_get_implementation"
+ single-instance="true">
+ <service name="com.sun.star.configuration.DefaultProvider"/>
+ <singleton name="com.sun.star.configuration.theDefaultProvider"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.configuration.ReadOnlyAccess"
+ constructor="com_sun_star_comp_configuration_ReadOnlyAccess_get_implementation">
+ <service name="com.sun.star.configuration.ReadOnlyAccess"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.configuration.ReadWriteAccess"
+ constructor="com_sun_star_comp_configuration_ReadWriteAccess_get_implementation">
+ <service name="com.sun.star.configuration.ReadWriteAccess"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.configuration.Update"
+ constructor="com_sun_star_comp_configuration_Update_get_implementation">
+ <service name="com.sun.star.configuration.Update_Service"/>
+ <singleton name="com.sun.star.configuration.Update"/>
+ </implementation>
+</component>
diff --git a/configmgr/source/configurationprovider.cxx b/configmgr/source/configurationprovider.cxx
new file mode 100644
index 000000000..b8bdcb426
--- /dev/null
+++ b/configmgr/source/configurationprovider.cxx
@@ -0,0 +1,403 @@
+/* -*- 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 <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/lang/EventObject.hpp>
+#include <com/sun/star/lang/Locale.hpp>
+#include <com/sun/star/lang/XLocalizable.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Exception.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/uno/XInterface.hpp>
+#include <com/sun/star/util/XFlushListener.hpp>
+#include <com/sun/star/util/XFlushable.hpp>
+#include <com/sun/star/util/XRefreshListener.hpp>
+#include <com/sun/star/util/XRefreshable.hpp>
+#include <cppu/unotype.hxx>
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/weak.hxx>
+#include <osl/mutex.hxx>
+#include <sal/types.h>
+#include <rtl/ref.hxx>
+#include <rtl/ustring.hxx>
+
+#include <i18nlangtag/languagetag.hxx>
+#include <utility>
+
+#include "components.hxx"
+#include "configurationprovider.hxx"
+#include "lock.hxx"
+#include "defaultprovider.hxx"
+#include "rootaccess.hxx"
+
+namespace configmgr::configuration_provider {
+
+namespace {
+
+constexpr OUStringLiteral accessServiceName =
+ u"com.sun.star.configuration.ConfigurationAccess";
+constexpr OUStringLiteral updateAccessServiceName =
+ u"com.sun.star.configuration.ConfigurationUpdateAccess";
+
+void badNodePath() {
+ throw css::uno::Exception(
+ ("com.sun.star.configuration.ConfigurationProvider expects a single,"
+ " non-empty, string nodepath argument"),
+ nullptr);
+}
+
+typedef
+ cppu::WeakComponentImplHelper<
+ css::lang::XServiceInfo, css::lang::XMultiServiceFactory,
+ css::util::XRefreshable, css::util::XFlushable,
+ css::lang::XLocalizable >
+ ServiceBase;
+
+class Service:
+ private cppu::BaseMutex, public ServiceBase
+{
+public:
+ explicit Service(
+ const css::uno::Reference< css::uno::XComponentContext >& context):
+ ServiceBase(m_aMutex), context_(context), default_(true),
+ lock_( lock() )
+ {
+ assert(context.is());
+ }
+
+ Service(
+ const css::uno::Reference< css::uno::XComponentContext >& context,
+ OUString locale):
+ ServiceBase(m_aMutex), context_(context), locale_(std::move(locale)),
+ default_(false),
+ lock_( lock() )
+ {
+ assert(context.is());
+ }
+
+private:
+ Service(const Service&) = delete;
+ Service& operator=(const Service&) = delete;
+
+ virtual ~Service() override {}
+
+ virtual void SAL_CALL disposing() override { flushModifications(); }
+
+ virtual OUString SAL_CALL getImplementationName() override
+ {
+ return default_
+ ? default_provider::getImplementationName()
+ : "com.sun.star.comp.configuration.ConfigurationProvider";
+ }
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
+ { return cppu::supportsService(this, ServiceName); }
+
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getSupportedServiceNames() override
+ {
+ return default_
+ ? default_provider::getSupportedServiceNames()
+ : css::uno::Sequence<OUString> { "com.sun.star.configuration.ConfigurationProvider" };
+ }
+
+ virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstance(
+ OUString const & aServiceSpecifier) override;
+
+ virtual css::uno::Reference< css::uno::XInterface > SAL_CALL
+ createInstanceWithArguments(
+ OUString const & ServiceSpecifier,
+ css::uno::Sequence< css::uno::Any > const & Arguments) override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getAvailableServiceNames() override;
+
+ virtual void SAL_CALL refresh() override;
+
+ virtual void SAL_CALL addRefreshListener(
+ css::uno::Reference< css::util::XRefreshListener > const & l) override;
+
+ virtual void SAL_CALL removeRefreshListener(
+ css::uno::Reference< css::util::XRefreshListener > const & l) override;
+
+ virtual void SAL_CALL flush() override;
+
+ virtual void SAL_CALL addFlushListener(
+ css::uno::Reference< css::util::XFlushListener > const & l) override;
+
+ virtual void SAL_CALL removeFlushListener(
+ css::uno::Reference< css::util::XFlushListener > const & l) override;
+
+ virtual void SAL_CALL setLocale(css::lang::Locale const & eLocale) override;
+
+ virtual css::lang::Locale SAL_CALL getLocale() override;
+
+ void flushModifications() const;
+
+ css::uno::Reference< css::uno::XComponentContext > context_;
+ OUString locale_;
+ bool default_;
+ std::shared_ptr<osl::Mutex> lock_;
+};
+
+css::uno::Reference< css::uno::XInterface > Service::createInstance(
+ OUString const & aServiceSpecifier)
+{
+ return createInstanceWithArguments(
+ aServiceSpecifier, css::uno::Sequence< css::uno::Any >());
+}
+
+css::uno::Reference< css::uno::XInterface >
+Service::createInstanceWithArguments(
+ OUString const & ServiceSpecifier,
+ css::uno::Sequence< css::uno::Any > const & Arguments)
+{
+ OUString nodepath;
+ OUString locale;
+ for (sal_Int32 i = 0; i < Arguments.getLength(); ++i) {
+ css::beans::NamedValue v1;
+ css::beans::PropertyValue v2;
+ OUString name;
+ css::uno::Any value;
+ if (Arguments[i] >>= v1) {
+ name = v1.Name;
+ value = v1.Value;
+ } else if (Arguments[i] >>= v2) {
+ name = v2.Name;
+ value = v2.Value;
+ } else if (Arguments.getLength() == 1 && (Arguments[i] >>= nodepath)) {
+ // For backwards compatibility, allow a single string argument that
+ // denotes nodepath.
+ if (nodepath.isEmpty()) {
+ badNodePath();
+ }
+ break;
+ } else {
+ throw css::uno::Exception(
+ ("com.sun.star.configuration.ConfigurationProvider expects"
+ " NamedValue or PropertyValue arguments"),
+ nullptr);
+ }
+ // For backwards compatibility, allow "nodepath" and "Locale" in any
+ // case:
+ if (name.equalsIgnoreAsciiCase("nodepath")) {
+ if (!nodepath.isEmpty() || !(value >>= nodepath) ||
+ nodepath.isEmpty())
+ {
+ badNodePath();
+ }
+ } else if (name.equalsIgnoreAsciiCase("locale")) {
+ if (!locale.isEmpty() || !(value >>= locale) ||
+ locale.isEmpty())
+ {
+ throw css::uno::Exception(
+ ("com.sun.star.configuration.ConfigurationProvider expects"
+ " at most one, non-empty, string Locale argument"),
+ nullptr);
+ }
+ }
+ }
+ if (nodepath.isEmpty()) {
+ badNodePath();
+ }
+ // For backwards compatibility, allow a nodepath that misses the leading
+ // slash:
+ if (nodepath[0] != '/') {
+ nodepath = "/" + nodepath;
+ }
+ if (locale.isEmpty()) {
+ //TODO: should the Access use the dynamically changing locale_ instead?
+ locale = locale_;
+ if (locale.isEmpty()) {
+ locale = "en-US";
+ }
+ }
+ bool update;
+ if (ServiceSpecifier == accessServiceName) {
+ update = false;
+ } else if (ServiceSpecifier == updateAccessServiceName) {
+ update = true;
+ } else {
+ throw css::uno::Exception(
+ ("com.sun.star.configuration.ConfigurationProvider does not support"
+ " service " + ServiceSpecifier),
+ static_cast< cppu::OWeakObject * >(this));
+ }
+ osl::MutexGuard guard(*lock_);
+ Components & components = Components::getSingleton(context_);
+ rtl::Reference root(
+ new RootAccess(components, nodepath, locale, update));
+ if (root->isValue()) {
+ throw css::uno::Exception(
+ ("com.sun.star.configuration.ConfigurationProvider: there is a leaf"
+ " value at nodepath " + nodepath),
+ static_cast< cppu::OWeakObject * >(this));
+ }
+ components.addRootAccess(root);
+ return static_cast< cppu::OWeakObject * >(root.get());
+}
+
+css::uno::Sequence< OUString > Service::getAvailableServiceNames()
+{
+ return { accessServiceName, updateAccessServiceName };
+}
+
+void Service::refresh() {
+ //TODO
+ cppu::OInterfaceContainerHelper * cont = rBHelper.getContainer(
+ cppu::UnoType< css::util::XRefreshListener >::get());
+ if (cont != nullptr) {
+ css::lang::EventObject ev(static_cast< cppu::OWeakObject * >(this));
+ cont->notifyEach(&css::util::XRefreshListener::refreshed, ev);
+ }
+}
+
+void Service::addRefreshListener(
+ css::uno::Reference< css::util::XRefreshListener > const & l)
+{
+ rBHelper.addListener(
+ cppu::UnoType< css::util::XRefreshListener >::get(), l);
+}
+
+void Service::removeRefreshListener(
+ css::uno::Reference< css::util::XRefreshListener > const & l)
+{
+ rBHelper.removeListener(
+ cppu::UnoType< css::util::XRefreshListener >::get(), l);
+}
+
+void Service::flush() {
+ flushModifications();
+ cppu::OInterfaceContainerHelper * cont = rBHelper.getContainer(
+ cppu::UnoType< css::util::XFlushListener >::get());
+ if (cont != nullptr) {
+ css::lang::EventObject ev(static_cast< cppu::OWeakObject * >(this));
+ cont->notifyEach(&css::util::XFlushListener::flushed, ev);
+ }
+}
+
+void Service::addFlushListener(
+ css::uno::Reference< css::util::XFlushListener > const & l)
+{
+ rBHelper.addListener(cppu::UnoType< css::util::XFlushListener >::get(), l);
+}
+
+void Service::removeFlushListener(
+ css::uno::Reference< css::util::XFlushListener > const & l)
+{
+ rBHelper.removeListener(
+ cppu::UnoType< css::util::XFlushListener >::get(), l);
+}
+
+void Service::setLocale(css::lang::Locale const & eLocale)
+{
+ osl::MutexGuard guard(*lock_);
+ locale_ = LanguageTag::convertToBcp47( eLocale, false);
+}
+
+css::lang::Locale Service::getLocale() {
+ osl::MutexGuard guard(*lock_);
+ css::lang::Locale loc;
+ if (! locale_.isEmpty()) {
+ loc = LanguageTag::convertToLocale( locale_, false);
+ }
+ return loc;
+}
+
+void Service::flushModifications() const {
+ Components * components;
+ {
+ osl::MutexGuard guard(*lock_);
+ components = &Components::getSingleton(context_);
+ }
+ components->flushModifications();
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_configuration_ConfigurationProvider_get_implementation(
+ css::uno::XComponentContext* Context, css::uno::Sequence<css::uno::Any> const& Arguments)
+{
+ if (!Arguments.hasElements()) {
+ auto p = css::configuration::theDefaultProvider::get(Context);
+ p->acquire();
+ return p.get();
+ } else {
+ OUString locale;
+ for (sal_Int32 i = 0; i < Arguments.getLength(); ++i) {
+ css::beans::NamedValue v1;
+ css::beans::PropertyValue v2;
+ OUString name;
+ css::uno::Any value;
+ if (Arguments[i] >>= v1) {
+ name = v1.Name;
+ value = v1.Value;
+ } else if (Arguments[i] >>= v2) {
+ name = v2.Name;
+ value = v2.Value;
+ } else {
+ throw css::uno::Exception(
+ ("com.sun.star.configuration.ConfigurationProvider factory"
+ " expects NamedValue or PropertyValue arguments"),
+ nullptr);
+ }
+ // For backwards compatibility, allow "Locale" and (ignored)
+ // "EnableAsync" in any case:
+ if (name.equalsIgnoreAsciiCase("locale")) {
+ if (!locale.isEmpty() || !(value >>= locale) ||
+ locale.isEmpty())
+ {
+ throw css::uno::Exception(
+ ("com.sun.star.configuration.ConfigurationProvider"
+ " factory expects at most one, non-empty, string"
+ " Locale argument"),
+ nullptr);
+ }
+ } else if (!name.equalsIgnoreAsciiCase("enableasync")) {
+ throw css::uno::Exception(
+ ("com.sun.star.configuration.ConfigurationProvider factory:"
+ " unknown argument " + name),
+ nullptr);
+ }
+ }
+ return cppu::acquire(new Service(Context, locale));
+ }
+}
+
+}
+
+css::uno::Reference< css::uno::XInterface > createDefault(
+ css::uno::Reference< css::uno::XComponentContext > const & context)
+{
+ return static_cast< cppu::OWeakObject * >(new Service(context));
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/configurationprovider.hxx b/configmgr/source/configurationprovider.hxx
new file mode 100644
index 000000000..96d40b895
--- /dev/null
+++ b/configmgr/source/configurationprovider.hxx
@@ -0,0 +1,41 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <com/sun/star/uno/Reference.hxx>
+
+namespace com::sun::star {
+ namespace uno {
+ class XComponentContext;
+ class XInterface;
+ }
+}
+
+namespace configmgr::configuration_provider {
+
+css::uno::Reference< css::uno::XInterface > createDefault(
+ css::uno::Reference< css::uno::XComponentContext >
+ const & context);
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/configurationregistry.cxx b/configmgr/source/configurationregistry.cxx
new file mode 100644
index 000000000..b2fd214f3
--- /dev/null
+++ b/configmgr/source/configurationregistry.cxx
@@ -0,0 +1,641 @@
+/* -*- 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 <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/container/NoSuchElementException.hpp>
+#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/registry/InvalidRegistryException.hpp>
+#include <com/sun/star/registry/InvalidValueException.hpp>
+#include <com/sun/star/registry/RegistryKeyType.hpp>
+#include <com/sun/star/registry/RegistryValueType.hpp>
+#include <com/sun/star/registry/XRegistryKey.hpp>
+#include <com/sun/star/registry/XSimpleRegistry.hpp>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/DeploymentException.hpp>
+#include <com/sun/star/uno/Exception.hpp>
+#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/TypeClass.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/uno/XInterface.hpp>
+#include <com/sun/star/util/XFlushable.hpp>
+#include <cppu/unotype.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/weak.hxx>
+#include <mutex>
+#include <rtl/ustring.hxx>
+#include <utility>
+#include <sal/types.h>
+
+namespace com::sun::star::util {
+ class XFlushListener;
+}
+
+namespace configmgr::configuration_registry {
+
+namespace {
+
+class Service:
+ public cppu::WeakImplHelper<
+ css::lang::XServiceInfo, css::registry::XSimpleRegistry,
+ css::util::XFlushable >
+{
+public:
+ explicit Service(css::uno::Reference< css::uno::XComponentContext > const & context);
+
+private:
+ Service(const Service&) = delete;
+ Service& operator=(const Service&) = delete;
+
+ virtual ~Service() override {}
+
+ virtual OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.comp.configuration.ConfigurationRegistry"; }
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
+ { return cppu::supportsService(this, ServiceName); }
+
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getSupportedServiceNames() override
+ { return { "com.sun.star.configuration.ConfigurationRegistry" }; }
+
+ virtual OUString SAL_CALL getURL() override;
+
+ virtual void SAL_CALL open(
+ OUString const & rURL, sal_Bool bReadOnly, sal_Bool) override;
+
+ virtual sal_Bool SAL_CALL isValid() override;
+
+ virtual void SAL_CALL close() override;
+
+ virtual void SAL_CALL destroy() override;
+
+ virtual css::uno::Reference< css::registry::XRegistryKey > SAL_CALL
+ getRootKey() override;
+
+ virtual sal_Bool SAL_CALL isReadOnly() override;
+
+ virtual void SAL_CALL mergeKey(OUString const &, OUString const &) override;
+
+ virtual void SAL_CALL flush() override;
+
+ virtual void SAL_CALL addFlushListener(
+ css::uno::Reference< css::util::XFlushListener > const &) override;
+
+ virtual void SAL_CALL removeFlushListener(
+ css::uno::Reference< css::util::XFlushListener > const &) override;
+
+ void checkValid();
+
+ void checkValid_RuntimeException();
+
+ void doClose();
+
+ css::uno::Reference< css::lang::XMultiServiceFactory > provider_;
+ std::mutex mutex_;
+ css::uno::Reference< css::uno::XInterface > access_;
+ OUString url_;
+ bool readOnly_;
+
+ friend class RegistryKey;
+};
+
+class RegistryKey:
+ public cppu::WeakImplHelper< css::registry::XRegistryKey >
+{
+public:
+ RegistryKey(Service & service, css::uno::Any value):
+ service_(service), value_(std::move(value)) {}
+
+private:
+ RegistryKey(const RegistryKey&) = delete;
+ RegistryKey& operator=(const RegistryKey&) = delete;
+
+ virtual ~RegistryKey() override {}
+
+ virtual OUString SAL_CALL getKeyName() override;
+
+ virtual sal_Bool SAL_CALL isReadOnly() override;
+
+ virtual sal_Bool SAL_CALL isValid() override;
+
+ virtual css::registry::RegistryKeyType SAL_CALL getKeyType(
+ OUString const &) override;
+
+ virtual css::registry::RegistryValueType SAL_CALL getValueType() override;
+
+ virtual sal_Int32 SAL_CALL getLongValue() override;
+
+ virtual void SAL_CALL setLongValue(sal_Int32) override;
+
+ virtual css::uno::Sequence< sal_Int32 > SAL_CALL getLongListValue() override;
+
+ virtual void SAL_CALL setLongListValue(
+ css::uno::Sequence< sal_Int32 > const &) override;
+
+ virtual OUString SAL_CALL getAsciiValue() override;
+
+ virtual void SAL_CALL setAsciiValue(OUString const &) override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL getAsciiListValue() override;
+
+ virtual void SAL_CALL setAsciiListValue(
+ css::uno::Sequence< OUString > const &) override;
+
+ virtual OUString SAL_CALL getStringValue() override;
+
+ virtual void SAL_CALL setStringValue(OUString const &) override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL getStringListValue() override;
+
+ virtual void SAL_CALL setStringListValue(
+ css::uno::Sequence< OUString > const &) override;
+
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getBinaryValue() override;
+
+ virtual void SAL_CALL setBinaryValue(css::uno::Sequence< sal_Int8 > const &) override;
+
+ virtual css::uno::Reference< css::registry::XRegistryKey > SAL_CALL openKey(
+ OUString const & aKeyName) override;
+
+ virtual css::uno::Reference< css::registry::XRegistryKey > SAL_CALL
+ createKey(OUString const &) override;
+
+ virtual void SAL_CALL closeKey() override;
+
+ virtual void SAL_CALL deleteKey(OUString const &) override;
+
+ virtual
+ css::uno::Sequence< css::uno::Reference< css::registry::XRegistryKey > >
+ SAL_CALL openKeys() override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL getKeyNames() override;
+
+ virtual sal_Bool SAL_CALL createLink(
+ OUString const &, OUString const &) override;
+
+ virtual void SAL_CALL deleteLink(OUString const &) override;
+
+ virtual OUString SAL_CALL getLinkTarget(OUString const &) override;
+
+ virtual OUString SAL_CALL getResolvedName(
+ OUString const & aKeyName) override;
+
+ Service & service_;
+ css::uno::Any value_;
+};
+
+Service::Service(
+ css::uno::Reference< css::uno::XComponentContext > const & context)
+ : readOnly_(false)
+{
+ assert(context.is());
+ try {
+ provider_.set(
+ context->getServiceManager()->createInstanceWithContext(
+ "com.sun.star.configuration.DefaultProvider", context),
+ css::uno::UNO_QUERY_THROW);
+ } catch (css::uno::RuntimeException &) {
+ throw;
+ } catch (css::uno::Exception & e) {
+ throw css::uno::DeploymentException(
+ ("component context fails to supply service"
+ " com.sun.star.configuration.DefaultProvider of type"
+ " com.sun.star.lang.XMultiServiceFactory: " + e.Message),
+ context);
+ }
+}
+
+OUString Service::getURL() {
+ std::unique_lock g(mutex_);
+ checkValid_RuntimeException();
+ return url_;
+}
+
+void Service::open(OUString const & rURL, sal_Bool bReadOnly, sal_Bool)
+{
+ //TODO: bCreate
+ std::unique_lock g(mutex_);
+ if (access_.is()) {
+ doClose();
+ }
+ css::uno::Sequence< css::uno::Any > args{ css::uno::Any(
+ css::beans::NamedValue("nodepath", css::uno::Any(rURL))) };
+ try {
+ access_ = provider_->createInstanceWithArguments(
+ (bReadOnly
+ ? OUString("com.sun.star.configuration.ConfigurationAccess")
+ : OUString(
+ "com.sun.star.configuration.ConfigurationUpdateAccess")),
+ args);
+ } catch (css::uno::RuntimeException &) {
+ throw;
+ } catch (css::uno::Exception & e) {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException(
+ "com.sun.star.configuration.ConfigurationRegistry: open failed: " +
+ e.Message,
+ static_cast< cppu::OWeakObject * >(this), anyEx );
+ }
+ url_ = rURL;
+ readOnly_ = bReadOnly;
+}
+
+sal_Bool Service::isValid() {
+ std::unique_lock g(mutex_);
+ return access_.is();
+}
+
+void Service::close()
+{
+ std::unique_lock g(mutex_);
+ checkValid();
+ doClose();
+}
+
+void Service::destroy()
+{
+ throw css::uno::RuntimeException(
+ "com.sun.star.configuration.ConfigurationRegistry: not implemented",
+ static_cast< cppu::OWeakObject * >(this));
+}
+
+css::uno::Reference< css::registry::XRegistryKey > Service::getRootKey()
+{
+ std::unique_lock g(mutex_);
+ checkValid();
+ return new RegistryKey(*this, css::uno::Any(access_));
+}
+
+sal_Bool Service::isReadOnly() {
+ std::unique_lock g(mutex_);
+ checkValid_RuntimeException();
+ return readOnly_;
+}
+
+void Service::mergeKey(OUString const &, OUString const &)
+{
+ throw css::uno::RuntimeException(
+ "com.sun.star.configuration.ConfigurationRegistry: not implemented",
+ static_cast< cppu::OWeakObject * >(this));
+}
+
+void Service::flush()
+{
+ throw css::uno::RuntimeException(
+ "com.sun.star.configuration.ConfigurationRegistry: not implemented",
+ static_cast< cppu::OWeakObject * >(this));
+}
+
+void Service::addFlushListener(
+ css::uno::Reference< css::util::XFlushListener > const &)
+{
+ throw css::uno::RuntimeException(
+ "com.sun.star.configuration.ConfigurationRegistry: not implemented",
+ static_cast< cppu::OWeakObject * >(this));
+}
+
+void Service::removeFlushListener(
+ css::uno::Reference< css::util::XFlushListener > const &)
+{
+ throw css::uno::RuntimeException(
+ "com.sun.star.configuration.ConfigurationRegistry: not implemented",
+ static_cast< cppu::OWeakObject * >(this));
+}
+
+void Service::checkValid() {
+ if (!access_.is()) {
+ throw css::registry::InvalidRegistryException(
+ "com.sun.star.configuration.ConfigurationRegistry: not valid",
+ static_cast< cppu::OWeakObject * >(this));
+ }
+}
+
+void Service::checkValid_RuntimeException() {
+ if (!access_.is()) {
+ throw css::uno::RuntimeException(
+ "com.sun.star.configuration.ConfigurationRegistry: not valid",
+ static_cast< cppu::OWeakObject * >(this));
+ }
+}
+
+void Service::doClose() {
+ access_.clear();
+}
+
+OUString RegistryKey::getKeyName() {
+ std::unique_lock g(service_.mutex_);
+ service_.checkValid_RuntimeException();
+ css::uno::Reference< css::container::XNamed > named;
+ if (value_ >>= named) {
+ return named->getName();
+ }
+ throw css::uno::RuntimeException(
+ "com.sun.star.configuration.ConfigurationRegistry: not implemented",
+ static_cast< cppu::OWeakObject * >(this));
+}
+
+sal_Bool RegistryKey::isReadOnly()
+{
+ std::unique_lock g(service_.mutex_);
+ service_.checkValid_RuntimeException();
+ return service_.readOnly_; //TODO: read-only sub-nodes in update access?
+}
+
+sal_Bool RegistryKey::isValid() {
+ return service_.isValid();
+}
+
+css::registry::RegistryKeyType RegistryKey::getKeyType(OUString const &)
+{
+ std::unique_lock g(service_.mutex_);
+ service_.checkValid();
+ return css::registry::RegistryKeyType_KEY;
+}
+
+css::registry::RegistryValueType RegistryKey::getValueType()
+{
+ std::unique_lock g(service_.mutex_);
+ service_.checkValid();
+ css::uno::Type t(value_.getValueType());
+ switch (t.getTypeClass()) {
+ case css::uno::TypeClass_LONG:
+ return css::registry::RegistryValueType_LONG;
+ case css::uno::TypeClass_STRING:
+ return css::registry::RegistryValueType_STRING;
+ case css::uno::TypeClass_SEQUENCE:
+ if (t == cppu::UnoType< css::uno::Sequence< sal_Int8 > >::get()) {
+ return css::registry::RegistryValueType_BINARY;
+ } else if (t == cppu::UnoType< css::uno::Sequence< sal_Int32 > >::get())
+ {
+ return css::registry::RegistryValueType_LONGLIST;
+ } else if (t ==
+ cppu::UnoType< css::uno::Sequence< OUString > >::get())
+ {
+ return css::registry::RegistryValueType_STRINGLIST;
+ }
+ [[fallthrough]];
+ default:
+ return css::registry::RegistryValueType_NOT_DEFINED;
+ }
+}
+
+sal_Int32 RegistryKey::getLongValue()
+{
+ std::unique_lock g(service_.mutex_);
+ service_.checkValid();
+ sal_Int32 v = 0;
+ if (value_ >>= v) {
+ return v;
+ }
+ throw css::registry::InvalidValueException(
+ "com.sun.star.configuration.ConfigurationRegistry",
+ static_cast< cppu::OWeakObject * >(this));
+}
+
+void RegistryKey::setLongValue(sal_Int32)
+{
+ throw css::uno::RuntimeException(
+ "com.sun.star.configuration.ConfigurationRegistry: not implemented",
+ static_cast< cppu::OWeakObject * >(this));
+}
+
+css::uno::Sequence< sal_Int32 > RegistryKey::getLongListValue()
+{
+ std::unique_lock g(service_.mutex_);
+ service_.checkValid();
+ css::uno::Sequence< sal_Int32 > v;
+ if (value_ >>= v) {
+ return v;
+ }
+ throw css::registry::InvalidValueException(
+ "com.sun.star.configuration.ConfigurationRegistry",
+ static_cast< cppu::OWeakObject * >(this));
+}
+
+void RegistryKey::setLongListValue(css::uno::Sequence< sal_Int32 > const &)
+{
+ throw css::uno::RuntimeException(
+ "com.sun.star.configuration.ConfigurationRegistry: not implemented",
+ static_cast< cppu::OWeakObject * >(this));
+}
+
+OUString RegistryKey::getAsciiValue()
+{
+ std::unique_lock g(service_.mutex_);
+ service_.checkValid();
+ OUString v;
+ if (value_ >>= v) {
+ return v;
+ }
+ throw css::registry::InvalidValueException(
+ "com.sun.star.configuration.ConfigurationRegistry",
+ static_cast< cppu::OWeakObject * >(this));
+}
+
+void RegistryKey::setAsciiValue(OUString const &)
+{
+ throw css::uno::RuntimeException(
+ "com.sun.star.configuration.ConfigurationRegistry: not implemented",
+ static_cast< cppu::OWeakObject * >(this));
+}
+
+css::uno::Sequence< OUString > RegistryKey::getAsciiListValue()
+{
+ std::unique_lock g(service_.mutex_);
+ service_.checkValid();
+ css::uno::Sequence< OUString > v;
+ if (value_ >>= v) {
+ return v;
+ }
+ throw css::registry::InvalidValueException(
+ "com.sun.star.configuration.ConfigurationRegistry",
+ static_cast< cppu::OWeakObject * >(this));
+}
+
+void RegistryKey::setAsciiListValue(css::uno::Sequence< OUString > const &)
+{
+ throw css::uno::RuntimeException(
+ "com.sun.star.configuration.ConfigurationRegistry: not implemented",
+ static_cast< cppu::OWeakObject * >(this));
+}
+
+OUString RegistryKey::getStringValue()
+{
+ std::unique_lock g(service_.mutex_);
+ service_.checkValid();
+ OUString v;
+ if (value_ >>= v) {
+ return v;
+ }
+ throw css::registry::InvalidValueException(
+ "com.sun.star.configuration.ConfigurationRegistry",
+ static_cast< cppu::OWeakObject * >(this));
+}
+
+void RegistryKey::setStringValue(OUString const &)
+{
+ throw css::uno::RuntimeException(
+ "com.sun.star.configuration.ConfigurationRegistry: not implemented",
+ static_cast< cppu::OWeakObject * >(this));
+}
+
+css::uno::Sequence< OUString > RegistryKey::getStringListValue()
+{
+ std::unique_lock g(service_.mutex_);
+ service_.checkValid();
+ css::uno::Sequence< OUString > v;
+ if (value_ >>= v) {
+ return v;
+ }
+ throw css::registry::InvalidValueException(
+ "com.sun.star.configuration.ConfigurationRegistry",
+ static_cast< cppu::OWeakObject * >(this));
+}
+
+void RegistryKey::setStringListValue(
+ css::uno::Sequence< OUString > const &)
+{
+ throw css::uno::RuntimeException(
+ "com.sun.star.configuration.ConfigurationRegistry: not implemented",
+ static_cast< cppu::OWeakObject * >(this));
+}
+
+css::uno::Sequence< sal_Int8 > RegistryKey::getBinaryValue()
+{
+ std::unique_lock g(service_.mutex_);
+ service_.checkValid();
+ css::uno::Sequence< sal_Int8 > v;
+ if (value_ >>= v) {
+ return v;
+ }
+ throw css::registry::InvalidValueException(
+ "com.sun.star.configuration.ConfigurationRegistry",
+ static_cast< cppu::OWeakObject * >(this));
+}
+
+void RegistryKey::setBinaryValue(css::uno::Sequence< sal_Int8 > const &)
+{
+ throw css::uno::RuntimeException(
+ "com.sun.star.configuration.ConfigurationRegistry: not implemented",
+ static_cast< cppu::OWeakObject * >(this));
+}
+
+css::uno::Reference< css::registry::XRegistryKey > RegistryKey::openKey(
+ OUString const & aKeyName)
+{
+ std::unique_lock g(service_.mutex_);
+ service_.checkValid_RuntimeException();
+ css::uno::Reference< css::container::XHierarchicalNameAccess > access;
+ if (value_ >>= access) {
+ try {
+ return new RegistryKey(
+ service_, access->getByHierarchicalName(aKeyName));
+ } catch (css::container::NoSuchElementException &) {}
+ }
+ return css::uno::Reference< css::registry::XRegistryKey >();
+}
+
+css::uno::Reference< css::registry::XRegistryKey > RegistryKey::createKey(
+ OUString const &)
+{
+ throw css::uno::RuntimeException(
+ "com.sun.star.configuration.ConfigurationRegistry: not implemented",
+ static_cast< cppu::OWeakObject * >(this));
+}
+
+void RegistryKey::closeKey()
+{
+ std::unique_lock g(service_.mutex_);
+ service_.checkValid_RuntimeException();
+}
+
+void RegistryKey::deleteKey(OUString const &)
+{
+ throw css::uno::RuntimeException(
+ "com.sun.star.configuration.ConfigurationRegistry: not implemented",
+ static_cast< cppu::OWeakObject * >(this));
+}
+
+css::uno::Sequence< css::uno::Reference< css::registry::XRegistryKey > >
+RegistryKey::openKeys()
+{
+ throw css::uno::RuntimeException(
+ "com.sun.star.configuration.ConfigurationRegistry: not implemented",
+ static_cast< cppu::OWeakObject * >(this));
+}
+
+css::uno::Sequence< OUString > RegistryKey::getKeyNames()
+{
+ throw css::uno::RuntimeException(
+ "com.sun.star.configuration.ConfigurationRegistry: not implemented",
+ static_cast< cppu::OWeakObject * >(this));
+}
+
+sal_Bool RegistryKey::createLink(OUString const &, OUString const &)
+{
+ std::unique_lock g(service_.mutex_);
+ service_.checkValid_RuntimeException();
+ return false;
+}
+
+void RegistryKey::deleteLink(OUString const &)
+{
+ std::unique_lock g(service_.mutex_);
+ service_.checkValid_RuntimeException();
+}
+
+OUString RegistryKey::getLinkTarget(OUString const &)
+{
+ std::unique_lock g(service_.mutex_);
+ service_.checkValid_RuntimeException();
+ return OUString();
+}
+
+OUString RegistryKey::getResolvedName(OUString const & aKeyName)
+{
+ std::unique_lock g(service_.mutex_);
+ service_.checkValid_RuntimeException();
+ return aKeyName;
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_configuration_ConfigurationRegistry_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new Service(context));
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/data.cxx b/configmgr/source/data.cxx
new file mode 100644
index 000000000..f173ee155
--- /dev/null
+++ b/configmgr/source/data.cxx
@@ -0,0 +1,336 @@
+/* -*- 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 <algorithm>
+#include <cassert>
+#include <cstddef>
+
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include <rtl/ref.hxx>
+#include <rtl/string.h>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/ustring.h>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <sal/types.h>
+#include <o3tl/string_view.hxx>
+
+#include "additions.hxx"
+#include "data.hxx"
+#include "node.hxx"
+#include "nodemap.hxx"
+#include "rootnode.hxx"
+#include "setnode.hxx"
+
+namespace configmgr {
+
+namespace {
+
+bool decode(
+ std::u16string_view encoded, std::size_t begin, std::size_t end,
+ OUString * decoded)
+{
+ assert(
+ begin <= end && end <= encoded.size() &&
+ decoded != nullptr);
+ OUStringBuffer buf(end - begin);
+ while (begin != end) {
+ sal_Unicode c = encoded[begin++];
+ if (c == '&') {
+ if (o3tl::starts_with(encoded.substr(begin), u"amp;")) {
+ buf.append('&');
+ begin += RTL_CONSTASCII_LENGTH("amp;");
+ } else if (o3tl::starts_with(encoded.substr(begin), u"quot;")) {
+ buf.append('"');
+ begin += RTL_CONSTASCII_LENGTH("quot;");
+ } else if (o3tl::starts_with(encoded.substr(begin), u"apos;")) {
+ buf.append('\'');
+ begin += RTL_CONSTASCII_LENGTH("apos;");
+ } else {
+ return false;
+ }
+ assert(begin <= end);
+ } else {
+ buf.append(c);
+ }
+ }
+ *decoded = buf.makeStringAndClear();
+ return true;
+}
+
+}
+
+OUString Data::createSegment(
+ std::u16string_view templateName, OUString const & name)
+{
+ if (templateName.empty()) {
+ return name;
+ }
+ OUStringBuffer buf(128);
+ buf.append(templateName);
+ //TODO: verify template name contains no bad chars?
+ buf.append("['");
+ for (sal_Int32 i = 0; i < name.getLength(); ++i) {
+ sal_Unicode c = name[i];
+ switch (c) {
+ case '&':
+ buf.append("&amp;");
+ break;
+ case '"':
+ buf.append("&quot;");
+ break;
+ case '\'':
+ buf.append("&apos;");
+ break;
+ default:
+ buf.append(c);
+ break;
+ }
+ }
+ buf.append("']");
+ return buf.makeStringAndClear();
+}
+
+sal_Int32 Data::parseSegment(
+ OUString const & path, sal_Int32 index, OUString * name,
+ bool * setElement, OUString * templateName)
+{
+ assert(
+ index >= 0 && index <= path.getLength() && name != nullptr &&
+ setElement != nullptr);
+ sal_Int32 i = index;
+ while (i < path.getLength() && path[i] != '/' && path[i] != '[') {
+ ++i;
+ }
+ if (i == path.getLength() || path[i] == '/') {
+ *name = path.copy(index, i - index);
+ *setElement = false;
+ return i;
+ }
+ if (templateName != nullptr) {
+ if (i - index == 1 && path[index] == '*') {
+ templateName->clear();
+ } else {
+ *templateName = path.copy(index, i - index);
+ }
+ }
+ if (++i == path.getLength()) {
+ return -1;
+ }
+ sal_Unicode del = path[i++];
+ if (del != '\'' && del != '"') {
+ return -1;
+ }
+ sal_Int32 j = path.indexOf(del, i);
+ if (j == -1 || j + 1 == path.getLength() || path[j + 1] != ']' ||
+ !decode(path, i, j, name))
+ {
+ return -1;
+ }
+ *setElement = true;
+ return j + 2;
+}
+
+OUString Data::fullTemplateName(
+ std::u16string_view component, std::u16string_view name)
+{
+ if (component.find(':') != std::u16string_view::npos || name.find(':') != std::u16string_view::npos) {
+ throw css::uno::RuntimeException(
+ OUString::Concat("bad component/name pair containing colon ") + component + "/" +
+ name);
+ }
+ return OUString::Concat(component) + ":" + name;
+}
+
+bool Data::equalTemplateNames(
+ OUString const & shortName, OUString const & longName)
+{
+ if (shortName.indexOf(':') == -1) {
+ sal_Int32 i = longName.indexOf(':') + 1;
+ assert(i > 0);
+ return
+ rtl_ustr_compare_WithLength(
+ shortName.getStr(), shortName.getLength(),
+ longName.getStr() + i, longName.getLength() - i) ==
+ 0;
+ } else {
+ return shortName == longName;
+ }
+}
+
+Data::Data(): root_(new RootNode) {}
+
+rtl::Reference< Node > Data::resolvePathRepresentation(
+ OUString const & pathRepresentation,
+ OUString * canonicRepresentation, std::vector<OUString> * path, int * finalizedLayer)
+ const
+{
+ if (pathRepresentation.isEmpty() || pathRepresentation[0] != '/') {
+ throw css::uno::RuntimeException(
+ "bad path " + pathRepresentation);
+ }
+ if (path != nullptr) {
+ path->clear();
+ }
+ if (pathRepresentation == "/") {
+ if (canonicRepresentation != nullptr) {
+ *canonicRepresentation = pathRepresentation;
+ }
+ if (finalizedLayer != nullptr) {
+ *finalizedLayer = NO_LAYER;
+ }
+ return root_;
+ }
+ OUString seg;
+ bool setElement;
+ OUString templateName;
+ sal_Int32 n = parseSegment(pathRepresentation, 1, &seg, &setElement, nullptr);
+ if (n == -1 || setElement)
+ {
+ throw css::uno::RuntimeException(
+ "bad path " + pathRepresentation);
+ }
+ NodeMap const & components = getComponents();
+ NodeMap::const_iterator i(components.find(seg));
+ OUStringBuffer canonic(128);
+ rtl::Reference< Node > parent;
+ int finalized = NO_LAYER;
+ for (rtl::Reference< Node > p(i == components.end() ? nullptr : i->second);;) {
+ if (!p.is()) {
+ return p;
+ }
+ if (canonicRepresentation != nullptr) {
+ canonic.append('/');
+ canonic.append(createSegment(templateName, seg));
+ }
+ if (path != nullptr) {
+ path->push_back(seg);
+ }
+ finalized = std::min(finalized, p->getFinalized());
+ if (n != pathRepresentation.getLength() &&
+ pathRepresentation[n++] != '/')
+ {
+ throw css::uno::RuntimeException(
+ "bad path " + pathRepresentation);
+ }
+ // for backwards compatibility, ignore a final slash
+ if (n == pathRepresentation.getLength()) {
+ if (canonicRepresentation != nullptr) {
+ *canonicRepresentation = canonic.makeStringAndClear();
+ }
+ if (finalizedLayer != nullptr) {
+ *finalizedLayer = finalized;
+ }
+ return p;
+ }
+ parent = p;
+ templateName.clear();
+ n = parseSegment(
+ pathRepresentation, n, &seg, &setElement, &templateName);
+ if (n == -1) {
+ throw css::uno::RuntimeException(
+ "bad path " + pathRepresentation);
+ }
+ // For backwards compatibility, allow set members to be accessed with
+ // simple path segments, like group members:
+ p = p->getMember(seg);
+ if (setElement) {
+ switch (parent->kind()) {
+ case Node::KIND_LOCALIZED_PROPERTY:
+ if (!templateName.isEmpty()) {
+ throw css::uno::RuntimeException(
+ "bad path " + pathRepresentation);
+ }
+ break;
+ case Node::KIND_SET:
+ if (!templateName.isEmpty() &&
+ !static_cast< SetNode * >(parent.get())->isValidTemplate(
+ templateName))
+ {
+ throw css::uno::RuntimeException(
+ "bad path " + pathRepresentation);
+ }
+ break;
+ default:
+ throw css::uno::RuntimeException(
+ "bad path " + pathRepresentation);
+ }
+ if (!templateName.isEmpty() && p != nullptr) {
+ assert(!p->getTemplateName().isEmpty());
+ if (!equalTemplateNames(templateName, p->getTemplateName())) {
+ throw css::uno::RuntimeException(
+ "bad path " + pathRepresentation);
+ }
+ }
+ }
+ }
+}
+
+rtl::Reference< Node > Data::getTemplate(
+ int layer, OUString const & fullName) const
+{
+ return templates.findNode(layer, fullName);
+}
+
+NodeMap & Data::getComponents() const {
+ return root_->getMembers();
+}
+
+Additions * Data::addExtensionXcuAdditions(
+ OUString const & url, int layer)
+{
+ rtl::Reference item(new ExtensionXcu);
+ ExtensionXcuAdditions::iterator i(
+ extensionXcuAdditions_.emplace(
+ url, rtl::Reference< ExtensionXcu >()).first);
+ if (i->second.is()) {
+ throw css::uno::RuntimeException(
+ "already added extension xcu " + url);
+ }
+ i->second = item;
+ item->layer = layer;
+ return &item->additions;
+}
+
+rtl::Reference< Data::ExtensionXcu > Data::removeExtensionXcuAdditions(
+ OUString const & url)
+{
+ ExtensionXcuAdditions::iterator i(extensionXcuAdditions_.find(url));
+ if (i == extensionXcuAdditions_.end()) {
+ // This can happen, as migration of pre OOo 3.3 UserInstallation
+ // extensions in dp_registry::backend::configuration::BackendImpl::
+ // PackageImpl::processPackage_ can cause just-in-time creation of
+ // extension xcu files that are never added via addExtensionXcuAdditions
+ // (also, there might be url spelling differences between calls to
+ // addExtensionXcuAdditions and removeExtensionXcuAdditions?):
+ SAL_INFO(
+ "configmgr",
+ "unknown Data::removeExtensionXcuAdditions(" << url << ")");
+ return rtl::Reference< ExtensionXcu >();
+ }
+ rtl::Reference< ExtensionXcu > item(i->second);
+ extensionXcuAdditions_.erase(i);
+ return item;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/data.hxx b/configmgr/source/data.hxx
new file mode 100644
index 000000000..c3614e643
--- /dev/null
+++ b/configmgr/source/data.hxx
@@ -0,0 +1,99 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <climits>
+#include "config_map.hxx"
+#include <vector>
+
+#include <rtl/ref.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+#include <salhelper/simplereferenceobject.hxx>
+
+#include "additions.hxx"
+#include "modifications.hxx"
+#include "nodemap.hxx"
+
+namespace configmgr {
+
+class Node;
+
+struct Data {
+ enum { NO_LAYER = INT_MAX };
+
+ struct ExtensionXcu: public salhelper::SimpleReferenceObject {
+ int layer;
+ Additions additions;
+ };
+
+ NodeMap templates;
+
+ Modifications modifications;
+
+ static OUString createSegment(
+ std::u16string_view templateName, OUString const & name);
+
+ static sal_Int32 parseSegment(
+ OUString const & path, sal_Int32 index, OUString * name,
+ bool * setElement, OUString * templateName);
+
+ static OUString fullTemplateName(
+ std::u16string_view component, std::u16string_view name);
+
+ //TODO: better rules under which circumstances a short template name matches
+ static bool equalTemplateNames(
+ OUString const & shortName, OUString const & longName);
+
+ Data();
+
+ rtl::Reference< Node > resolvePathRepresentation(
+ OUString const & pathRepresentation,
+ OUString * canonicRepresentation, std::vector<OUString> * path, int * finalizedLayer)
+ const;
+
+ rtl::Reference< Node > getTemplate(
+ int layer, OUString const & fullName) const;
+
+ NodeMap & getComponents() const;
+
+ Additions * addExtensionXcuAdditions(
+ OUString const & url, int layer);
+
+ rtl::Reference< ExtensionXcu > removeExtensionXcuAdditions(
+ OUString const & url);
+
+private:
+ Data(const Data&) = delete;
+ Data& operator=(const Data&) = delete;
+
+ typedef config_map< rtl::Reference< ExtensionXcu > >
+ ExtensionXcuAdditions;
+
+ rtl::Reference< Node > root_;
+
+ ExtensionXcuAdditions extensionXcuAdditions_;
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/dconf.cxx b/configmgr/source/dconf.cxx
new file mode 100644
index 000000000..9db51fa17
--- /dev/null
+++ b/configmgr/source/dconf.cxx
@@ -0,0 +1,1598 @@
+/* -*- 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/.
+ */
+
+#include <sal/config.h>
+
+#include <cassert>
+#include <cstddef>
+#include <cstring>
+#include <forward_list>
+#include <limits>
+#include <vector>
+
+extern "C" {
+ //TODO: <https://bugzilla.gnome.org/show_bug.cgi?id=754245>
+ // "common/dconf-changeset.h etc. lack extern "C" wrapper for C++", fixed on current dconf
+ // master (towards 0.40?) now with
+ // <https://gitlab.gnome.org/GNOME/dconf/-/commit/db3d4df6d1a763698f27b013dc42da8d4ae02639>
+ // "Merge branch 'wip/issue-23' into 'master'"
+#include <dconf/dconf.h>
+}
+
+#include <com/sun/star/uno/Sequence.hxx>
+#include <o3tl/safeint.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+
+#include "data.hxx"
+#include "dconf.hxx"
+#include "groupnode.hxx"
+#include "localizedpropertynode.hxx"
+#include "localizedvaluenode.hxx"
+#include "nodemap.hxx"
+#include "propertynode.hxx"
+#include "setnode.hxx"
+
+// component-data is encoded in dconf as follows:
+//
+// * The node hierarchy (starting at component nodes with names like
+// "org.openoffice.Setup") maps to dconf paths underneath
+// "/org/libreoffice/registry/".
+//
+// * Component, group, set, and localized property nodes map to dconf dirs,
+// while property and localized value nodes map to dconf keys.
+//
+// * The names of nodes that are not set elements are used directly as dconf
+// path segments. (The syntax for node names is any non-empty sequences of
+// any Unicode scalar values except U+0000--0008, U+000B--000C, U+000E--001F,
+// U+002F SOLIDUS, and U+FFFE--FFFF. TODO: "<aruiz> sberg, in general I think
+// it'd be nice if you used path separators instead of dots though, they have
+// meaning in dconf/gvdb world :-)"?)
+//
+// * The names of set element nodes are encoded as dconf path segments as
+// follows: each occurrence of U+0000 NULL is replace by the three characters
+// "\00", each occurrence of U+002F SOLIDUS is replaced by the three
+// characters "\2F", and each occurrence of U+005C REVERSE SOLIDUS is replaced
+// by the three characters "\5C".
+//
+// * Set elements (which must themselves be either sets or groups) map to
+// "indirection" dconf dirs as follows:
+//
+// ** The dir must contain a key named "op" of string type, with a value of
+// "fuse", "replace", or "remove".
+//
+// ** If "op" is "fuse" or "replace", the dir must contain exactly the following
+// further keys and dirs:
+//
+// *** The dir must contain a key named "template" of string type, containing
+// the full template name, encoded as follows: each occurrence of U+0000
+// NULL is replace by the three characters "\00" and each occurrence of
+// U+005C REVERSE SOLIDUS is replaced by the three characters "\5C".
+//
+// *** The dir must contain a dir named "content" that contains the set
+// element's (i.e., set or group node's) real content.
+//
+// ** If "op" is "remove", the dir must contain no further keys or dirs.
+//
+// * Property and localized property value "fuse" operations map to GVariant
+// instances as follows:
+//
+// ** Non-nillable boolean values map to GVariant boolean instances.
+//
+// ** Non-nillable short values map to GVariant int16 instances.
+//
+// ** Non-nillable int values map to GVariant int32 instances.
+//
+// ** Non-nillable long values map to GVariant int64 instances.
+//
+// ** Non-nillable double values map to GVariant double instances.
+//
+// ** Non-nillable string values map to GVariant string instances, with the
+// following encoding: each occurrence of U+0000 NULL is replace by the three
+// characters "\00" and each occurrence of U+005C REVERSE SOLIDUS is replaced
+// by the three characters "\5C".
+//
+// ** Non-nillable hexbinary values map to GVariant byte array instances.
+//
+// ** Non-nillable list values recursively map to GVariant array instances.
+//
+// ** Nillable values recursively map to GVariant maybe instances.
+//
+// * Property "remove" operations map to GVariant instances of empty tuple type.
+//
+// Finalization: The component-update.dtd allows for finalization of
+// oor:component-data, node, and prop elements, while dconf allows for locking
+// of individual keys. That does not match, but just mark the individual Node
+// instances that correspond to individual dconf keys as finalized for
+// non-writable dconf keys.
+//
+// TODO: support "mandatory" and "external"?
+
+namespace configmgr::dconf {
+
+namespace {
+
+template<typename T> class GObjectHolder {
+public:
+ explicit GObjectHolder(T * object): object_(object) {}
+
+ ~GObjectHolder() {
+ if (object_ != nullptr) {
+ g_object_unref(object_);
+ }
+ }
+
+ T * get() const { return object_; }
+
+private:
+ GObjectHolder(GObjectHolder const &) = delete;
+ GObjectHolder& operator =(GObjectHolder const &) = delete;
+
+ T * object_;
+};
+
+class GVariantHolder {
+public:
+ explicit GVariantHolder(GVariant * variant = nullptr): variant_(variant) {}
+
+ ~GVariantHolder() { unref(); }
+
+ void reset(GVariant * variant) {
+ unref();
+ variant_ = variant;
+ }
+
+ void release() { variant_ = nullptr; }
+
+ GVariant * get() const { return variant_; }
+
+private:
+ GVariantHolder(GVariantHolder const &) = delete;
+ GVariantHolder& operator =(GVariantHolder const &) = delete;
+
+ void unref() {
+ if (variant_ != nullptr) {
+ g_variant_unref(variant_);
+ }
+ }
+
+ GVariant * variant_;
+};
+
+class GVariantTypeHolder {
+public:
+ explicit GVariantTypeHolder(GVariantType * type): type_(type) {}
+
+ ~GVariantTypeHolder() {
+ if (type_ != nullptr) {
+ g_variant_type_free(type_);
+ }
+ }
+
+ GVariantType * get() const { return type_; }
+
+private:
+ GVariantTypeHolder(GVariantTypeHolder const &) = delete;
+ GVariantTypeHolder& operator =(GVariantTypeHolder const &) = delete;
+
+ GVariantType * type_;
+};
+
+class StringArrayHolder {
+public:
+ explicit StringArrayHolder(gchar ** array): array_(array) {}
+
+ ~StringArrayHolder() { g_strfreev(array_); }
+
+ gchar ** get() const { return array_; }
+
+private:
+ StringArrayHolder(StringArrayHolder const &) = delete;
+ StringArrayHolder& operator =(StringArrayHolder const &) = delete;
+
+ gchar ** array_;
+};
+
+class ChangesetHolder {
+public:
+ explicit ChangesetHolder(DConfChangeset * changeset):
+ changeset_(changeset)
+ {}
+
+ ~ChangesetHolder() {
+ if (changeset_ != nullptr) {
+ dconf_changeset_unref(changeset_);
+ }
+ }
+
+ DConfChangeset * get() const { return changeset_; }
+
+private:
+ ChangesetHolder(ChangesetHolder const &) = delete;
+ ChangesetHolder& operator =(ChangesetHolder const &) = delete;
+
+ DConfChangeset * changeset_;
+};
+
+OString getRoot() {
+ return "/org/libreoffice/registry";
+}
+
+bool decode(OUString * string, bool slash) {
+ for (sal_Int32 i = 0;; ++i) {
+ i = string->indexOf('\\', i);
+ if (i == -1) {
+ return true;
+ }
+ if (string->match("00", i + 1)) {
+ *string = string->replaceAt(i, 3, OUStringChar(u'\0'));
+ } else if (slash && string->match("2F", i + 1)) {
+ *string = string->replaceAt(i, 3, u"/");
+ } else if (string->match("5C", i + 1)) {
+ *string = string->replaceAt(i + 1, 2, u"");
+ } else {
+ SAL_WARN("configmgr.dconf", "bad escape in " << *string);
+ return false;
+ }
+ }
+}
+
+bool getBoolean(
+ OString const & key, GVariantHolder const & variant, css::uno::Any * value)
+{
+ assert(value != nullptr);
+ if (!g_variant_is_of_type(variant.get(), G_VARIANT_TYPE_BOOLEAN)) {
+ SAL_WARN(
+ "configmgr.dconf",
+ "bad key " << key << " does not match boolean property");
+ return false;
+ }
+ *value <<= bool(g_variant_get_boolean(variant.get()));
+ return true;
+}
+
+bool getShort(
+ OString const & key, GVariantHolder const & variant, css::uno::Any * value)
+{
+ assert(value != nullptr);
+ if (!g_variant_is_of_type(variant.get(), G_VARIANT_TYPE_INT16)) {
+ SAL_WARN(
+ "configmgr.dconf",
+ "bad key " << key << " does not match short property");
+ return false;
+ }
+ *value <<= sal_Int16(g_variant_get_int16(variant.get()));
+ return true;
+}
+
+bool getInt(
+ OString const & key, GVariantHolder const & variant, css::uno::Any * value)
+{
+ assert(value != nullptr);
+ if (!g_variant_is_of_type(variant.get(), G_VARIANT_TYPE_INT32)) {
+ SAL_WARN(
+ "configmgr.dconf",
+ "bad key " << key << " does not match int property");
+ return false;
+ }
+ *value <<= sal_Int32(g_variant_get_int32(variant.get()));
+ return true;
+}
+
+bool getLong(
+ OString const & key, GVariantHolder const & variant, css::uno::Any * value)
+{
+ assert(value != nullptr);
+ if (!g_variant_is_of_type(variant.get(), G_VARIANT_TYPE_INT64)) {
+ SAL_WARN(
+ "configmgr.dconf",
+ "bad key " << key << " does not match long property");
+ return false;
+ }
+ *value <<= sal_Int64(g_variant_get_int64(variant.get()));
+ return true;
+}
+
+bool getDouble(
+ OString const & key, GVariantHolder const & variant, css::uno::Any * value)
+{
+ assert(value != nullptr);
+ if (!g_variant_is_of_type(variant.get(), G_VARIANT_TYPE_DOUBLE)) {
+ SAL_WARN(
+ "configmgr.dconf",
+ "bad key " << key << " does not match double property");
+ return false;
+ }
+ *value <<= double(g_variant_get_double(variant.get()));
+ return true;
+}
+
+bool getStringValue(
+ OString const & key, GVariantHolder const & variant, OUString * value)
+{
+ assert(value != nullptr);
+ if (!g_variant_is_of_type(variant.get(), G_VARIANT_TYPE_STRING)) {
+ SAL_WARN(
+ "configmgr.dconf",
+ "bad key " << key << " does not match string property");
+ return false;
+ }
+ gsize n;
+ char const * p = g_variant_get_string(variant.get(), &n);
+ if (n > o3tl::make_unsigned(
+ std::numeric_limits<sal_Int32>::max()))
+ {
+ SAL_WARN("configmgr.dconf", "too long string value for key " << key);
+ return false;
+ }
+ if (!rtl_convertStringToUString(
+ &value->pData, p, static_cast<sal_Int32>(n), RTL_TEXTENCODING_UTF8,
+ (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR
+ | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR
+ | RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)))
+ {
+ SAL_WARN("configmgr.dconf", "non--UTF-8 string value for key " << key);
+ return false;
+ }
+ return decode(value, false);
+}
+
+bool getString(
+ OString const & key, GVariantHolder const & variant, css::uno::Any * value)
+{
+ assert(value != nullptr);
+ OUString v;
+ if (!getStringValue(key, variant, &v)) {
+ return false;
+ }
+ *value <<= v;
+ return true;
+}
+
+bool getHexbinaryValue(
+ OString const & key, GVariantHolder const & variant,
+ css::uno::Sequence<sal_Int8> * value)
+{
+ assert(value != nullptr);
+ if (std::strcmp(g_variant_get_type_string(variant.get()), "ay") != 0) {
+ SAL_WARN(
+ "configmgr.dconf",
+ "bad key " << key << " does not match hexbinary property");
+ return false;
+ }
+ gsize n;
+ gconstpointer p = g_variant_get_fixed_array(
+ variant.get(), &n, sizeof (guchar));
+ if (n > o3tl::make_unsigned(
+ std::numeric_limits<sal_Int32>::max()))
+ {
+ SAL_WARN("configmgr.dconf", "too long hexbinary value for key " << key);
+ return false;
+ }
+ value->realloc(static_cast<sal_Int32>(n));
+ static_assert(sizeof (sal_Int8) == sizeof (guchar), "size mismatch");
+ std::memcpy(value->getArray(), p, n * sizeof (guchar));
+ // assuming that n * sizeof (guchar) is small enough for std::size_t
+ return true;
+}
+
+bool getHexbinary(
+ OString const & key, GVariantHolder const & variant, css::uno::Any * value)
+{
+ assert(value != nullptr);
+ css::uno::Sequence<sal_Int8> v;
+ if (!getHexbinaryValue(key, variant, &v)) {
+ return false;
+ }
+ *value <<= v;
+ return true;
+}
+
+bool getBooleanList(
+ OString const & key, GVariantHolder const & variant, css::uno::Any * value)
+{
+ assert(value != nullptr);
+ if (std::strcmp(g_variant_get_type_string(variant.get()), "ab") != 0) {
+ SAL_WARN(
+ "configmgr.dconf",
+ "bad key " << key << " does not match boolean list property");
+ return false;
+ }
+ gsize n;
+ gconstpointer p = g_variant_get_fixed_array(
+ variant.get(), &n, sizeof (guchar));
+ if (n > o3tl::make_unsigned(
+ std::numeric_limits<sal_Int32>::max()))
+ {
+ SAL_WARN("configmgr.dconf", "too long boolean list for key " << key);
+ return false;
+ }
+ css::uno::Sequence<sal_Bool> v(static_cast<sal_Int32>(n));
+ static_assert(sizeof (sal_Bool) == sizeof (guchar), "size mismatch");
+ std::memcpy(v.getArray(), p, n * sizeof (guchar));
+ // assuming that n * sizeof (guchar) is small enough for std::size_t
+ *value <<= v;
+ return true;
+}
+
+bool getShortList(
+ OString const & key, GVariantHolder const & variant, css::uno::Any * value)
+{
+ assert(value != nullptr);
+ if (std::strcmp(g_variant_get_type_string(variant.get()), "an") != 0) {
+ SAL_WARN(
+ "configmgr.dconf",
+ "bad key " << key << " does not match short list property");
+ return false;
+ }
+ gsize n;
+ gconstpointer p = g_variant_get_fixed_array(
+ variant.get(), &n, sizeof (gint16));
+ if (n > o3tl::make_unsigned(
+ std::numeric_limits<sal_Int32>::max()))
+ {
+ SAL_WARN("configmgr.dconf", "too long short list for key " << key);
+ return false;
+ }
+ css::uno::Sequence<sal_Int16> v(static_cast<sal_Int32>(n));
+ static_assert(sizeof (sal_Int16) == sizeof (gint16), "size mismatch");
+ std::memcpy(v.getArray(), p, n * sizeof (gint16));
+ // assuming that n * sizeof (gint16) is small enough for std::size_t
+ *value <<= v;
+ return true;
+}
+
+bool getIntList(
+ OString const & key, GVariantHolder const & variant, css::uno::Any * value)
+{
+ assert(value != nullptr);
+ if (std::strcmp(g_variant_get_type_string(variant.get()), "ai") != 0) {
+ SAL_WARN(
+ "configmgr.dconf",
+ "bad key " << key << " does not match int list property");
+ return false;
+ }
+ gsize n;
+ gconstpointer p = g_variant_get_fixed_array(
+ variant.get(), &n, sizeof (gint32));
+ if (n > o3tl::make_unsigned(
+ std::numeric_limits<sal_Int32>::max()))
+ {
+ SAL_WARN("configmgr.dconf", "too long int list for key " << key);
+ return false;
+ }
+ css::uno::Sequence<sal_Int32> v(static_cast<sal_Int32>(n));
+ static_assert(sizeof (sal_Int32) == sizeof (gint32), "size mismatch");
+ std::memcpy(v.getArray(), p, n * sizeof (gint32));
+ // assuming that n * sizeof (gint32) is small enough for std::size_t
+ *value <<= v;
+ return true;
+}
+
+bool getLongList(
+ OString const & key, GVariantHolder const & variant, css::uno::Any * value)
+{
+ assert(value != nullptr);
+ if (std::strcmp(g_variant_get_type_string(variant.get()), "ax") != 0) {
+ SAL_WARN(
+ "configmgr.dconf",
+ "bad key " << key << " does not match long list property");
+ return false;
+ }
+ gsize n;
+ gconstpointer p = g_variant_get_fixed_array(
+ variant.get(), &n, sizeof (gint64));
+ if (n > o3tl::make_unsigned(
+ std::numeric_limits<sal_Int32>::max()))
+ {
+ SAL_WARN("configmgr.dconf", "too long long list for key " << key);
+ return false;
+ }
+ css::uno::Sequence<sal_Int64> v(static_cast<sal_Int32>(n));
+ static_assert(sizeof (sal_Int64) == sizeof (gint64), "size mismatch");
+ std::memcpy(v.getArray(), p, n * sizeof (gint64));
+ // assuming that n * sizeof (gint64) is small enough for std::size_t
+ *value <<= v;
+ return true;
+}
+
+bool getDoubleList(
+ OString const & key, GVariantHolder const & variant, css::uno::Any * value)
+{
+ assert(value != nullptr);
+ if (std::strcmp(g_variant_get_type_string(variant.get()), "ad") != 0) {
+ SAL_WARN(
+ "configmgr.dconf",
+ "bad key " << key << " does not match double list property");
+ return false;
+ }
+ gsize n;
+ gconstpointer p = g_variant_get_fixed_array(
+ variant.get(), &n, sizeof (gdouble));
+ if (n > o3tl::make_unsigned(
+ std::numeric_limits<sal_Int32>::max()))
+ {
+ SAL_WARN("configmgr.dconf", "too long double list for key " << key);
+ return false;
+ }
+ css::uno::Sequence<double> v(static_cast<sal_Int32>(n));
+ static_assert(std::is_same<double, gdouble>::value, "type mismatch");
+ std::memcpy(v.getArray(), p, n * sizeof (gdouble));
+ // assuming that n * sizeof (gdouble) is small enough for std::size_t
+ *value <<= v;
+ return true;
+}
+
+bool getStringList(
+ OString const & key, GVariantHolder const & variant, css::uno::Any * value)
+{
+ assert(value != nullptr);
+ if (std::strcmp(g_variant_get_type_string(variant.get()), "as") != 0) {
+ SAL_WARN(
+ "configmgr.dconf",
+ "bad key " << key << " does not match string list property");
+ return false;
+ }
+ gsize n = g_variant_n_children(variant.get());
+ if (n > o3tl::make_unsigned(
+ std::numeric_limits<sal_Int32>::max()))
+ {
+ SAL_WARN("configmgr.dconf", "too long string list for key " << key);
+ return false;
+ }
+ css::uno::Sequence<OUString> v(static_cast<sal_Int32>(n));
+ for (gsize i = 0; i != n; ++i) {
+ GVariantHolder c(g_variant_get_child_value(variant.get(), i));
+ if (!getStringValue(key, c, v.getArray() + i)) {
+ return false;
+ }
+ }
+ *value <<= v;
+ return true;
+}
+
+bool getHexbinaryList(
+ OString const & key, GVariantHolder const & variant, css::uno::Any * value)
+{
+ assert(value != nullptr);
+ if (std::strcmp(g_variant_get_type_string(variant.get()), "aay") != 0) {
+ SAL_WARN(
+ "configmgr.dconf",
+ "bad key " << key << " does not match hexbinary list property");
+ return false;
+ }
+ gsize n = g_variant_n_children(variant.get());
+ if (n > o3tl::make_unsigned(
+ std::numeric_limits<sal_Int32>::max()))
+ {
+ SAL_WARN("configmgr.dconf", "too long hexbinary list for key " << key);
+ return false;
+ }
+ css::uno::Sequence<css::uno::Sequence<sal_Int8>> v(
+ static_cast<sal_Int32>(n));
+ for (gsize i = 0; i != n; ++i) {
+ GVariantHolder c(g_variant_get_child_value(variant.get(), i));
+ if (!getHexbinaryValue(key, c, v.getArray() + i)) {
+ return false;
+ }
+ }
+ *value <<= v;
+ return true;
+}
+
+bool getAny(
+ OString const & key, GVariantHolder const & variant, css::uno::Any * value)
+{
+ char const * t = g_variant_get_type_string(variant.get());
+ if (std::strcmp(t, "b") == 0) {
+ return getBoolean(key, variant, value);
+ }
+ if (std::strcmp(t, "n") == 0) {
+ return getShort(key, variant, value);
+ }
+ if (std::strcmp(t, "i") == 0) {
+ return getInt(key, variant, value);
+ }
+ if (std::strcmp(t, "x") == 0) {
+ return getLong(key, variant, value);
+ }
+ if (std::strcmp(t, "d") == 0) {
+ return getDouble(key, variant, value);
+ }
+ if (std::strcmp(t, "s") == 0) {
+ return getString(key, variant, value);
+ }
+ if (std::strcmp(t, "ay") == 0) {
+ return getHexbinary(key, variant, value);
+ }
+ if (std::strcmp(t, "ab") == 0) {
+ return getBooleanList(key, variant, value);
+ }
+ if (std::strcmp(t, "an") == 0) {
+ return getShortList(key, variant, value);
+ }
+ if (std::strcmp(t, "ai") == 0) {
+ return getIntList(key, variant, value);
+ }
+ if (std::strcmp(t, "ax") == 0) {
+ return getLongList(key, variant, value);
+ }
+ if (std::strcmp(t, "ad") == 0) {
+ return getDoubleList(key, variant, value);
+ }
+ if (std::strcmp(t, "as") == 0) {
+ return getStringList(key, variant, value);
+ }
+ if (std::strcmp(t, "aay") == 0) {
+ return getHexbinaryList(key, variant, value);
+ }
+ SAL_WARN(
+ "configmgr.dconf", "bad key " << key << " does not match any property");
+ return false;
+}
+
+enum class ReadValue { Error, Value, Remove };
+
+ReadValue readValue(
+ GObjectHolder<DConfClient> const & client, OString const & path, Type type,
+ bool nillable, bool removable, css::uno::Any * value)
+{
+ assert(value != nullptr);
+ assert(!value->hasValue());
+ assert(!path.endsWith("/"));
+ GVariantHolder v(dconf_client_read(client.get(), path.getStr()));
+ if (v.get() == nullptr) {
+ SAL_WARN("configmgr.dconf", "cannot read key " << path);
+ return ReadValue::Error;
+ }
+ if (removable && std::strcmp(g_variant_get_type_string(v.get()), "()") == 0)
+ {
+ return ReadValue::Remove;
+ }
+ bool nil;
+ if (nillable) {
+ if (g_variant_classify(v.get()) != G_VARIANT_CLASS_MAYBE) {
+ SAL_WARN(
+ "configmgr.dconf",
+ "bad key " << path << " does not match nillable property");
+ }
+ v.reset(g_variant_get_maybe(v.get()));
+ nil = v.get() == nullptr;
+ } else {
+ nil = false;
+ }
+ if (!nil) {
+ switch (type) {
+ case TYPE_ANY:
+ if (!getAny(path, v, value)) {
+ return ReadValue::Error;
+ }
+ break;
+ case TYPE_BOOLEAN:
+ if (!getBoolean(path, v, value)) {
+ return ReadValue::Error;
+ }
+ break;
+ case TYPE_SHORT:
+ if (!getShort(path, v, value)) {
+ return ReadValue::Error;
+ }
+ break;
+ case TYPE_INT:
+ if (!getInt(path, v, value)) {
+ return ReadValue::Error;
+ }
+ break;
+ case TYPE_LONG:
+ if (!getLong(path, v, value)) {
+ return ReadValue::Error;
+ }
+ break;
+ case TYPE_DOUBLE:
+ if (!getDouble(path, v, value)) {
+ return ReadValue::Error;
+ }
+ break;
+ case TYPE_STRING:
+ if (!getString(path, v, value)) {
+ return ReadValue::Error;
+ }
+ break;
+ case TYPE_HEXBINARY:
+ if (!getHexbinary(path, v, value)) {
+ return ReadValue::Error;
+ }
+ break;
+ case TYPE_BOOLEAN_LIST:
+ if (!getBooleanList(path, v, value)) {
+ return ReadValue::Error;
+ }
+ break;
+ case TYPE_SHORT_LIST:
+ if (!getShortList(path, v, value)) {
+ return ReadValue::Error;
+ }
+ break;
+ case TYPE_INT_LIST:
+ if (!getIntList(path, v, value)) {
+ return ReadValue::Error;
+ }
+ break;
+ case TYPE_LONG_LIST:
+ if (!getLongList(path, v, value)) {
+ return ReadValue::Error;
+ }
+ break;
+ case TYPE_DOUBLE_LIST:
+ if (!getDoubleList(path, v, value)) {
+ return ReadValue::Error;
+ }
+ break;
+ case TYPE_STRING_LIST:
+ if (!getStringList(path, v, value)) {
+ return ReadValue::Error;
+ }
+ break;
+ case TYPE_HEXBINARY_LIST:
+ if (!getHexbinaryList(path, v, value)) {
+ return ReadValue::Error;
+ }
+ break;
+ default:
+ assert(false); // cannot happen
+ }
+ }
+ return ReadValue::Value;
+}
+
+void finalize(
+ GObjectHolder<DConfClient> const & client, OString const & path,
+ rtl::Reference<Node> const & node, int layer)
+{
+ if (!dconf_client_is_writable(client.get(), path.getStr())) {
+ node->setFinalized(layer);
+ }
+}
+
+void readDir(
+ Data & data, int layer, rtl::Reference<Node> const & node,
+ NodeMap & members, GObjectHolder<DConfClient> const & client,
+ OString const & dir)
+{
+ StringArrayHolder a(dconf_client_list(client.get(), dir.getStr(), nullptr));
+ for (char const * const * p = a.get(); *p != nullptr; ++p) {
+ std::size_t n = std::strlen(*p);
+ if (n > o3tl::make_unsigned(
+ std::numeric_limits<sal_Int32>::max()))
+ {
+ SAL_WARN("configmgr.dconf", "too long dir/key in dir " << dir);
+ continue;
+ }
+ OString s(*p, static_cast<sal_Int32>(n));
+ OString path(dir + s);
+ OUString name;
+ if (!rtl_convertStringToUString(
+ &name.pData, s.getStr(), s.getLength(), RTL_TEXTENCODING_UTF8,
+ (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR
+ | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR
+ | RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)))
+ {
+ SAL_WARN("configmgr.dconf", "non--UTF-8 dir/key in dir " << dir);
+ continue;
+ }
+ bool isDir = name.endsWith("/", &name);
+ OUString templ;
+ bool remove;
+ bool replace;
+ if (node.is() && node->kind() == Node::KIND_SET) {
+ if (!isDir) {
+ SAL_WARN(
+ "configmgr.dconf",
+ "bad key " << path << " does not match set element");
+ continue;
+ }
+ if (!decode(&name, true)) {
+ continue;
+ }
+ enum class Op { None, Fuse, Replace, Remove };
+ Op op = Op::None;
+ bool content = false;
+ bool bad = false;
+ StringArrayHolder a2(
+ dconf_client_list(client.get(), path.getStr(), nullptr));
+ for (char const * const * p2 = a2.get(); *p2 != nullptr; ++p2) {
+ if (std::strcmp(*p2, "op") == 0) {
+ OString path2(path + "op");
+ GVariantHolder v(
+ dconf_client_read(client.get(), path2.getStr()));
+ if (v.get() == nullptr) {
+ SAL_WARN(
+ "configmgr.dconf", "cannot read key " << path2);
+ bad = true;
+ break;
+ }
+ OUString ops;
+ if (!getStringValue(path2, v, &ops)) {
+ bad = true;
+ break;
+ }
+ if (ops == "fuse") {
+ op = Op::Fuse;
+ } else if (ops == "replace") {
+ op = Op::Replace;
+ } else if (ops == "remove") {
+ op = Op::Remove;
+ } else {
+ SAL_WARN(
+ "configmgr.dconf",
+ "bad key " << path2 << " value " << ops);
+ bad = true;
+ break;
+ }
+ } else if (std::strcmp(*p2, "template") == 0) {
+ OString path2(path + "template");
+ GVariantHolder v(
+ dconf_client_read(client.get(), path2.getStr()));
+ if (v.get() == nullptr) {
+ SAL_WARN(
+ "configmgr.dconf", "cannot read key " << path2);
+ bad = true;
+ break;
+ }
+ if (!getStringValue(path2, v, &templ)) {
+ bad = true;
+ break;
+ }
+ if (!static_cast<SetNode *>(node.get())->
+ isValidTemplate(templ))
+ {
+ SAL_WARN(
+ "configmgr.dconf",
+ "bad key " << path2 << " value " << templ
+ << " denotes unsupported set element template");
+ bad = true;
+ break;
+ }
+ } else if (std::strcmp(*p2, "content/") == 0) {
+ content = true;
+ } else {
+ SAL_WARN(
+ "configmgr.dconf",
+ "bad dir/key " << p2
+ << " in set element indirection dir " << path);
+ bad = true;
+ break;
+ }
+ }
+ if (bad) {
+ continue;
+ }
+ switch (op) {
+ default: // case Op::None:
+ SAL_WARN(
+ "configmgr.dconf",
+ "bad set element indirection dir " << path
+ << " missing \"op\" key");
+ continue;
+ case Op::Fuse:
+ case Op::Replace:
+ if (templ.isEmpty() || !content) {
+ SAL_WARN(
+ "configmgr.dconf",
+ "missing \"content\" and/or \"template\" dir/key in "
+ "\"op\" = \"fuse\"/\"remove\" set element"
+ " indirection dir " << path);
+ continue;
+ }
+ path += "content/";
+ remove = false;
+ replace = op == Op::Replace;
+ break;
+ case Op::Remove:
+ if (!templ.isEmpty() || content) {
+ SAL_WARN(
+ "configmgr.dconf",
+ "bad \"content\" and/or \"template\" dir/key in \"op\" "
+ "= \"remove\" set element indirection dir "
+ << path);
+ continue;
+ }
+ remove = true;
+ replace = false;
+ break;
+ }
+ } else {
+ remove = false;
+ replace = false;
+ }
+ rtl::Reference<Node> member(members.findNode(layer, name));
+ bool insert = !member.is();
+ if (!remove) {
+ if (replace || insert) {
+ if (!node.is()) {
+ SAL_WARN("configmgr.dconf", "bad unmatched " << path);
+ continue;
+ }
+ switch (node->kind()) {
+ case Node::KIND_LOCALIZED_PROPERTY:
+ member.set(new LocalizedValueNode(layer));
+ break;
+ case Node::KIND_GROUP:
+ if (!static_cast<GroupNode *>(node.get())->isExtensible()) {
+ SAL_WARN("configmgr.dconf", "bad unmatched " << path);
+ continue;
+ }
+ member.set(
+ new PropertyNode(
+ layer, TYPE_ANY, true, css::uno::Any(), true));
+ break;
+ case Node::KIND_SET:
+ assert(!templ.isEmpty());
+ member = data.getTemplate(layer, templ);
+ if (!member.is()) {
+ SAL_WARN(
+ "configmgr.dconf",
+ "bad " << path << " denoting undefined template "
+ << templ);
+ continue;
+ }
+ member = member->clone(true);
+ break;
+ default:
+ assert(false); // cannot happen
+ }
+ } else if (!templ.isEmpty() && templ != member->getTemplateName()) {
+ SAL_WARN(
+ "configmgr.dconf",
+ "bad " << path
+ << " denoting set element of non-matching template "
+ << member->getTemplateName());
+ continue;
+ }
+ }
+ if (member.is()) {
+ if (member->getFinalized() < layer) {
+ continue;
+ }
+ switch (member->kind()) {
+ case Node::KIND_PROPERTY:
+ {
+ if (isDir) {
+ SAL_WARN(
+ "configmgr.dconf",
+ "bad dir " << path << " does not match property");
+ continue;
+ }
+ rtl::Reference<PropertyNode> prop(
+ static_cast<PropertyNode *>(member.get()));
+ css::uno::Any value;
+ switch (readValue(
+ client, path, prop->getStaticType(),
+ prop->isNillable(), prop->isExtension(),
+ &value))
+ {
+ case ReadValue::Error:
+ continue;
+ case ReadValue::Value:
+ prop->setValue(layer, value);
+ finalize(client, path, member, layer);
+ break;
+ case ReadValue::Remove:
+ remove = true;
+ break;
+ }
+ break;
+ }
+ case Node::KIND_LOCALIZED_VALUE:
+ {
+ if (isDir) {
+ SAL_WARN(
+ "configmgr.dconf",
+ "bad dir " << path
+ << " does not match localized value");
+ continue;
+ }
+ assert(
+ node.is()
+ && node->kind() == Node::KIND_LOCALIZED_PROPERTY);
+ rtl::Reference<LocalizedPropertyNode> locProp(
+ static_cast<LocalizedPropertyNode *>(node.get()));
+ css::uno::Any value;
+ if (readValue(
+ client, path, locProp->getStaticType(),
+ locProp->isNillable(), false, &value)
+ == ReadValue::Error)
+ {
+ continue;
+ }
+ static_cast<LocalizedValueNode *>(member.get())->setValue(
+ layer, value);
+ finalize(client, path, member, layer);
+ break;
+ }
+ case Node::KIND_LOCALIZED_PROPERTY:
+ case Node::KIND_GROUP:
+ case Node::KIND_SET:
+ if (!isDir) {
+ SAL_WARN(
+ "configmgr.dconf",
+ "bad key " << path
+ << " does not match localized property, group, or"
+ " set, respectively");
+ continue;
+ }
+ assert(path.endsWith("/"));
+ readDir(
+ data, layer, member, member->getMembers(), client, path);
+ break;
+ default:
+ assert(false); // cannot happen
+ }
+ }
+ if (remove) {
+ if (!(member.is() && member->getMandatory())) {
+ members.erase(name);
+ }
+ } else if (replace) {
+ members.erase(name);
+ members.insert(NodeMap::value_type(name, member));
+ } else if (insert) {
+ members.insert(NodeMap::value_type(name, member));
+ }
+ }
+}
+
+OString encodeSegment(OUString const & name, bool setElement) {
+ if (!setElement) {
+ return name.toUtf8();
+ }
+ OUStringBuffer buf;
+ for (sal_Int32 i = 0; i != name.getLength(); ++i) {
+ sal_Unicode c = name[i];
+ switch (c) {
+ case '\0':
+ buf.append("\\00");
+ break;
+ case '/':
+ buf.append("\\2F");
+ break;
+ case '\\':
+ buf.append("\\5C");
+ break;
+ default:
+ buf.append(c);
+ }
+ }
+ return buf.makeStringAndClear().toUtf8();
+}
+
+OString encodeString(OUString const & value) {
+ OUStringBuffer buf;
+ for (sal_Int32 i = 0; i != value.getLength(); ++i) {
+ sal_Unicode c = value[i];
+ switch (c) {
+ case '\0':
+ buf.append("\\00");
+ break;
+ case '\\':
+ buf.append("\\5C");
+ break;
+ default:
+ buf.append(c);
+ }
+ }
+ return buf.makeStringAndClear().toUtf8();
+}
+
+bool addProperty(
+ ChangesetHolder const & changeset, OString const & pathRepresentation,
+ Type type, bool nillable, css::uno::Any const & value)
+{
+ Type dynType = getDynamicType(value);
+ assert(dynType != TYPE_ERROR);
+ if (type == TYPE_ANY) {
+ type = dynType;
+ }
+ GVariantHolder v;
+ std::forward_list<GVariantHolder> children;
+ if (dynType == TYPE_NIL) {
+ switch (type) {
+ case TYPE_BOOLEAN:
+ v.reset(g_variant_new_maybe(G_VARIANT_TYPE_BOOLEAN, nullptr));
+ break;
+ case TYPE_SHORT:
+ v.reset(g_variant_new_maybe(G_VARIANT_TYPE_INT16, nullptr));
+ break;
+ case TYPE_INT:
+ v.reset(g_variant_new_maybe(G_VARIANT_TYPE_INT32, nullptr));
+ break;
+ case TYPE_LONG:
+ v.reset(g_variant_new_maybe(G_VARIANT_TYPE_INT64, nullptr));
+ break;
+ case TYPE_DOUBLE:
+ v.reset(g_variant_new_maybe(G_VARIANT_TYPE_DOUBLE, nullptr));
+ break;
+ case TYPE_STRING:
+ v.reset(g_variant_new_maybe(G_VARIANT_TYPE_STRING, nullptr));
+ break;
+ case TYPE_HEXBINARY:
+ case TYPE_BOOLEAN_LIST:
+ case TYPE_SHORT_LIST:
+ case TYPE_INT_LIST:
+ case TYPE_LONG_LIST:
+ case TYPE_DOUBLE_LIST:
+ case TYPE_STRING_LIST:
+ case TYPE_HEXBINARY_LIST:
+ {
+ static char const * const typeString[
+ TYPE_HEXBINARY_LIST - TYPE_HEXBINARY + 1]
+ = { "ay", "ab", "an", "ai", "ax", "ad", "as", "aay" };
+ GVariantTypeHolder ty(
+ g_variant_type_new(typeString[type - TYPE_HEXBINARY]));
+ if (ty.get() == nullptr) {
+ SAL_WARN("configmgr.dconf", "g_variant_type_new failed");
+ return false;
+ }
+ v.reset(g_variant_new_maybe(ty.get(), nullptr));
+ break;
+ }
+ default:
+ assert(false); // this cannot happen
+ break;
+ }
+ if (v.get() == nullptr) {
+ SAL_WARN("configmgr.dconf", "g_variant_new_maybe failed");
+ return false;
+ }
+ } else {
+ switch (type) {
+ case TYPE_BOOLEAN:
+ v.reset(g_variant_new_boolean(value.get<bool>()));
+ break;
+ case TYPE_SHORT:
+ v.reset(g_variant_new_int16(value.get<sal_Int16>()));
+ break;
+ case TYPE_INT:
+ v.reset(g_variant_new_int32(value.get<sal_Int32>()));
+ break;
+ case TYPE_LONG:
+ v.reset(g_variant_new_int64(value.get<sal_Int64>()));
+ break;
+ case TYPE_DOUBLE:
+ v.reset(g_variant_new_double(value.get<double>()));
+ break;
+ case TYPE_STRING:
+ v.reset(
+ g_variant_new_string(
+ encodeString(value.get<OUString>()).getStr()));
+ break;
+ case TYPE_HEXBINARY:
+ {
+ css::uno::Sequence<sal_Int8> seq(
+ value.get<css::uno::Sequence<sal_Int8>>());
+ static_assert(
+ sizeof(sal_Int32) <= sizeof(gsize),
+ "G_MAXSIZE too small");
+ static_assert(
+ sizeof (sal_Int8) == sizeof (guchar), "size mismatch");
+ v.reset(
+ g_variant_new_fixed_array(
+ G_VARIANT_TYPE_BYTE, seq.getConstArray(),
+ seq.getLength(), sizeof (sal_Int8)));
+ break;
+ }
+ case TYPE_BOOLEAN_LIST:
+ {
+ css::uno::Sequence<sal_Bool> seq(
+ value.get<css::uno::Sequence<sal_Bool>>());
+ static_assert(
+ sizeof(sal_Int32) <= sizeof(gsize),
+ "G_MAXSIZE too small");
+ static_assert(sizeof (sal_Bool) == 1, "size mismatch");
+ v.reset(
+ g_variant_new_fixed_array(
+ G_VARIANT_TYPE_BOOLEAN, seq.getConstArray(),
+ seq.getLength(), sizeof (sal_Bool)));
+ break;
+ }
+ case TYPE_SHORT_LIST:
+ {
+ css::uno::Sequence<sal_Int16> seq(
+ value.get<css::uno::Sequence<sal_Int16>>());
+ static_assert(
+ sizeof(sal_Int32) <= sizeof(gsize),
+ "G_MAXSIZE too small");
+ static_assert(
+ sizeof (sal_Int16) == sizeof (gint16), "size mismatch");
+ v.reset(
+ g_variant_new_fixed_array(
+ G_VARIANT_TYPE_INT16, seq.getConstArray(),
+ seq.getLength(), sizeof (sal_Int16)));
+ //TODO: endian-ness?
+ break;
+ }
+ case TYPE_INT_LIST:
+ {
+ css::uno::Sequence<sal_Int32> seq(
+ value.get<css::uno::Sequence<sal_Int32>>());
+ static_assert(
+ sizeof(sal_Int32) <= sizeof(gsize),
+ "G_MAXSIZE too small");
+ static_assert(
+ sizeof (sal_Int32) == sizeof (gint32), "size mismatch");
+ v.reset(
+ g_variant_new_fixed_array(
+ G_VARIANT_TYPE_INT32, seq.getConstArray(),
+ seq.getLength(), sizeof (sal_Int32)));
+ //TODO: endian-ness?
+ break;
+ }
+ case TYPE_LONG_LIST:
+ {
+ css::uno::Sequence<sal_Int64> seq(
+ value.get<css::uno::Sequence<sal_Int64>>());
+ static_assert(
+ sizeof(sal_Int32) <= sizeof(gsize),
+ "G_MAXSIZE too small");
+ static_assert(
+ sizeof (sal_Int64) == sizeof (gint64), "size mismatch");
+ v.reset(
+ g_variant_new_fixed_array(
+ G_VARIANT_TYPE_INT64, seq.getConstArray(),
+ seq.getLength(), sizeof (sal_Int64)));
+ //TODO: endian-ness?
+ break;
+ }
+ case TYPE_DOUBLE_LIST:
+ {
+ css::uno::Sequence<double> seq(
+ value.get<css::uno::Sequence<double>>());
+ static_assert(
+ sizeof(sal_Int32) <= sizeof(gsize),
+ "G_MAXSIZE too small");
+ static_assert(
+ sizeof (double) == sizeof (gdouble), "size mismatch");
+ v.reset(
+ g_variant_new_fixed_array(
+ G_VARIANT_TYPE_DOUBLE, seq.getConstArray(),
+ seq.getLength(), sizeof (double)));
+ //TODO: endian-ness?
+ break;
+ }
+ case TYPE_STRING_LIST:
+ {
+ const css::uno::Sequence<OUString> seq(
+ value.get<css::uno::Sequence<OUString>>());
+ std::vector<GVariant *> vs;
+ for (OUString const & s : seq) {
+ children.emplace_front(
+ g_variant_new_string(encodeString(s).getStr()));
+ if (children.front().get() == nullptr) {
+ SAL_WARN(
+ "configmgr.dconf", "g_variant_new_string failed");
+ return false;
+ }
+ vs.push_back(children.front().get());
+ }
+ static_assert(
+ sizeof(sal_Int32) <= sizeof(gsize),
+ "G_MAXSIZE too small");
+ v.reset(
+ g_variant_new_array(
+ G_VARIANT_TYPE_STRING, vs.data(), seq.getLength()));
+ break;
+ }
+ case TYPE_HEXBINARY_LIST:
+ {
+ const css::uno::Sequence<css::uno::Sequence<sal_Int8>> seqSeq(
+ value.get<
+ css::uno::Sequence<css::uno::Sequence<sal_Int8>>>());
+ std::vector<GVariant *> vs;
+ for (css::uno::Sequence<sal_Int8> const & seq : seqSeq) {
+ static_assert(
+ sizeof(sal_Int32) <= sizeof(gsize),
+ "G_MAXSIZE too small");
+ static_assert(
+ sizeof (sal_Int8) == sizeof (guchar), "size mismatch");
+ children.emplace_front(
+ g_variant_new_fixed_array(
+ G_VARIANT_TYPE_BYTE, seq.getConstArray(),
+ seq.getLength(), sizeof (sal_Int8)));
+ if (children.front().get() == nullptr) {
+ SAL_WARN(
+ "configmgr.dconf",
+ "g_variant_new_fixed_array failed");
+ return false;
+ }
+ vs.push_back(children.front().get());
+ }
+ GVariantTypeHolder ty(g_variant_type_new("aay"));
+ if (ty.get() == nullptr) {
+ SAL_WARN("configmgr.dconf", "g_variant_type_new failed");
+ return false;
+ }
+ static_assert(
+ sizeof(sal_Int32) <= sizeof(gsize),
+ "G_MAXSIZE too small");
+ v.reset(
+ g_variant_new_array(ty.get(), vs.data(), seqSeq.getLength()));
+ break;
+ }
+ default:
+ assert(false); // this cannot happen
+ break;
+ }
+ if (v.get() == nullptr) {
+ SAL_WARN("configmgr.dconf", "GVariant creation failed");
+ return false;
+ }
+ if (nillable) {
+ GVariantHolder v1(g_variant_new_maybe(nullptr, v.get()));
+ if (v1.get() == nullptr) {
+ SAL_WARN("configmgr.dconf", "g_variant_new_maybe failed");
+ return false;
+ }
+ v.release();
+ v.reset(v1.get());
+ v1.release();
+ }
+ }
+ dconf_changeset_set(
+ changeset.get(), pathRepresentation.getStr(), v.get());
+ for (auto & i: children) {
+ i.release();
+ }
+ v.release();
+ return true;
+}
+
+bool addNode(
+ Components & components, ChangesetHolder const & changeset,
+ rtl::Reference<Node> const & parent, OString const & pathRepresentation,
+ rtl::Reference<Node> const & node)
+{
+ switch (node->kind()) {
+ case Node::KIND_PROPERTY:
+ {
+ PropertyNode * prop = static_cast<PropertyNode *>(node.get());
+ if (!addProperty(
+ changeset, pathRepresentation, prop->getStaticType(),
+ prop->isNillable(), prop->getValue(components)))
+ {
+ return false;
+ }
+ break;
+ }
+ case Node::KIND_LOCALIZED_VALUE:
+ {
+ //TODO: name.isEmpty()?
+ LocalizedPropertyNode * locprop
+ = static_cast<LocalizedPropertyNode *>(parent.get());
+ if (!addProperty(
+ changeset, pathRepresentation,
+ locprop->getStaticType(), locprop->isNillable(),
+ static_cast<LocalizedValueNode *>(node.get())->getValue()))
+ {
+ return false;
+ }
+ break;
+ }
+ case Node::KIND_LOCALIZED_PROPERTY:
+ case Node::KIND_GROUP:
+ case Node::KIND_SET:
+ for (auto const & i: node->getMembers()) {
+ OUString templ(i.second->getTemplateName());
+ OString path(
+ pathRepresentation + "/"
+ + encodeSegment(i.first, !templ.isEmpty()));
+ if (!templ.isEmpty()) {
+ path += "/";
+ GVariantHolder v(g_variant_new_string("replace"));
+ if (v.get() == nullptr) {
+ SAL_WARN("configmgr.dconf", "g_variant_new_string failed");
+ return false;
+ }
+ dconf_changeset_set(
+ changeset.get(), OString(path + "op").getStr(), v.get());
+ v.release();
+ v.reset(g_variant_new_string(encodeString(templ).getStr()));
+ if (v.get() == nullptr) {
+ SAL_WARN("configmgr.dconf", "g_variant_new_string failed");
+ return false;
+ }
+ dconf_changeset_set(
+ changeset.get(), OString(path + "template").getStr(),
+ v.get());
+ v.release();
+ path += "content";
+ }
+ if (!addNode(components, changeset, parent, path, i.second)) {
+ return false;
+ }
+ }
+ break;
+ case Node::KIND_ROOT:
+ assert(false); // this cannot happen
+ break;
+ }
+ return true;
+}
+
+bool addModifications(
+ Components & components, ChangesetHolder const & changeset,
+ OString const & parentPathRepresentation,
+ rtl::Reference<Node> const & parent, OUString const & nodeName,
+ rtl::Reference<Node> const & node,
+ Modifications::Node const & modifications)
+{
+ // It is never necessary to write oor:finalized or oor:mandatory attributes,
+ // as they cannot be set via the UNO API.
+ if (modifications.children.empty()) {
+ assert(parent.is());
+ // components themselves have no parent but must have children
+ if (node.is()) {
+ OUString templ(node->getTemplateName());
+ OString path(
+ parentPathRepresentation + "/"
+ + encodeSegment(nodeName, !templ.isEmpty()));
+ if (!templ.isEmpty()) {
+ path += "/";
+ GVariantHolder v(g_variant_new_string("replace"));
+ if (v.get() == nullptr) {
+ SAL_WARN("configmgr.dconf", "g_variant_new_string failed");
+ return false;
+ }
+ dconf_changeset_set(
+ changeset.get(), OString(path + "op").getStr(), v.get());
+ v.release();
+ v.reset(g_variant_new_string(encodeString(templ).getStr()));
+ if (v.get() == nullptr) {
+ SAL_WARN("configmgr.dconf", "g_variant_new_string failed");
+ return false;
+ }
+ dconf_changeset_set(
+ changeset.get(), OString(path + "template").getStr(),
+ v.get());
+ v.release();
+ path += "content";
+ }
+ if (!addNode(components, changeset, parent, path, node)) {
+ return false;
+ }
+ } else {
+ switch (parent->kind()) {
+ case Node::KIND_LOCALIZED_PROPERTY:
+ case Node::KIND_GROUP:
+ {
+ GVariantHolder v(g_variant_new_tuple(nullptr, 0));
+ if (v.get() == nullptr) {
+ SAL_WARN(
+ "configmgr.dconf", "g_variant_new_tuple failed");
+ return false;
+ }
+ OString path(parentPathRepresentation);
+ if (!nodeName.isEmpty()) { // KIND_LOCALIZED_PROPERTY
+ path += "/" + encodeSegment(nodeName, false);
+ }
+ dconf_changeset_set(
+ changeset.get(), path.getStr(), v.get());
+ v.release();
+ break;
+ }
+ case Node::KIND_SET:
+ {
+ OString path(
+ parentPathRepresentation + "/"
+ + encodeSegment(nodeName, true) + "/");
+ GVariantHolder v(g_variant_new_string("remove"));
+ if (v.get() == nullptr) {
+ SAL_WARN(
+ "configmgr.dconf", "g_variant_new_string failed");
+ return false;
+ }
+ dconf_changeset_set(
+ changeset.get(), OString(path + "op").getStr(),
+ v.get());
+ v.release();
+ dconf_changeset_set(
+ changeset.get(), OString(path + "template").getStr(),
+ nullptr);
+ dconf_changeset_set(
+ changeset.get(), OString(path + "content/").getStr(),
+ nullptr);
+ break;
+ }
+ default:
+ assert(false); // this cannot happen
+ break;
+ }
+ }
+ } else {
+ assert(node.is());
+ OUString templ(node->getTemplateName());
+ OString path(
+ parentPathRepresentation + "/"
+ + encodeSegment(nodeName, !templ.isEmpty()));
+ if (!templ.isEmpty()) {
+ path += "/";
+ GVariantHolder v(g_variant_new_string("fuse"));
+ if (v.get() == nullptr) {
+ SAL_WARN("configmgr.dconf", "g_variant_new_string failed");
+ return false;
+ }
+ dconf_changeset_set(
+ changeset.get(), OString(path + "op").getStr(), v.get());
+ v.release();
+ v.reset(g_variant_new_string(encodeString(templ).getStr()));
+ if (v.get() == nullptr) {
+ SAL_WARN("configmgr.dconf", "g_variant_new_string failed");
+ return false;
+ }
+ dconf_changeset_set(
+ changeset.get(), OString(path + "template").getStr(), v.get());
+ v.release();
+ path += "content";
+ }
+ for (auto const & i: modifications.children) {
+ if (!addModifications(
+ components, changeset, path, node, i.first,
+ node->getMember(i.first), i.second))
+ {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+}
+
+void readLayer(Data & data, int layer) {
+ GObjectHolder<DConfClient> client(dconf_client_new());
+ if (client.get() == nullptr) {
+ SAL_WARN("configmgr.dconf", "dconf_client_new failed");
+ return;
+ }
+ readDir(
+ data, layer, rtl::Reference<Node>(), data.getComponents(), client,
+ getRoot() + "/");
+}
+
+void writeModifications(Components & components, Data & data) {
+ GObjectHolder<DConfClient> client(dconf_client_new());
+ if (client.get() == nullptr) {
+ SAL_WARN("configmgr.dconf", "dconf_client_new failed");
+ }
+ ChangesetHolder cs(dconf_changeset_new());
+ if (cs.get() == nullptr) {
+ SAL_WARN("configmgr.dconf", "dconf_changeset_new failed");
+ return;
+ }
+ for (auto const & i: data.modifications.getRoot().children) {
+ if (!addModifications(
+ components, cs, getRoot(), rtl::Reference<Node>(), i.first,
+ data.getComponents().findNode(Data::NO_LAYER, i.first),
+ i.second))
+ {
+ return;
+ }
+ }
+ if (!dconf_client_change_sync(
+ client.get(), cs.get(), nullptr, nullptr, nullptr))
+ {
+ //TODO: GError
+ SAL_WARN("configmgr.dconf", "dconf_client_change_sync failed");
+ return;
+ }
+ data.modifications.clear();
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/dconf.hxx b/configmgr/source/dconf.hxx
new file mode 100644
index 000000000..9fa51247c
--- /dev/null
+++ b/configmgr/source/dconf.hxx
@@ -0,0 +1,27 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+namespace configmgr {
+ class Components;
+ struct Data;
+}
+
+namespace configmgr::dconf {
+
+void readLayer(Data & data, int layer);
+
+void writeModifications(Components & components, Data & data);
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/defaultprovider.cxx b/configmgr/source/defaultprovider.cxx
new file mode 100644
index 000000000..7161659e0
--- /dev/null
+++ b/configmgr/source/defaultprovider.cxx
@@ -0,0 +1,53 @@
+/* -*- 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 <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/uno/XInterface.hpp>
+#include <osl/mutex.hxx>
+#include <rtl/ustring.hxx>
+
+#include "configurationprovider.hxx"
+#include "defaultprovider.hxx"
+#include "lock.hxx"
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_configuration_DefaultProvider_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
+{
+ osl::MutexGuard guard(*configmgr::lock());
+ css::uno::Reference<css::uno::XInterface> singleton(
+ configmgr::configuration_provider::createDefault(context));
+ singleton->acquire();
+ return singleton.get();
+}
+
+namespace configmgr::default_provider
+{
+OUString getImplementationName() { return "com.sun.star.comp.configuration.DefaultProvider"; }
+
+css::uno::Sequence<OUString> getSupportedServiceNames()
+{
+ return { "com.sun.star.configuration.DefaultProvider" };
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/defaultprovider.hxx b/configmgr/source/defaultprovider.hxx
new file mode 100644
index 000000000..d20ba200b
--- /dev/null
+++ b/configmgr/source/defaultprovider.hxx
@@ -0,0 +1,33 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <com/sun/star/uno/Sequence.hxx>
+
+namespace configmgr::default_provider
+{
+OUString getImplementationName();
+
+css::uno::Sequence<OUString> getSupportedServiceNames();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/groupnode.cxx b/configmgr/source/groupnode.cxx
new file mode 100644
index 000000000..00934e917
--- /dev/null
+++ b/configmgr/source/groupnode.cxx
@@ -0,0 +1,77 @@
+/* -*- 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 <rtl/ref.hxx>
+#include <rtl/ustring.hxx>
+#include <utility>
+
+#include "data.hxx"
+#include "groupnode.hxx"
+#include "node.hxx"
+#include "nodemap.hxx"
+
+namespace configmgr {
+
+GroupNode::GroupNode(
+ int layer, bool extensible, OUString templateName):
+ Node(layer), extensible_(extensible), templateName_(std::move(templateName)),
+ mandatory_(Data::NO_LAYER)
+{}
+
+rtl::Reference< Node > GroupNode::clone(bool keepTemplateName) const {
+ return new GroupNode(*this, keepTemplateName);
+}
+
+NodeMap & GroupNode::getMembers() {
+ return members_;
+}
+
+OUString GroupNode::getTemplateName() const {
+ return templateName_;
+}
+
+void GroupNode::setMandatory(int layer) {
+ mandatory_ = layer;
+}
+
+int GroupNode::getMandatory() const {
+ return mandatory_;
+}
+
+
+GroupNode::GroupNode(GroupNode const & other, bool keepTemplateName):
+ Node(other), extensible_(other.extensible_), mandatory_(other.mandatory_)
+{
+ other.members_.cloneInto(&members_);
+ if (keepTemplateName) {
+ templateName_ = other.templateName_;
+ }
+}
+
+GroupNode::~GroupNode() {}
+
+Node::Kind GroupNode::kind() const {
+ return KIND_GROUP;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/groupnode.hxx b/configmgr/source/groupnode.hxx
new file mode 100644
index 000000000..961ecc090
--- /dev/null
+++ b/configmgr/source/groupnode.hxx
@@ -0,0 +1,63 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <rtl/ref.hxx>
+#include <rtl/ustring.hxx>
+
+#include "node.hxx"
+#include "nodemap.hxx"
+
+namespace configmgr
+{
+class GroupNode : public Node
+{
+public:
+ GroupNode(int layer, bool extensible, OUString templateName);
+
+ virtual rtl::Reference<Node> clone(bool keepTemplateName) const override;
+
+ virtual NodeMap& getMembers() override;
+
+ virtual OUString getTemplateName() const override;
+
+ virtual void setMandatory(int layer) override;
+
+ virtual int getMandatory() const override;
+
+ bool isExtensible() const { return extensible_; }
+
+private:
+ GroupNode(GroupNode const& other, bool keepTemplateName);
+
+ virtual ~GroupNode() override;
+
+ virtual Kind kind() const override;
+
+ bool extensible_;
+ NodeMap members_;
+ OUString templateName_; // non-empty if this node is a template, free node, or set member
+ int mandatory_;
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/localizedpropertynode.cxx b/configmgr/source/localizedpropertynode.cxx
new file mode 100644
index 000000000..6e47264f2
--- /dev/null
+++ b/configmgr/source/localizedpropertynode.cxx
@@ -0,0 +1,60 @@
+/* -*- 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 <rtl/ref.hxx>
+
+#include "localizedpropertynode.hxx"
+#include "node.hxx"
+#include "nodemap.hxx"
+#include "type.hxx"
+
+namespace configmgr {
+
+LocalizedPropertyNode::LocalizedPropertyNode(
+ int layer, Type staticType, bool nillable):
+ Node(layer), staticType_(staticType), nillable_(nillable)
+{}
+
+rtl::Reference< Node > LocalizedPropertyNode::clone(bool) const {
+ return new LocalizedPropertyNode(*this);
+}
+
+NodeMap & LocalizedPropertyNode::getMembers() {
+ return members_;
+}
+
+
+LocalizedPropertyNode::LocalizedPropertyNode(
+ LocalizedPropertyNode const & other):
+ Node(other), staticType_(other.staticType_), nillable_(other.nillable_)
+{
+ other.members_.cloneInto(&members_);
+}
+
+LocalizedPropertyNode::~LocalizedPropertyNode() {}
+
+Node::Kind LocalizedPropertyNode::kind() const {
+ return KIND_LOCALIZED_PROPERTY;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/localizedpropertynode.hxx b/configmgr/source/localizedpropertynode.hxx
new file mode 100644
index 000000000..d4831a89f
--- /dev/null
+++ b/configmgr/source/localizedpropertynode.hxx
@@ -0,0 +1,59 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <rtl/ref.hxx>
+
+#include "node.hxx"
+#include "nodemap.hxx"
+#include "type.hxx"
+
+namespace configmgr
+{
+class LocalizedPropertyNode : public Node
+{
+public:
+ LocalizedPropertyNode(int layer, Type staticType, bool nillable);
+
+ virtual rtl::Reference<Node> clone(bool keepTemplateName) const override;
+
+ virtual NodeMap& getMembers() override;
+
+ Type getStaticType() const { return staticType_; }
+
+ bool isNillable() const { return nillable_; }
+
+private:
+ LocalizedPropertyNode(LocalizedPropertyNode const& other);
+
+ virtual ~LocalizedPropertyNode() override;
+
+ virtual Kind kind() const override;
+
+ Type staticType_; // as specified in the component-schema (TYPE_ANY, ..., TYPE_HEXBINARY_LIST;
+ // not TYPE_ERROR or TYPE_NIL)
+ bool nillable_;
+ NodeMap members_;
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/localizedvaluenode.cxx b/configmgr/source/localizedvaluenode.cxx
new file mode 100644
index 000000000..816975063
--- /dev/null
+++ b/configmgr/source/localizedvaluenode.cxx
@@ -0,0 +1,64 @@
+/* -*- 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 <com/sun/star/uno/Any.hxx>
+#include <rtl/ref.hxx>
+#include <rtl/ustring.hxx>
+#include <utility>
+
+#include "localizedvaluenode.hxx"
+#include "node.hxx"
+
+namespace configmgr {
+
+LocalizedValueNode::LocalizedValueNode(int layer, css::uno::Any value):
+ Node(layer), value_(std::move(value))
+{}
+
+LocalizedValueNode::LocalizedValueNode(int layer):
+ Node(layer)
+{}
+
+rtl::Reference< Node > LocalizedValueNode::clone(bool) const {
+ return new LocalizedValueNode(*this);
+}
+
+OUString LocalizedValueNode::getTemplateName() const {
+ return "*";
+}
+
+
+void LocalizedValueNode::setValue(int layer, css::uno::Any const & value)
+{
+ setLayer(layer);
+ if (&value != &value_)
+ value_ = value;
+}
+
+LocalizedValueNode::~LocalizedValueNode() {}
+
+Node::Kind LocalizedValueNode::kind() const {
+ return KIND_LOCALIZED_VALUE;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/localizedvaluenode.hxx b/configmgr/source/localizedvaluenode.hxx
new file mode 100644
index 000000000..07949ac5a
--- /dev/null
+++ b/configmgr/source/localizedvaluenode.hxx
@@ -0,0 +1,61 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <com/sun/star/uno/Any.hxx>
+#include <rtl/ref.hxx>
+
+#include "node.hxx"
+
+namespace configmgr
+{
+class LocalizedValueNode : public Node
+{
+public:
+ explicit LocalizedValueNode(int layer);
+ LocalizedValueNode(int layer, css::uno::Any value);
+
+ virtual rtl::Reference<Node> clone(bool keepTemplateName) const override;
+
+ virtual OUString getTemplateName() const override;
+
+ const css::uno::Any& getValue() const { return value_; }
+ css::uno::Any* getValuePtr(int layer)
+ {
+ setLayer(layer);
+ return &value_;
+ }
+
+ void setValue(int layer, css::uno::Any const& value);
+
+private:
+ LocalizedValueNode(LocalizedValueNode const&) = default;
+
+ virtual ~LocalizedValueNode() override;
+
+ virtual Kind kind() const override;
+
+ css::uno::Any value_;
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/lock.cxx b/configmgr/source/lock.cxx
new file mode 100644
index 000000000..65b490b78
--- /dev/null
+++ b/configmgr/source/lock.cxx
@@ -0,0 +1,38 @@
+/* -*- 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 <osl/mutex.hxx>
+
+#include "lock.hxx"
+
+namespace configmgr
+{
+std::shared_ptr<osl::Mutex> const& lock()
+{
+ // fdo#31494# get ownership right
+ // Ensure that the mutex lives as long as all its consumers, otherwise
+ // the configmgr DLL exit delete this before unotools releases some configmgr derived reference.
+ static std::shared_ptr<osl::Mutex> theLock = std::make_shared<osl::Mutex>();
+ return theLock;
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/lock.hxx b/configmgr/source/lock.hxx
new file mode 100644
index 000000000..195dbf5e7
--- /dev/null
+++ b/configmgr/source/lock.hxx
@@ -0,0 +1,31 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+#include <osl/mutex.hxx>
+#include <memory>
+
+namespace configmgr
+{
+std::shared_ptr<osl::Mutex> const& lock();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/modifications.cxx b/configmgr/source/modifications.cxx
new file mode 100644
index 000000000..d84904c3f
--- /dev/null
+++ b/configmgr/source/modifications.cxx
@@ -0,0 +1,77 @@
+/* -*- 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 <rtl/ustring.hxx>
+
+#include "modifications.hxx"
+
+namespace configmgr {
+
+Modifications::Modifications() {}
+
+Modifications::~Modifications() {}
+
+void Modifications::add(std::vector<OUString> const & path) {
+ Node * p = &root_;
+ bool wasPresent = false;
+ for (auto const& pathItem : path) {
+ Node::Children::iterator j(p->children.find(pathItem));
+ if (j == p->children.end()) {
+ if (wasPresent && p->children.empty()) {
+ return;
+ }
+ j = p->children.emplace(pathItem, Node()).first;
+ wasPresent = false;
+ } else {
+ wasPresent = true;
+ }
+ p = &j->second;
+ }
+ p->children.clear();
+}
+
+void Modifications::remove(std::vector<OUString> const & path) {
+ assert(!path.empty());
+ Node * p = &root_;
+ for (auto i(path.begin());;) {
+ Node::Children::iterator j(p->children.find(*i));
+ if (j == p->children.end()) {
+ break;
+ }
+ if (++i == path.end()) {
+ p->children.erase(j);
+ if (p->children.empty()) {
+ std::vector<OUString> parent(path);
+ parent.pop_back();
+ remove(parent);
+ }
+ break;
+ }
+ p = &j->second;
+ }
+}
+
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/modifications.hxx b/configmgr/source/modifications.hxx
new file mode 100644
index 000000000..22e608ac7
--- /dev/null
+++ b/configmgr/source/modifications.hxx
@@ -0,0 +1,67 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <vector>
+
+#include <rtl/ustring.hxx>
+
+#include <boost/unordered_map.hpp>
+#include <config_dconf.h>
+
+namespace configmgr
+{
+class Modifications
+{
+public:
+ struct Node
+ {
+ typedef boost::unordered_map<OUString, Node, OUStringHash> Children;
+
+ Children children;
+ };
+
+ Modifications();
+
+ ~Modifications();
+
+ void add(std::vector<OUString> const& path);
+
+ void remove(std::vector<OUString> const& path);
+
+#if ENABLE_DCONF
+ void clear() { root_.children.clear(); }
+#endif
+
+ bool empty() const { return root_.children.empty(); }
+
+ Node const& getRoot() const { return root_; }
+
+private:
+ Modifications(const Modifications&) = delete;
+ Modifications& operator=(const Modifications&) = delete;
+
+ Node root_;
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/node.cxx b/configmgr/source/node.cxx
new file mode 100644
index 000000000..8f00d3887
--- /dev/null
+++ b/configmgr/source/node.cxx
@@ -0,0 +1,79 @@
+/* -*- 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 <com/sun/star/uno/RuntimeException.hpp>
+#include <rtl/ref.hxx>
+#include <rtl/ustring.hxx>
+
+#include "data.hxx"
+#include "node.hxx"
+#include "nodemap.hxx"
+
+namespace configmgr {
+
+NodeMap & Node::getMembers() {
+ assert(false);
+ throw css::uno::RuntimeException("this cannot happen");
+}
+
+OUString Node::getTemplateName() const {
+ return OUString();
+}
+
+void Node::setMandatory(int layer) {
+ (void) layer; // avoid warnings
+ assert(layer == Data::NO_LAYER);
+}
+
+int Node::getMandatory() const {
+ return Data::NO_LAYER;
+}
+
+void Node::setLayer(int layer) {
+ assert(layer >= layer_);
+ layer_ = layer;
+}
+
+
+void Node::setFinalized(int layer) {
+ finalized_ = layer;
+}
+
+
+rtl::Reference< Node > Node::getMember(OUString const & name) {
+ NodeMap const & members = getMembers();
+ NodeMap::const_iterator i(members.find(name));
+ return i == members.end() ? rtl::Reference< Node >() : i->second;
+}
+
+Node::Node(int layer): layer_(layer), finalized_(Data::NO_LAYER) {}
+
+Node::Node(const Node & other):
+ SimpleReferenceObject(), layer_(other.layer_), finalized_(other.finalized_)
+{}
+
+Node::~Node() {}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/node.hxx b/configmgr/source/node.hxx
new file mode 100644
index 000000000..b858c9e42
--- /dev/null
+++ b/configmgr/source/node.hxx
@@ -0,0 +1,68 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <rtl/ref.hxx>
+#include <rtl/ustring.hxx>
+#include <salhelper/simplereferenceobject.hxx>
+
+namespace configmgr {
+
+class NodeMap;
+
+class Node: public salhelper::SimpleReferenceObject {
+public:
+ enum Kind {
+ KIND_PROPERTY, KIND_LOCALIZED_PROPERTY, KIND_LOCALIZED_VALUE,
+ KIND_GROUP, KIND_SET, KIND_ROOT };
+
+ virtual Kind kind() const = 0;
+
+ virtual rtl::Reference< Node > clone(bool keepTemplateName) const = 0;
+
+ virtual NodeMap & getMembers();
+ virtual OUString getTemplateName() const;
+
+ virtual void setMandatory(int layer);
+ virtual int getMandatory() const;
+
+ void setLayer(int layer);
+ int getLayer() const { return layer_;}
+
+ void setFinalized(int layer);
+ int getFinalized() const { return finalized_;}
+
+ rtl::Reference< Node > getMember(OUString const & name);
+
+protected:
+ explicit Node(int layer);
+ explicit Node(const Node & other);
+
+ virtual ~Node() override;
+private:
+ int layer_;
+ int finalized_;
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/nodemap.cxx b/configmgr/source/nodemap.cxx
new file mode 100644
index 000000000..e21578b28
--- /dev/null
+++ b/configmgr/source/nodemap.cxx
@@ -0,0 +1,53 @@
+/* -*- 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 <rtl/ustring.hxx>
+
+#include "node.hxx"
+#include "nodemap.hxx"
+
+namespace configmgr
+{
+void NodeMap::cloneInto(NodeMap* target) const
+{
+ assert(target != nullptr && target->empty());
+ NodeMapImpl clone(maImpl);
+ for (auto& elem : clone)
+ {
+ elem.second = elem.second->clone(true);
+ }
+ std::swap(clone, target->maImpl);
+ target->clearCache();
+}
+
+rtl::Reference<Node> NodeMap::findNode(int layer, OUString const& name) const
+{
+ const_iterator i;
+ if (maCache == end() || maCache->first != name)
+ maCache = const_cast<NodeMap*>(this)->maImpl.find(name);
+ i = maCache;
+ return i == end() || i->second->getLayer() > layer ? rtl::Reference<Node>() : i->second;
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/nodemap.hxx b/configmgr/source/nodemap.hxx
new file mode 100644
index 000000000..19447c7f7
--- /dev/null
+++ b/configmgr/source/nodemap.hxx
@@ -0,0 +1,68 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+#include "config_map.hxx"
+#include <rtl/ref.hxx>
+#include "node.hxx"
+
+namespace configmgr {
+
+typedef config_map< rtl::Reference< Node > > NodeMapImpl;
+class NodeMap
+{
+ NodeMapImpl maImpl;
+
+ NodeMap(const NodeMap &rMap) = delete;
+
+ public:
+ typedef NodeMapImpl::iterator iterator;
+ typedef NodeMapImpl::const_iterator const_iterator;
+ typedef NodeMapImpl::value_type value_type;
+
+ NodeMap() { clearCache(); }
+ bool empty() const { return maImpl.empty(); }
+ iterator find(const OUString &aStr) { return maImpl.find( aStr ); }
+
+ const_iterator find(const OUString &aStr) const { return maImpl.find( aStr ); }
+ iterator begin() { return maImpl.begin(); }
+ const_iterator begin() const { return maImpl.begin(); }
+
+ iterator end() { return maImpl.end(); }
+ const_iterator end() const { return maImpl.end(); }
+
+ rtl::Reference<Node> &operator[](const OUString &aStr) { clearCache(); return maImpl[aStr]; }
+ std::pair<iterator,bool> insert(const value_type &vt) { clearCache(); return maImpl.insert(vt); }
+ void erase(const iterator &it) { maImpl.erase(it); clearCache(); }
+ void erase(const OUString &aStr) { maImpl.erase(aStr); clearCache(); }
+
+ rtl::Reference< Node > findNode(int layer, OUString const & name) const;
+ void cloneInto(NodeMap * target) const;
+
+private:
+ // We get a large number of repeated identical lookups.
+ mutable const_iterator maCache;
+ void clearCache() { maCache = maImpl.end(); }
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/parsemanager.cxx b/configmgr/source/parsemanager.cxx
new file mode 100644
index 000000000..36dea373d
--- /dev/null
+++ b/configmgr/source/parsemanager.cxx
@@ -0,0 +1,91 @@
+/* -*- 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 <set>
+
+#include <sal/log.hxx>
+#include <sal/types.h>
+#include <xmlreader/span.hxx>
+#include <xmlreader/xmlreader.hxx>
+
+#include "parsemanager.hxx"
+#include "parser.hxx"
+
+namespace configmgr {
+
+ParseManager::ParseManager(
+ OUString const & url, rtl::Reference< Parser > const & parser)
+ : reader_(url), parser_(parser), itemNamespaceId_(-1)
+{
+ assert(parser.is());
+ int id;
+ id = reader_.registerNamespaceIri(
+ xmlreader::Span(
+ RTL_CONSTASCII_STRINGPARAM("http://openoffice.org/2001/registry")));
+ assert(id == NAMESPACE_OOR);
+ id = reader_.registerNamespaceIri(
+ xmlreader::Span(
+ RTL_CONSTASCII_STRINGPARAM("http://www.w3.org/2001/XMLSchema")));
+ assert(id == NAMESPACE_XS);
+ id = reader_.registerNamespaceIri(
+ xmlreader::Span(
+ RTL_CONSTASCII_STRINGPARAM(
+ "http://www.w3.org/2001/XMLSchema-instance")));
+ assert(id == NAMESPACE_XSI);
+ (void)id;
+}
+
+bool ParseManager::parse(std::set< OUString > const * existingDependencies) {
+ sal_uInt32 startTime( osl_getGlobalTimer() );
+ for (;;) {
+ switch (itemData_.is()
+ ? xmlreader::XmlReader::Result::Begin
+ : reader_.nextItem(
+ parser_->getTextMode(), &itemData_, &itemNamespaceId_))
+ {
+ case xmlreader::XmlReader::Result::Begin:
+ if (!parser_->startElement(
+ reader_, itemNamespaceId_, itemData_, existingDependencies))
+ {
+ SAL_INFO("configmgr", "parsing " << reader_.getUrl() << " took " << (osl_getGlobalTimer() - startTime) << " ms, fail");
+ return false;
+ }
+ break;
+ case xmlreader::XmlReader::Result::End:
+ parser_->endElement(reader_);
+ break;
+ case xmlreader::XmlReader::Result::Text:
+ parser_->characters(itemData_);
+ break;
+ case xmlreader::XmlReader::Result::Done:
+ SAL_INFO("configmgr", "parsing " << reader_.getUrl() << " took " << (osl_getGlobalTimer() - startTime) << " ms, success");
+ return true;
+ }
+ itemData_.clear();
+ }
+}
+
+ParseManager::~ParseManager() {}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/parsemanager.hxx b/configmgr/source/parsemanager.hxx
new file mode 100644
index 000000000..fba61ec07
--- /dev/null
+++ b/configmgr/source/parsemanager.hxx
@@ -0,0 +1,56 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <set>
+
+#include <rtl/ref.hxx>
+#include <salhelper/simplereferenceobject.hxx>
+#include <xmlreader/span.hxx>
+#include <xmlreader/xmlreader.hxx>
+
+
+namespace configmgr {
+
+class Parser;
+
+class ParseManager: public salhelper::SimpleReferenceObject {
+public:
+ ParseManager(
+ OUString const & url, rtl::Reference< Parser > const & parser);
+
+ bool parse(std::set< OUString > const * existingDependencies);
+
+ enum { NAMESPACE_OOR = 1, NAMESPACE_XS = 2, NAMESPACE_XSI = 3 };
+
+private:
+ virtual ~ParseManager() override;
+
+ xmlreader::XmlReader reader_;
+ rtl::Reference< Parser > parser_;
+ xmlreader::Span itemData_;
+ int itemNamespaceId_;
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/parser.hxx b/configmgr/source/parser.hxx
new file mode 100644
index 000000000..a3984203c
--- /dev/null
+++ b/configmgr/source/parser.hxx
@@ -0,0 +1,53 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <set>
+
+#include <salhelper/simplereferenceobject.hxx>
+#include <xmlreader/xmlreader.hxx>
+
+namespace xmlreader { struct Span; }
+
+namespace configmgr {
+
+class Parser: public salhelper::SimpleReferenceObject {
+public:
+ virtual xmlreader::XmlReader::Text getTextMode() = 0;
+
+ virtual bool startElement(
+ xmlreader::XmlReader & reader, int nsId, xmlreader::Span const & name,
+ std::set< OUString > const * existingDependencies) = 0;
+
+ virtual void endElement(xmlreader::XmlReader const & reader) = 0;
+
+ virtual void characters(xmlreader::Span const & text) = 0;
+
+protected:
+ Parser() {}
+
+ virtual ~Parser() override {}
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/partial.cxx b/configmgr/source/partial.cxx
new file mode 100644
index 000000000..f31e98549
--- /dev/null
+++ b/configmgr/source/partial.cxx
@@ -0,0 +1,134 @@
+/* -*- 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 <set>
+
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+
+#include "data.hxx"
+#include "partial.hxx"
+
+namespace configmgr {
+
+namespace {
+
+bool parseSegment(
+ OUString const & path, sal_Int32 * index, OUString * segment)
+{
+ assert(
+ index != nullptr && *index >= 0 && *index <= path.getLength() &&
+ segment != nullptr);
+ if (path[(*index)++] == '/') {
+ OUString name;
+ bool setElement;
+ OUString templateName;
+ *index = Data::parseSegment(
+ path, *index, &name, &setElement, &templateName);
+ if (*index != -1) {
+ *segment = Data::createSegment(templateName, name);
+ return *index == path.getLength();
+ }
+ }
+ throw css::uno::RuntimeException("bad path " + path);
+}
+
+}
+
+Partial::Partial(
+ std::set< OUString > const & includedPaths,
+ std::set< OUString > const & excludedPaths)
+{
+ // The Partial::Node tree built up here encodes the following information:
+ // * Inner node, startInclude: an include starts here that contains excluded
+ // sub-trees
+ // * Inner node, !startInclude: contains in-/excluded sub-trees
+ // * Leaf node, startInclude: an include starts here
+ // * Leaf node, !startInclude: an exclude starts here
+ for (auto const& includedPath : includedPaths)
+ {
+ sal_Int32 n = 0;
+ for (Node * p = &root_;;) {
+ OUString seg;
+ bool end = parseSegment(includedPath, &n, &seg);
+ p = &p->children[seg];
+ if (p->startInclude) {
+ break;
+ }
+ if (end) {
+ p->children.clear();
+ p->startInclude = true;
+ break;
+ }
+ }
+ }
+ for (auto const& excludedPath : excludedPaths)
+ {
+ sal_Int32 n = 0;
+ for (Node * p = &root_;;) {
+ OUString seg;
+ bool end = parseSegment(excludedPath, &n, &seg);
+ if (end) {
+ p->children[seg].clear();
+ break;
+ }
+ Node::Children::iterator j(p->children.find(seg));
+ if (j == p->children.end()) {
+ break;
+ }
+ p = &j->second;
+ }
+ }
+}
+
+Partial::~Partial() {}
+
+Partial::Containment Partial::contains(std::vector<OUString> const & path) const {
+ //TODO: For set elements, the segment names recorded in the node tree need
+ // not match the corresponding path segments, so this function can fail.
+
+ // * If path ends at a leaf node or goes past a leaf node:
+ // ** If that leaf node is startInclude: => CONTAINS_NODE
+ // ** If that leaf node is !startInclude: => CONTAINS_NOT
+ // * If path ends at inner node:
+ // ** If there is some startInclude along its trace: => CONTAINS_NODE
+ // ** If there is no startInclude along its trace: => CONTAINS_SUBNODES
+ Node const * p = &root_;
+ bool bIncludes = false;
+ for (auto const& elemPath : path)
+ {
+ Node::Children::const_iterator j(p->children.find(elemPath));
+ if (j == p->children.end()) {
+ return p->startInclude ? CONTAINS_NODE : CONTAINS_NOT;
+ }
+ p = &j->second;
+ bIncludes |= p->startInclude;
+ }
+ return p->children.empty() && !p->startInclude
+ ? CONTAINS_NOT
+ : bIncludes ? CONTAINS_NODE : CONTAINS_SUBNODES;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/partial.hxx b/configmgr/source/partial.hxx
new file mode 100644
index 000000000..265c70124
--- /dev/null
+++ b/configmgr/source/partial.hxx
@@ -0,0 +1,62 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <set>
+#include <boost/unordered_map.hpp>
+
+#include <rtl/ustring.hxx>
+
+namespace configmgr {
+
+class Partial {
+public:
+ enum Containment { CONTAINS_NOT, CONTAINS_SUBNODES, CONTAINS_NODE };
+
+ Partial(
+ std::set< OUString > const & includedPaths,
+ std::set< OUString > const & excludedPaths);
+
+ ~Partial();
+
+ Containment contains(std::vector<OUString> const & path) const;
+
+private:
+ Partial(const Partial&) = delete;
+ Partial& operator=(const Partial&) = delete;
+
+ struct Node {
+ typedef boost::unordered_map< OUString, Node, OUStringHash > Children;
+
+ Node(): startInclude(false) {}
+ void clear() { startInclude=false; children.clear(); }
+
+ Children children;
+ bool startInclude;
+ };
+
+ Node root_;
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/propertynode.cxx b/configmgr/source/propertynode.cxx
new file mode 100644
index 000000000..351025a2f
--- /dev/null
+++ b/configmgr/source/propertynode.cxx
@@ -0,0 +1,92 @@
+/* -*- 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 <com/sun/star/beans/Optional.hpp>
+#include <com/sun/star/uno/Any.hxx>
+#include <rtl/ref.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <utility>
+
+#include "components.hxx"
+#include "node.hxx"
+#include "propertynode.hxx"
+#include "type.hxx"
+
+namespace configmgr {
+
+PropertyNode::PropertyNode(
+ int layer, Type staticType, bool nillable, css::uno::Any value,
+ bool extension):
+ Node(layer), staticType_(staticType), nillable_(nillable),
+ extension_(extension), value_(std::move(value))
+{}
+
+rtl::Reference< Node > PropertyNode::clone(bool) const {
+ return new PropertyNode(*this);
+}
+
+
+css::uno::Any const & PropertyNode::getValue(Components & components) {
+ if (!externalDescriptor_.isEmpty()) {
+ css::beans::Optional< css::uno::Any > val(
+ components.getExternalValue(externalDescriptor_));
+ if (val.IsPresent) {
+ value_ = val.Value; //TODO: check value type
+ }
+ externalDescriptor_.clear(); // must not throw
+ }
+ SAL_WARN_IF(
+ !(value_.hasValue() || nillable_), "configmgr",
+ "non-nillable property without value");
+ return value_;
+}
+
+void PropertyNode::setValue(int layer, css::uno::Any const & value) {
+ setLayer(layer);
+ value_ = value;
+ externalDescriptor_.clear();
+}
+
+css::uno::Any *PropertyNode::getValuePtr(int layer)
+{
+ setLayer(layer);
+ externalDescriptor_.clear();
+ return &value_;
+}
+
+void PropertyNode::setExternal(int layer, OUString const & descriptor) {
+ assert(!descriptor.isEmpty());
+ setLayer(layer);
+ externalDescriptor_ = descriptor;
+}
+
+PropertyNode::~PropertyNode() {}
+
+Node::Kind PropertyNode::kind() const {
+ return KIND_PROPERTY;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/propertynode.hxx b/configmgr/source/propertynode.hxx
new file mode 100644
index 000000000..108b8e944
--- /dev/null
+++ b/configmgr/source/propertynode.hxx
@@ -0,0 +1,74 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <com/sun/star/uno/Any.hxx>
+#include <rtl/ref.hxx>
+
+#include "node.hxx"
+#include "type.hxx"
+
+
+namespace configmgr {
+
+class Components;
+
+class PropertyNode: public Node {
+public:
+ PropertyNode(
+ int layer, Type staticType, bool nillable,
+ css::uno::Any value, bool extension);
+
+ virtual rtl::Reference< Node > clone(bool keepTemplateName) const override;
+
+ Type getStaticType() const { return staticType_;}
+
+ bool isNillable() const { return nillable_;}
+
+ css::uno::Any const & getValue(Components & components);
+
+ void setValue(int layer, css::uno::Any const & value);
+ css::uno::Any *getValuePtr(int layer);
+
+ void setExternal(int layer, OUString const & descriptor);
+
+ bool isExtension() const { return extension_;}
+
+private:
+ PropertyNode(PropertyNode const&) = default;
+
+ virtual ~PropertyNode() override;
+
+ virtual Kind kind() const override;
+
+ Type staticType_;
+ // as specified in the component-schema (TYPE_ANY, ...,
+ // TYPE_HEXBINARY_LIST; not TYPE_ERROR or TYPE_NIL)
+ bool nillable_;
+ bool extension_;
+ OUString externalDescriptor_;
+ css::uno::Any value_;
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/readonlyaccess.cxx b/configmgr/source/readonlyaccess.cxx
new file mode 100644
index 000000000..76ec606d5
--- /dev/null
+++ b/configmgr/source/readonlyaccess.cxx
@@ -0,0 +1,121 @@
+/* -*- 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/.
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/NotInitializedException.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/lang/XServiceInfo.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/XInterface.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/weak.hxx>
+#include <osl/mutex.hxx>
+#include <rtl/ref.hxx>
+#include <rtl/ustring.hxx>
+#include <utility>
+#include <sal/types.h>
+
+#include "components.hxx"
+#include "lock.hxx"
+#include "rootaccess.hxx"
+
+namespace configmgr::read_only_access {
+
+namespace {
+
+class Service:
+ public cppu::WeakImplHelper<
+ css::lang::XServiceInfo, css::lang::XInitialization,
+ css::container::XHierarchicalNameAccess >
+{
+public:
+ explicit Service(
+ css::uno::Reference< css::uno::XComponentContext > context):
+ context_(std::move(context)) {}
+
+private:
+ Service(const Service&) = delete;
+ Service& operator=(const Service&) = delete;
+
+ virtual ~Service() override {}
+
+ virtual OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.comp.configuration.ReadOnlyAccess"; }
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
+ { return cppu::supportsService(this, ServiceName); }
+
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getSupportedServiceNames() override
+ { return { "com.sun.star.configuration.ReadOnlyAccess" }; }
+
+ virtual void SAL_CALL initialize(
+ css::uno::Sequence< css::uno::Any > const & aArguments) override;
+
+ virtual css::uno::Any SAL_CALL getByHierarchicalName(
+ OUString const & aName) override
+ { return getRoot()->getByHierarchicalName(aName); }
+
+ virtual sal_Bool SAL_CALL hasByHierarchicalName(OUString const & aName) override
+ { return getRoot()->hasByHierarchicalName(aName); }
+
+ rtl::Reference< RootAccess > getRoot();
+
+ css::uno::Reference< css::uno::XComponentContext > context_;
+
+ osl::Mutex mutex_;
+ rtl::Reference< RootAccess > root_;
+};
+
+void Service::initialize(css::uno::Sequence< css::uno::Any > const & aArguments)
+{
+ OUString locale;
+ if (aArguments.getLength() != 1 || !(aArguments[0] >>= locale)) {
+ throw css::lang::IllegalArgumentException(
+ "not exactly one string argument",
+ static_cast< cppu::OWeakObject * >(this), -1);
+ }
+ osl::MutexGuard g1(mutex_);
+ if (root_.is()) {
+ throw css::uno::RuntimeException(
+ "already initialized", static_cast< cppu::OWeakObject * >(this));
+ }
+ osl::MutexGuard g2(*lock());
+ Components & components = Components::getSingleton(context_);
+ root_ = new RootAccess(components, "/", locale, false);
+ components.addRootAccess(root_);
+}
+
+rtl::Reference< RootAccess > Service::getRoot() {
+ osl::MutexGuard g(mutex_);
+ if (!root_.is()) {
+ throw css::lang::NotInitializedException(
+ "not initialized", static_cast< cppu::OWeakObject * >(this));
+ }
+ return root_;
+}
+
+}
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_configuration_ReadOnlyAccess_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new configmgr::read_only_access::Service(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/readwriteaccess.cxx b/configmgr/source/readwriteaccess.cxx
new file mode 100644
index 000000000..57e8b298a
--- /dev/null
+++ b/configmgr/source/readwriteaccess.cxx
@@ -0,0 +1,144 @@
+/* -*- 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/.
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/configuration/XReadWriteAccess.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/NotInitializedException.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/lang/XServiceInfo.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/XInterface.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/weak.hxx>
+#include <osl/mutex.hxx>
+#include <rtl/ref.hxx>
+#include <rtl/ustring.hxx>
+#include <utility>
+#include <sal/types.h>
+
+#include "components.hxx"
+#include "lock.hxx"
+#include "rootaccess.hxx"
+
+namespace configmgr::read_write_access {
+
+namespace {
+
+class Service:
+ public cppu::WeakImplHelper<
+ css::lang::XServiceInfo, css::lang::XInitialization,
+ css::configuration::XReadWriteAccess >
+{
+public:
+ explicit Service(
+ css::uno::Reference< css::uno::XComponentContext > context):
+ context_(std::move(context)) {}
+
+private:
+ Service(const Service&) = delete;
+ Service& operator=(const Service&) = delete;
+
+ virtual ~Service() override {}
+
+ virtual OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.comp.configuration.ReadWriteAccess"; }
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
+ { return cppu::supportsService(this, ServiceName); }
+
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getSupportedServiceNames() override
+ { return { "com.sun.star.configuration.ReadWriteAccess" }; }
+
+ virtual void SAL_CALL initialize(
+ css::uno::Sequence< css::uno::Any > const & aArguments) override;
+
+ virtual css::uno::Any SAL_CALL getByHierarchicalName(
+ OUString const & aName) override
+ { return getRoot()->getByHierarchicalName(aName); }
+
+ virtual sal_Bool SAL_CALL hasByHierarchicalName(OUString const & aName) override
+ { return getRoot()->hasByHierarchicalName(aName); }
+
+ virtual void SAL_CALL replaceByHierarchicalName(
+ OUString const & aName, css::uno::Any const & aElement) override
+ { getRoot()->replaceByHierarchicalName(aName, aElement); }
+
+ virtual void SAL_CALL commitChanges() override
+ { getRoot()->commitChanges(); }
+
+ virtual sal_Bool SAL_CALL hasPendingChanges() override
+ { return getRoot()->hasPendingChanges(); }
+
+ virtual css::uno::Sequence< ::css::util::ElementChange > SAL_CALL getPendingChanges() override
+ { return getRoot()->getPendingChanges(); }
+
+ css::beans::Property SAL_CALL getPropertyByHierarchicalName(
+ OUString const & aHierarchicalName)
+ override
+ { return getRoot()->getPropertyByHierarchicalName(aHierarchicalName); }
+
+ sal_Bool SAL_CALL hasPropertyByHierarchicalName(
+ OUString const & aHierarchicalName) override
+ { return getRoot()->hasPropertyByHierarchicalName(aHierarchicalName); }
+
+ rtl::Reference< RootAccess > getRoot();
+
+ css::uno::Reference< css::uno::XComponentContext > context_;
+
+ osl::Mutex mutex_;
+ rtl::Reference< RootAccess > root_;
+};
+
+void Service::initialize(css::uno::Sequence< css::uno::Any > const & aArguments)
+{
+ OUString locale;
+ if (aArguments.getLength() != 1 || !(aArguments[0] >>= locale)) {
+ throw css::lang::IllegalArgumentException(
+ "not exactly one string argument",
+ static_cast< cppu::OWeakObject * >(this), -1);
+ }
+ osl::MutexGuard g1(mutex_);
+ if (root_.is()) {
+ throw css::uno::RuntimeException(
+ "already initialized", static_cast< cppu::OWeakObject * >(this));
+ }
+ osl::MutexGuard g2(*lock());
+ Components & components = Components::getSingleton(context_);
+ root_ = new RootAccess(components, "/", locale, true);
+ components.addRootAccess(root_);
+}
+
+rtl::Reference< RootAccess > Service::getRoot() {
+ osl::MutexGuard g(mutex_);
+ if (!root_.is()) {
+ throw css::lang::NotInitializedException(
+ "not initialized", static_cast< cppu::OWeakObject * >(this));
+ }
+ return root_;
+}
+
+}
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_configuration_ReadWriteAccess_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(new configmgr::read_write_access::Service(context));
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/rootaccess.cxx b/configmgr/source/rootaccess.cxx
new file mode 100644
index 000000000..ff7adddef
--- /dev/null
+++ b/configmgr/source/rootaccess.cxx
@@ -0,0 +1,313 @@
+/* -*- 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 <utility>
+#include <vector>
+
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/EventObject.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/Type.hxx>
+#include <com/sun/star/uno/XInterface.hpp>
+#include <com/sun/star/util/ChangesEvent.hpp>
+#include <com/sun/star/util/ChangesSet.hpp>
+#include <com/sun/star/util/ElementChange.hpp>
+#include <com/sun/star/util/XChangesBatch.hpp>
+#include <com/sun/star/util/XChangesListener.hpp>
+#include <com/sun/star/util/XChangesNotifier.hpp>
+#include <comphelper/sequence.hxx>
+#include <cppu/unotype.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <cppuhelper/weak.hxx>
+#include <osl/mutex.hxx>
+#include <rtl/ref.hxx>
+#include <rtl/ustring.hxx>
+
+#include "broadcaster.hxx"
+#include "components.hxx"
+#include "data.hxx"
+#include "lock.hxx"
+#include "modifications.hxx"
+#include "node.hxx"
+#include "rootaccess.hxx"
+
+namespace configmgr {
+
+RootAccess::RootAccess(
+ Components & components, OUString pathRepresentation,
+ OUString locale, bool update):
+ Access(components), pathRepresentation_(std::move(pathRepresentation)),
+ locale_(std::move(locale)),
+ lock_( lock() ),
+ update_(update), finalized_(false), alive_(true)
+{
+}
+
+std::vector<OUString> RootAccess::getAbsolutePath() {
+ getNode();
+ return path_;
+}
+
+void RootAccess::initBroadcaster(
+ Modifications::Node const & modifications, Broadcaster * broadcaster)
+{
+ assert(broadcaster != nullptr);
+ std::vector< css::util::ElementChange > changes;
+ initBroadcasterAndChanges(
+ modifications, broadcaster, changesListeners_.empty() ? nullptr : &changes);
+ if (changes.empty())
+ return;
+
+ css::util::ChangesSet set(comphelper::containerToSequence(changes));
+ for (auto const& changesListener : changesListeners_)
+ {
+ cppu::OWeakObject* pSource = this;
+ css::uno::Reference< css::uno::XInterface > xBase( pSource, css::uno::UNO_QUERY );
+ broadcaster->addChangesNotification(
+ changesListener,
+ css::util::ChangesEvent(
+ pSource, css::uno::Any( xBase ), set));
+ }
+}
+
+void RootAccess::acquire() noexcept {
+ Access::acquire();
+}
+
+void RootAccess::release() noexcept {
+ Access::release();
+}
+
+OUString const & RootAccess::getAbsolutePathRepresentation() {
+ getNode(); // turn pathRepresentation_ into canonic form
+ return pathRepresentation_;
+}
+
+
+void RootAccess::setAlive(bool b) {
+ alive_ = b;
+}
+
+void RootAccess::addChangesListener(
+ css::uno::Reference< css::util::XChangesListener > const & aListener)
+{
+ assert(thisIs(IS_ANY));
+ {
+ osl::MutexGuard g(*lock_);
+ checkLocalizedPropertyAccess();
+ if (!aListener.is()) {
+ throw css::uno::RuntimeException(
+ "null listener", static_cast< cppu::OWeakObject * >(this));
+ }
+ if (!isDisposed()) {
+ changesListeners_.insert(aListener);
+ return;
+ }
+ }
+ try {
+ aListener->disposing(
+ css::lang::EventObject(static_cast< cppu::OWeakObject * >(this)));
+ } catch (css::lang::DisposedException &) {}
+}
+
+void RootAccess::removeChangesListener(
+ css::uno::Reference< css::util::XChangesListener > const & aListener)
+{
+ assert(thisIs(IS_ANY));
+ osl::MutexGuard g(*lock_);
+ checkLocalizedPropertyAccess();
+ ChangesListeners::iterator i(changesListeners_.find(aListener));
+ if (i != changesListeners_.end()) {
+ changesListeners_.erase(i);
+ }
+}
+
+void RootAccess::commitChanges()
+{
+ assert(thisIs(IS_UPDATE));
+ if (!alive_)
+ {
+ return;
+ }
+ Broadcaster bc;
+ {
+ osl::MutexGuard g(*lock_);
+
+ checkLocalizedPropertyAccess();
+ int finalizedLayer;
+ Modifications globalMods;
+ commitChildChanges(
+ ((getComponents().resolvePathRepresentation(
+ pathRepresentation_, nullptr, nullptr, &finalizedLayer)
+ == node_) &&
+ finalizedLayer == Data::NO_LAYER),
+ &globalMods);
+ getComponents().writeModifications();
+ getComponents().initGlobalBroadcaster(globalMods, this, &bc);
+ }
+ bc.send();
+}
+
+sal_Bool RootAccess::hasPendingChanges() {
+ assert(thisIs(IS_UPDATE));
+ osl::MutexGuard g(*lock_);
+ checkLocalizedPropertyAccess();
+ //TODO: Optimize:
+ std::vector< css::util::ElementChange > changes;
+ reportChildChanges(&changes);
+ return !changes.empty();
+}
+
+css::uno::Sequence< ::css::util::ElementChange > RootAccess::getPendingChanges()
+{
+ assert(thisIs(IS_UPDATE));
+ osl::MutexGuard g(*lock_);
+ checkLocalizedPropertyAccess();
+ std::vector< css::util::ElementChange > changes;
+ reportChildChanges(&changes);
+ return comphelper::containerToSequence(changes);
+}
+
+RootAccess::~RootAccess()
+{
+ osl::MutexGuard g(*lock_);
+ if (alive_)
+ getComponents().removeRootAccess(this);
+}
+
+std::vector<OUString> RootAccess::getRelativePath() {
+ return std::vector<OUString>();
+}
+
+OUString RootAccess::getRelativePathRepresentation() {
+ return OUString();
+}
+
+rtl::Reference< Node > RootAccess::getNode() {
+ if (!node_.is()) {
+ OUString canonic;
+ int finalizedLayer;
+ node_ = getComponents().resolvePathRepresentation(
+ pathRepresentation_, &canonic, &path_, &finalizedLayer);
+ if (!node_.is()) {
+ throw css::uno::RuntimeException(
+ "cannot find " + pathRepresentation_, nullptr);
+ // RootAccess::queryInterface indirectly calls
+ // RootAccess::getNode, so if this RootAccess were passed out in
+ // RuntimeException.Context, client code that called
+ // queryInterface on it would cause trouble; therefore,
+ // RuntimeException.Context is left null here
+ }
+ pathRepresentation_ = canonic;
+ assert(!path_.empty() || node_->kind() == Node::KIND_ROOT);
+ if (!path_.empty()) {
+ name_ = path_.back();
+ }
+ finalized_ = finalizedLayer != Data::NO_LAYER;
+ }
+ return node_;
+}
+
+bool RootAccess::isFinalized() {
+ getNode();
+ return finalized_;
+}
+
+const OUString & RootAccess::getNameInternal() {
+ getNode();
+ return name_;
+}
+
+rtl::Reference< RootAccess > RootAccess::getRootAccess() {
+ return this;
+}
+
+rtl::Reference< Access > RootAccess::getParentAccess() {
+ return rtl::Reference< Access >();
+}
+
+void RootAccess::addTypes(std::vector< css::uno::Type > * types) const {
+ assert(types != nullptr);
+ types->push_back(cppu::UnoType< css::util::XChangesNotifier >::get());
+ types->push_back(cppu::UnoType< css::util::XChangesBatch >::get());
+}
+
+void RootAccess::addSupportedServiceNames(
+ std::vector<OUString> * services)
+{
+ assert(services != nullptr);
+ services->push_back("com.sun.star.configuration.AccessRootElement");
+ if (update_) {
+ services->push_back("com.sun.star.configuration.UpdateRootElement");
+ }
+}
+
+void RootAccess::initDisposeBroadcaster(Broadcaster * broadcaster) {
+ assert(broadcaster != nullptr);
+ for (auto const& changesListener : changesListeners_)
+ {
+ broadcaster->addDisposeNotification(
+ changesListener,
+ css::lang::EventObject(static_cast< cppu::OWeakObject * >(this)));
+ }
+ Access::initDisposeBroadcaster(broadcaster);
+}
+
+void RootAccess::clearListeners() noexcept {
+ changesListeners_.clear();
+ Access::clearListeners();
+}
+
+css::uno::Any RootAccess::queryInterface(css::uno::Type const & aType)
+{
+ assert(thisIs(IS_ANY));
+ osl::MutexGuard g(*lock_);
+ checkLocalizedPropertyAccess();
+ css::uno::Any res(Access::queryInterface(aType));
+ if (res.hasValue()) {
+ return res;
+ }
+ res = cppu::queryInterface(
+ aType, static_cast< css::util::XChangesNotifier * >(this));
+ if (res.hasValue()) {
+ return res;
+ }
+ if (!res.hasValue() && update_) {
+ res = cppu::queryInterface(
+ aType, static_cast< css::util::XChangesBatch * >(this));
+ }
+ return res;
+}
+
+OUString RootAccess::getImplementationName()
+{
+ assert(thisIs(IS_ANY));
+ osl::MutexGuard g(*lock_);
+ checkLocalizedPropertyAccess();
+ return "configmgr.RootAccess";
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/rootaccess.hxx b/configmgr/source/rootaccess.hxx
new file mode 100644
index 000000000..4eb90d36c
--- /dev/null
+++ b/configmgr/source/rootaccess.hxx
@@ -0,0 +1,144 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <set>
+#include <vector>
+
+#include <com/sun/star/util/XChangesBatch.hpp>
+#include <com/sun/star/util/XChangesNotifier.hpp>
+#include <rtl/ref.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+
+#include "access.hxx"
+#include "modifications.hxx"
+
+namespace com::sun::star {
+ namespace uno {
+ class Any;
+ class Type;
+ }
+ namespace util { class XChangesListener; }
+}
+
+namespace configmgr {
+
+class Broadcaster;
+class Components;
+class Node;
+
+class RootAccess:
+ public Access, public css::util::XChangesNotifier,
+ public css::util::XChangesBatch
+{
+public:
+ RootAccess(
+ Components & components, OUString pathRepresentation,
+ OUString locale, bool update);
+
+ virtual std::vector<OUString> getAbsolutePath() override;
+
+ virtual void initBroadcaster(
+ Modifications::Node const & modifications, Broadcaster * broadcaster) override;
+
+ virtual void SAL_CALL acquire() noexcept override;
+
+ virtual void SAL_CALL release() noexcept override;
+
+ OUString const & getAbsolutePathRepresentation();
+
+ const OUString& getLocale() const { return locale_;}
+
+ bool isUpdate() const { return update_;}
+
+ void setAlive(bool b);
+
+ virtual void SAL_CALL addChangesListener(
+ css::uno::Reference< css::util::XChangesListener >
+ const & aListener) override;
+
+ virtual void SAL_CALL removeChangesListener(
+ css::uno::Reference< css::util::XChangesListener >
+ const & aListener) override;
+
+ virtual void SAL_CALL commitChanges() override;
+
+ virtual sal_Bool SAL_CALL hasPendingChanges() override;
+
+ virtual css::uno::Sequence< ::css::util::ElementChange > SAL_CALL getPendingChanges() override;
+
+private:
+ virtual ~RootAccess() override;
+
+ virtual std::vector<OUString> getRelativePath() override;
+
+ virtual OUString getRelativePathRepresentation() override;
+
+ virtual rtl::Reference< Node > getNode() override;
+
+ virtual bool isFinalized() override;
+
+ virtual const OUString & getNameInternal() override;
+
+ virtual rtl::Reference< RootAccess > getRootAccess() override;
+
+ virtual rtl::Reference< Access > getParentAccess() override;
+
+ virtual void addTypes(std::vector< css::uno::Type > * types)
+ const override;
+
+ virtual void addSupportedServiceNames(
+ std::vector<OUString> * services) override;
+
+ virtual void initDisposeBroadcaster(Broadcaster * broadcaster) override;
+
+ virtual void clearListeners() noexcept override;
+
+ virtual css::uno::Any SAL_CALL queryInterface(
+ css::uno::Type const & aType) override;
+
+ virtual OUString SAL_CALL getImplementationName() override;
+
+ typedef
+ std::multiset<
+ css::uno::Reference<
+ css::util::XChangesListener > >
+ ChangesListeners;
+
+ OUString pathRepresentation_;
+ OUString locale_;
+ std::vector<OUString> path_;
+ rtl::Reference< Node > node_;
+ OUString name_;
+ ChangesListeners changesListeners_;
+
+ std::shared_ptr<osl::Mutex> lock_;
+
+ bool update_:1;
+ bool finalized_:1;
+ bool alive_:1;
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/rootnode.cxx b/configmgr/source/rootnode.cxx
new file mode 100644
index 000000000..ce70d688c
--- /dev/null
+++ b/configmgr/source/rootnode.cxx
@@ -0,0 +1,39 @@
+/* -*- 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/.
+ */
+
+#include <sal/config.h>
+
+#include <rtl/ref.hxx>
+
+#include "data.hxx"
+#include "node.hxx"
+#include "rootnode.hxx"
+
+namespace configmgr {
+
+RootNode::RootNode(): Node(Data::NO_LAYER) {}
+
+RootNode::~RootNode() {}
+
+Node::Kind RootNode::kind() const {
+ return KIND_ROOT;
+}
+
+rtl::Reference< Node > RootNode::clone(bool) const {
+ assert(false); // this cannot happen
+ return rtl::Reference< Node >();
+}
+
+NodeMap & RootNode::getMembers() {
+ return members_;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/rootnode.hxx b/configmgr/source/rootnode.hxx
new file mode 100644
index 000000000..84e10ef26
--- /dev/null
+++ b/configmgr/source/rootnode.hxx
@@ -0,0 +1,39 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <rtl/ref.hxx>
+
+#include "node.hxx"
+#include "nodemap.hxx"
+
+namespace configmgr
+{
+class RootNode : public Node
+{
+public:
+ RootNode();
+
+private:
+ virtual ~RootNode() override;
+
+ virtual Kind kind() const override;
+
+ virtual rtl::Reference<Node> clone(bool keepTemplateName) const override;
+
+ virtual NodeMap& getMembers() override;
+
+ NodeMap members_;
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/setnode.cxx b/configmgr/source/setnode.cxx
new file mode 100644
index 000000000..5e15ac31d
--- /dev/null
+++ b/configmgr/source/setnode.cxx
@@ -0,0 +1,90 @@
+/* -*- 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 <algorithm>
+
+#include <rtl/ref.hxx>
+#include <rtl/ustring.hxx>
+#include <utility>
+
+#include "data.hxx"
+#include "node.hxx"
+#include "nodemap.hxx"
+#include "setnode.hxx"
+
+namespace configmgr {
+
+SetNode::SetNode(
+ int layer, OUString defaultTemplateName,
+ OUString templateName):
+ Node(layer), defaultTemplateName_(std::move(defaultTemplateName)),
+ templateName_(std::move(templateName)), mandatory_(Data::NO_LAYER)
+{}
+
+rtl::Reference< Node > SetNode::clone(bool keepTemplateName) const {
+ return new SetNode(*this, keepTemplateName);
+}
+
+NodeMap & SetNode::getMembers() {
+ return members_;
+}
+
+OUString SetNode::getTemplateName() const {
+ return templateName_;
+}
+
+void SetNode::setMandatory(int layer) {
+ mandatory_ = layer;
+}
+
+int SetNode::getMandatory() const {
+ return mandatory_;
+}
+
+
+bool SetNode::isValidTemplate(OUString const & templateName) const {
+ return Data::equalTemplateNames(templateName, defaultTemplateName_) ||
+ std::any_of(
+ additionalTemplateNames_.begin(),
+ additionalTemplateNames_.end(),
+ [&templateName](OUString const & longName) { return Data::equalTemplateNames(templateName, longName); } );
+}
+
+SetNode::SetNode(SetNode const & other, bool keepTemplateName):
+ Node(other), defaultTemplateName_(other.defaultTemplateName_),
+ additionalTemplateNames_(other.additionalTemplateNames_),
+ mandatory_(other.mandatory_)
+{
+ other.members_.cloneInto(&members_);
+ if (keepTemplateName) {
+ templateName_ = other.templateName_;
+ }
+}
+
+SetNode::~SetNode() {}
+
+Node::Kind SetNode::kind() const {
+ return KIND_SET;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/setnode.hxx b/configmgr/source/setnode.hxx
new file mode 100644
index 000000000..706d153da
--- /dev/null
+++ b/configmgr/source/setnode.hxx
@@ -0,0 +1,73 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <vector>
+
+#include <rtl/ref.hxx>
+#include <rtl/ustring.hxx>
+
+#include "node.hxx"
+#include "nodemap.hxx"
+
+namespace configmgr {
+
+class SetNode: public Node {
+public:
+ SetNode(
+ int layer, OUString defaultTemplateName,
+ OUString templateName);
+
+ virtual rtl::Reference< Node > clone(bool keepTemplateName) const override;
+
+ virtual NodeMap & getMembers() override;
+
+ virtual OUString getTemplateName() const override;
+
+ virtual void setMandatory(int layer) override;
+
+ virtual int getMandatory() const override;
+
+ OUString const & getDefaultTemplateName() const { return defaultTemplateName_;}
+
+ std::vector<OUString> & getAdditionalTemplateNames() { return additionalTemplateNames_;}
+
+ bool isValidTemplate(OUString const & templateName) const;
+
+private:
+ SetNode(SetNode const & other, bool keepTemplateName);
+
+ virtual ~SetNode() override;
+
+ virtual Kind kind() const override;
+
+ OUString defaultTemplateName_;
+ std::vector<OUString> additionalTemplateNames_;
+ NodeMap members_;
+ OUString templateName_;
+ // non-empty if this node is a template, free node, or set member
+ int mandatory_;
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/type.cxx b/configmgr/source/type.cxx
new file mode 100644
index 000000000..b8fab28ac
--- /dev/null
+++ b/configmgr/source/type.cxx
@@ -0,0 +1,162 @@
+/* -*- 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 <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include <com/sun/star/uno/Type.hxx>
+#include <com/sun/star/uno/TypeClass.hpp>
+#include <cppu/unotype.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+
+#include "type.hxx"
+
+namespace configmgr {
+
+bool isListType(Type type) {
+ return type >= TYPE_BOOLEAN_LIST;
+}
+
+Type elementType(Type type) {
+ switch (type) {
+ case TYPE_BOOLEAN_LIST:
+ return TYPE_BOOLEAN;
+ case TYPE_SHORT_LIST:
+ return TYPE_SHORT;
+ case TYPE_INT_LIST:
+ return TYPE_INT;
+ case TYPE_LONG_LIST:
+ return TYPE_LONG;
+ case TYPE_DOUBLE_LIST:
+ return TYPE_DOUBLE;
+ case TYPE_STRING_LIST:
+ return TYPE_STRING;
+ case TYPE_HEXBINARY_LIST:
+ return TYPE_HEXBINARY;
+ default:
+ assert(false);
+ throw css::uno::RuntimeException("this cannot happen");
+ }
+}
+
+css::uno::Type const & mapType(Type type) {
+ switch (type) {
+ case TYPE_ANY:
+ return cppu::UnoType< css::uno::Any >::get();
+ case TYPE_BOOLEAN:
+ return cppu::UnoType< sal_Bool >::get();
+ case TYPE_SHORT:
+ return cppu::UnoType< sal_Int16 >::get();
+ case TYPE_INT:
+ return cppu::UnoType< sal_Int32 >::get();
+ case TYPE_LONG:
+ return cppu::UnoType< sal_Int64 >::get();
+ case TYPE_DOUBLE:
+ return cppu::UnoType< double >::get();
+ case TYPE_STRING:
+ return cppu::UnoType< OUString >::get();
+ case TYPE_HEXBINARY:
+ return cppu::UnoType< css::uno::Sequence< sal_Int8 > >::get();
+ case TYPE_BOOLEAN_LIST:
+ return cppu::UnoType< css::uno::Sequence< sal_Bool > >::get();
+ case TYPE_SHORT_LIST:
+ return cppu::UnoType< css::uno::Sequence< sal_Int16 > >::get();
+ case TYPE_INT_LIST:
+ return cppu::UnoType< css::uno::Sequence< sal_Int32 > >::get();
+ case TYPE_LONG_LIST:
+ return cppu::UnoType< css::uno::Sequence< sal_Int64 > >::get();
+ case TYPE_DOUBLE_LIST:
+ return cppu::UnoType< css::uno::Sequence< double > >::get();
+ case TYPE_STRING_LIST:
+ return cppu::UnoType< css::uno::Sequence< OUString > >::get();
+ case TYPE_HEXBINARY_LIST:
+ return cppu::UnoType<
+ css::uno::Sequence< css::uno::Sequence< sal_Int8 > > >::get();
+ default:
+ assert(false);
+ throw css::uno::RuntimeException("this cannot happen");
+ }
+}
+
+Type getDynamicType(css::uno::Any const & value) {
+ switch (value.getValueType().getTypeClass()) {
+ case css::uno::TypeClass_VOID:
+ return TYPE_NIL;
+ case css::uno::TypeClass_BOOLEAN:
+ return TYPE_BOOLEAN;
+ case css::uno::TypeClass_BYTE:
+ return TYPE_SHORT;
+ case css::uno::TypeClass_SHORT:
+ return TYPE_SHORT;
+ case css::uno::TypeClass_UNSIGNED_SHORT:
+ return value.has< sal_Int16 >() ? TYPE_SHORT : TYPE_INT;
+ case css::uno::TypeClass_LONG:
+ return TYPE_INT;
+ case css::uno::TypeClass_UNSIGNED_LONG:
+ return value.has< sal_Int32 >() ? TYPE_INT : TYPE_LONG;
+ case css::uno::TypeClass_HYPER:
+ return TYPE_LONG;
+ case css::uno::TypeClass_UNSIGNED_HYPER:
+ return value.has< sal_Int64 >() ? TYPE_LONG : TYPE_ERROR;
+ case css::uno::TypeClass_FLOAT:
+ case css::uno::TypeClass_DOUBLE:
+ return TYPE_DOUBLE;
+ case css::uno::TypeClass_STRING:
+ return TYPE_STRING;
+ case css::uno::TypeClass_SEQUENCE: //TODO
+ {
+ OUString name(value.getValueType().getTypeName());
+ if ( name == "[]byte" ) {
+ return TYPE_HEXBINARY;
+ } else if (name == "[]boolean")
+ {
+ return TYPE_BOOLEAN_LIST;
+ } else if ( name == "[]short" )
+ {
+ return TYPE_SHORT_LIST;
+ } else if ( name == "[]long" )
+ {
+ return TYPE_INT_LIST;
+ } else if ( name == "[]hyper" )
+ {
+ return TYPE_LONG_LIST;
+ } else if (name == "[]double")
+ {
+ return TYPE_DOUBLE_LIST;
+ } else if (name == "[]string")
+ {
+ return TYPE_STRING_LIST;
+ } else if (name == "[][]byte")
+ {
+ return TYPE_HEXBINARY_LIST;
+ }
+ }
+ [[fallthrough]];
+ default:
+ return TYPE_ERROR;
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/type.hxx b/configmgr/source/type.hxx
new file mode 100644
index 000000000..7fd096ac1
--- /dev/null
+++ b/configmgr/source/type.hxx
@@ -0,0 +1,48 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+#include <sal/types.h>
+
+namespace com::sun::star::uno {
+ class Any;
+ class Type;
+}
+
+namespace configmgr {
+
+enum Type {
+ TYPE_ERROR, TYPE_NIL, TYPE_ANY, TYPE_BOOLEAN, TYPE_SHORT, TYPE_INT,
+ TYPE_LONG, TYPE_DOUBLE, TYPE_STRING, TYPE_HEXBINARY, TYPE_BOOLEAN_LIST,
+ TYPE_SHORT_LIST, TYPE_INT_LIST, TYPE_LONG_LIST, TYPE_DOUBLE_LIST,
+ TYPE_STRING_LIST, TYPE_HEXBINARY_LIST };
+
+bool isListType(Type type);
+
+Type elementType(Type type);
+
+css::uno::Type const & mapType(Type type);
+
+Type getDynamicType(css::uno::Any const & value);
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/update.cxx b/configmgr/source/update.cxx
new file mode 100644
index 000000000..1cc2a06fe
--- /dev/null
+++ b/configmgr/source/update.cxx
@@ -0,0 +1,150 @@
+/* -*- 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 <set>
+
+#include <com/sun/star/configuration/XUpdate.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/uno/XInterface.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/weak.hxx>
+#include <osl/mutex.hxx>
+#include <rtl/ref.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+
+#include "broadcaster.hxx"
+#include "components.hxx"
+#include "lock.hxx"
+#include "modifications.hxx"
+#include "rootaccess.hxx"
+
+namespace configmgr::update {
+
+namespace {
+
+std::set< OUString > seqToSet(
+ css::uno::Sequence< OUString > const & sequence)
+{
+ return std::set< OUString >( sequence.begin(), sequence.end() );
+}
+
+class Service:
+ public cppu::WeakImplHelper< css::configuration::XUpdate >
+{
+public:
+ explicit Service(const css::uno::Reference< css::uno::XComponentContext >& context):
+ context_(context)
+ {
+ assert(context.is());
+ lock_ = lock();
+ }
+
+private:
+ Service(const Service&) = delete;
+ Service& operator=(const Service&) = delete;
+
+ virtual ~Service() override {}
+
+ virtual void SAL_CALL insertExtensionXcsFile(
+ sal_Bool shared, OUString const & fileUri) override;
+
+ virtual void SAL_CALL insertExtensionXcuFile(
+ sal_Bool shared, OUString const & fileUri) override;
+
+ virtual void SAL_CALL removeExtensionXcuFile(OUString const & fileUri) override;
+
+ virtual void SAL_CALL insertModificationXcuFile(
+ OUString const & fileUri,
+ css::uno::Sequence< OUString > const & includedPaths,
+ css::uno::Sequence< OUString > const & excludedPaths) override;
+
+ std::shared_ptr<osl::Mutex> lock_;
+ css::uno::Reference< css::uno::XComponentContext > context_;
+};
+
+void Service::insertExtensionXcsFile(
+ sal_Bool shared, OUString const & fileUri)
+{
+ osl::MutexGuard g(*lock_);
+ Components::getSingleton(context_).insertExtensionXcsFile(shared, fileUri);
+}
+
+void Service::insertExtensionXcuFile(
+ sal_Bool shared, OUString const & fileUri)
+{
+ Broadcaster bc;
+ {
+ osl::MutexGuard g(*lock_);
+ Components & components = Components::getSingleton(context_);
+ Modifications mods;
+ components.insertExtensionXcuFile(shared, fileUri, &mods);
+ components.initGlobalBroadcaster(
+ mods, rtl::Reference< RootAccess >(), &bc);
+ }
+ bc.send();
+}
+
+void Service::removeExtensionXcuFile(OUString const & fileUri)
+{
+ Broadcaster bc;
+ {
+ osl::MutexGuard g(*lock_);
+ Components & components = Components::getSingleton(context_);
+ Modifications mods;
+ components.removeExtensionXcuFile(fileUri, &mods);
+ components.initGlobalBroadcaster(
+ mods, rtl::Reference< RootAccess >(), &bc);
+ }
+ bc.send();
+}
+
+void Service::insertModificationXcuFile(
+ OUString const & fileUri,
+ css::uno::Sequence< OUString > const & includedPaths,
+ css::uno::Sequence< OUString > const & excludedPaths)
+{
+ Broadcaster bc;
+ {
+ osl::MutexGuard g(*lock_);
+ Components & components = Components::getSingleton(context_);
+ Modifications mods;
+ components.insertModificationXcuFile(
+ fileUri, seqToSet(includedPaths), seqToSet(excludedPaths), &mods);
+ components.initGlobalBroadcaster(
+ mods, rtl::Reference< RootAccess >(), &bc);
+ }
+ bc.send();
+}
+
+}
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_configuration_Update_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(new configmgr::update::Service(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/valueparser.cxx b/configmgr/source/valueparser.cxx
new file mode 100644
index 000000000..82778e6c8
--- /dev/null
+++ b/configmgr/source/valueparser.cxx
@@ -0,0 +1,454 @@
+/* -*- 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 <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <comphelper/sequence.hxx>
+#include <rtl/math.h>
+#include <rtl/string.h>
+#include <rtl/string.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+#include <xmlreader/span.hxx>
+#include <xmlreader/xmlreader.hxx>
+
+#include "localizedvaluenode.hxx"
+#include "node.hxx"
+#include "nodemap.hxx"
+#include "parsemanager.hxx"
+#include "propertynode.hxx"
+#include "type.hxx"
+#include "valueparser.hxx"
+
+namespace configmgr {
+
+namespace {
+
+bool parseHexDigit(char c, int * value) {
+ assert(value != nullptr);
+ if (c >= '0' && c <= '9') {
+ *value = c - '0';
+ return true;
+ }
+ if (c >= 'A' && c <= 'F') {
+ *value = c - 'A' + 10;
+ return true;
+ }
+ if (c >= 'a' && c <= 'f') {
+ *value = c - 'a' + 10;
+ return true;
+ }
+ return false;
+}
+
+bool parseValue(xmlreader::Span const & text, sal_Bool * value) {
+ assert(text.is() && value != nullptr);
+ if (text == "true" || text == "1") {
+ *value = true;
+ return true;
+ }
+ if (text == "false" || text == "0") {
+ *value = false;
+ return true;
+ }
+ return false;
+}
+
+bool parseValue(xmlreader::Span const & text, sal_Int16 * value) {
+ assert(text.is() && value != nullptr);
+ // For backwards compatibility, support hexadecimal values:
+ sal_Int32 n =
+ rtl_str_shortenedCompareIgnoreAsciiCase_WithLength(
+ text.begin, text.length, RTL_CONSTASCII_STRINGPARAM("0X"),
+ RTL_CONSTASCII_LENGTH("0X")) == 0 ?
+ static_cast< sal_Int32 >(
+ OString(
+ text.begin + RTL_CONSTASCII_LENGTH("0X"),
+ text.length - RTL_CONSTASCII_LENGTH("0X")).toUInt32(16)) :
+ OString(text.begin, text.length).toInt32();
+ //TODO: check valid lexical representation
+ if (n >= SAL_MIN_INT16 && n <= SAL_MAX_INT16) {
+ *value = static_cast< sal_Int16 >(n);
+ return true;
+ }
+ return false;
+}
+
+bool parseValue(xmlreader::Span const & text, sal_Int32 * value) {
+ assert(text.is() && value != nullptr);
+ // For backwards compatibility, support hexadecimal values:
+ *value =
+ rtl_str_shortenedCompareIgnoreAsciiCase_WithLength(
+ text.begin, text.length, RTL_CONSTASCII_STRINGPARAM("0X"),
+ RTL_CONSTASCII_LENGTH("0X")) == 0 ?
+ static_cast< sal_Int32 >(
+ OString(
+ text.begin + RTL_CONSTASCII_LENGTH("0X"),
+ text.length - RTL_CONSTASCII_LENGTH("0X")).toUInt32(16)) :
+ OString(text.begin, text.length).toInt32();
+ //TODO: check valid lexical representation
+ return true;
+}
+
+bool parseValue(xmlreader::Span const & text, sal_Int64 * value) {
+ assert(text.is() && value != nullptr);
+ // For backwards compatibility, support hexadecimal values:
+ *value =
+ rtl_str_shortenedCompareIgnoreAsciiCase_WithLength(
+ text.begin, text.length, RTL_CONSTASCII_STRINGPARAM("0X"),
+ RTL_CONSTASCII_LENGTH("0X")) == 0 ?
+ static_cast< sal_Int64 >(
+ OString(
+ text.begin + RTL_CONSTASCII_LENGTH("0X"),
+ text.length - RTL_CONSTASCII_LENGTH("0X")).toUInt64(16)) :
+ OString(text.begin, text.length).toInt64();
+ //TODO: check valid lexical representation
+ return true;
+}
+
+bool parseValue(xmlreader::Span const & text, double * value) {
+ assert(text.is() && value != nullptr);
+ *value = rtl_math_stringToDouble(
+ text.begin, text.begin + text.length, '.', 0, nullptr, nullptr);
+ //TODO: check valid lexical representation
+ return true;
+}
+
+bool parseValue(xmlreader::Span const & text, OUString * value) {
+ assert(text.is() && value != nullptr);
+ *value = text.convertFromUtf8();
+ return true;
+}
+
+bool parseValue(
+ xmlreader::Span const & text, css::uno::Sequence< sal_Int8 > * value)
+{
+ assert(text.is() && value != nullptr);
+ if ((text.length & 1) != 0) {
+ return false;
+ }
+ std::vector< sal_Int8 > seq;
+ for (sal_Int32 i = 0; i != text.length;) {
+ int n1;
+ int n2;
+ if (!parseHexDigit(text.begin[i++], &n1) ||
+ !parseHexDigit(text.begin[i++], &n2))
+ {
+ return false;
+ }
+ seq.push_back(static_cast< sal_Int8 >((n1 << 4) | n2));
+ }
+ *value = comphelper::containerToSequence(seq);
+ return true;
+}
+
+template< typename T > css::uno::Any parseSingleValue(
+ xmlreader::Span const & text)
+{
+ T val;
+ if (!parseValue(text, &val)) {
+ throw css::uno::RuntimeException("invalid value");
+ }
+ return css::uno::Any(val);
+}
+
+template< typename T > css::uno::Any parseListValue(
+ OString const & separator, xmlreader::Span const & text)
+{
+ std::vector< T > seq;
+ xmlreader::Span sep;
+ if (separator.isEmpty()) {
+ sep = xmlreader::Span(RTL_CONSTASCII_STRINGPARAM(" "));
+ } else {
+ sep = xmlreader::Span(separator.getStr(), separator.getLength());
+ }
+ if (text.length != 0) {
+ for (xmlreader::Span t(text);;) {
+ sal_Int32 i = rtl_str_indexOfStr_WithLength(
+ t.begin, t.length, sep.begin, sep.length);
+ T val;
+ if (!parseValue(
+ xmlreader::Span(t.begin, i == -1 ? t.length : i), &val))
+ {
+ throw css::uno::RuntimeException("invalid value");
+ }
+ seq.push_back(val);
+ if (i < 0) {
+ break;
+ }
+ t.begin += i + sep.length;
+ t.length -= i + sep.length;
+ }
+ }
+ return css::uno::Any(comphelper::containerToSequence(seq));
+}
+
+css::uno::Any parseValue(
+ OString const & separator, xmlreader::Span const & text, Type type)
+{
+ switch (type) {
+ case TYPE_ANY:
+ throw css::uno::RuntimeException("invalid value of type any");
+ case TYPE_BOOLEAN:
+ return parseSingleValue< sal_Bool >(text);
+ case TYPE_SHORT:
+ return parseSingleValue< sal_Int16 >(text);
+ case TYPE_INT:
+ return parseSingleValue< sal_Int32 >(text);
+ case TYPE_LONG:
+ return parseSingleValue< sal_Int64 >(text);
+ case TYPE_DOUBLE:
+ return parseSingleValue< double >(text);
+ case TYPE_STRING:
+ return parseSingleValue< OUString >(text);
+ case TYPE_HEXBINARY:
+ return parseSingleValue< css::uno::Sequence< sal_Int8 > >(text);
+ case TYPE_BOOLEAN_LIST:
+ return parseListValue< sal_Bool >(separator, text);
+ case TYPE_SHORT_LIST:
+ return parseListValue< sal_Int16 >(separator, text);
+ case TYPE_INT_LIST:
+ return parseListValue< sal_Int32 >(separator, text);
+ case TYPE_LONG_LIST:
+ return parseListValue< sal_Int64 >(separator, text);
+ case TYPE_DOUBLE_LIST:
+ return parseListValue< double >(separator, text);
+ case TYPE_STRING_LIST:
+ return parseListValue< OUString >(separator, text);
+ case TYPE_HEXBINARY_LIST:
+ return parseListValue< css::uno::Sequence< sal_Int8 > >(
+ separator, text);
+ default:
+ assert(false);
+ throw css::uno::RuntimeException("this cannot happen");
+ }
+}
+
+}
+
+ValueParser::ValueParser(int layer): type_(TYPE_ERROR), layer_(layer), state_() {}
+
+ValueParser::~ValueParser() {}
+
+xmlreader::XmlReader::Text ValueParser::getTextMode() const {
+ if (!node_)
+ return xmlreader::XmlReader::Text::NONE;
+
+ switch (state_) {
+ case State::Text:
+ if (!items_.empty()) {
+ break;
+ }
+ [[fallthrough]];
+ case State::IT:
+ return
+ (type_ == TYPE_STRING || type_ == TYPE_STRING_LIST ||
+ !separator_.isEmpty())
+ ? xmlreader::XmlReader::Text::Raw
+ : xmlreader::XmlReader::Text::Normalized;
+ default:
+ break;
+ }
+ return xmlreader::XmlReader::Text::NONE;
+}
+
+bool ValueParser::startElement(
+ xmlreader::XmlReader & reader, int nsId, xmlreader::Span const & name)
+{
+ if (!node_.is()) {
+ return false;
+ }
+ switch (state_) {
+ case State::Text:
+ if (nsId == xmlreader::XmlReader::NAMESPACE_NONE && name == "it" &&
+ isListType(type_) && separator_.isEmpty())
+ {
+ pad_.clear();
+ // before first <it>, characters are not ignored; assume they
+ // are only whitespace
+ state_ = State::IT;
+ return true;
+ }
+ [[fallthrough]];
+ case State::IT:
+ if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
+ name == "unicode" &&
+ (type_ == TYPE_STRING || type_ == TYPE_STRING_LIST))
+ {
+ sal_Int32 scalar = -1;
+ for (;;) {
+ int attrNsId;
+ xmlreader::Span attrLn;
+ if (!reader.nextAttribute(&attrNsId, &attrLn)) {
+ break;
+ }
+ if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "scalar")
+ {
+ if (!parseValue(reader.getAttributeValue(true), &scalar)) {
+ scalar = -1;
+ }
+ break;
+ }
+ }
+ if (scalar >= 0 && scalar < 0x20 && scalar != 0x09 &&
+ scalar != 0x0A && scalar != 0x0D)
+ {
+ char c = static_cast< char >(scalar);
+ pad_.add(&c, 1);
+ } else if (scalar == 0xFFFE) {
+ pad_.add(RTL_CONSTASCII_STRINGPARAM("\xEF\xBF\xBE"));
+ } else if (scalar == 0xFFFF) {
+ pad_.add(RTL_CONSTASCII_STRINGPARAM("\xEF\xBF\xBF"));
+ } else {
+ throw css::uno::RuntimeException(
+ "bad unicode scalar attribute in " + reader.getUrl());
+ }
+ state_ = state_ == State::Text ? State::TextUnicode : State::ITUnicode;
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+ throw css::uno::RuntimeException(
+ "bad member <" + name.convertFromUtf8() + "> in " + reader.getUrl());
+}
+
+bool ValueParser::endElement() {
+ if (!node_.is()) {
+ return false;
+ }
+ switch (state_) {
+ case State::Text:
+ {
+ css::uno::Any *pValue = nullptr;
+
+ switch (node_->kind()) {
+ case Node::KIND_PROPERTY:
+ pValue = static_cast< PropertyNode * >(node_.get())->getValuePtr(layer_);
+ break;
+ case Node::KIND_LOCALIZED_PROPERTY:
+ {
+ NodeMap & members = node_->getMembers();
+ NodeMap::iterator i(members.find(localizedName_));
+ LocalizedValueNode *pLVNode;
+ if (i == members.end()) {
+ pLVNode = new LocalizedValueNode(layer_);
+ members.insert(
+ NodeMap::value_type(localizedName_, pLVNode ));
+ } else {
+ pLVNode = static_cast< LocalizedValueNode * >(i->second.get());
+ }
+ pValue = pLVNode->getValuePtr(layer_);
+ }
+ break;
+ default:
+ assert(false); // this cannot happen
+ return false;
+ }
+
+ if (items_.empty()) {
+ *pValue = parseValue(separator_, pad_.get(), type_);
+ pad_.clear();
+ } else {
+ switch (type_) {
+ case TYPE_BOOLEAN_LIST:
+ *pValue = convertItems< sal_Bool >();
+ break;
+ case TYPE_SHORT_LIST:
+ *pValue = convertItems< sal_Int16 >();
+ break;
+ case TYPE_INT_LIST:
+ *pValue = convertItems< sal_Int32 >();
+ break;
+ case TYPE_LONG_LIST:
+ *pValue = convertItems< sal_Int64 >();
+ break;
+ case TYPE_DOUBLE_LIST:
+ *pValue = convertItems< double >();
+ break;
+ case TYPE_STRING_LIST:
+ *pValue = convertItems< OUString >();
+ break;
+ case TYPE_HEXBINARY_LIST:
+ *pValue = convertItems< css::uno::Sequence< sal_Int8 > >();
+ break;
+ default:
+ assert(false); // this cannot happen
+ break;
+ }
+ items_.clear();
+ }
+ separator_.clear();
+ node_.clear();
+ }
+ break;
+ case State::TextUnicode:
+ state_ = State::Text;
+ break;
+ case State::ITUnicode:
+ state_ = State::IT;
+ break;
+ case State::IT:
+ items_.push_back(
+ parseValue(OString(), pad_.get(), elementType(type_)));
+ pad_.clear();
+ state_ = State::Text;
+ break;
+ }
+ return true;
+}
+
+void ValueParser::characters(xmlreader::Span const & text) {
+ if (node_.is()) {
+ assert(state_ == State::Text || state_ == State::IT);
+ pad_.add(text.begin, text.length);
+ }
+}
+
+void ValueParser::start(
+ rtl::Reference< Node > const & node, OUString const & localizedName)
+{
+ assert(node.is() && !node_.is());
+ node_ = node;
+ localizedName_ = localizedName;
+ state_ = State::Text;
+}
+
+
+template< typename T > css::uno::Any ValueParser::convertItems() {
+ css::uno::Sequence< T > seq(items_.size());
+ auto seqRange = asNonConstRange(seq);
+ for (sal_Int32 i = 0; i < seq.getLength(); ++i) {
+ bool ok = (items_[i] >>= seqRange[i]);
+ assert(ok);
+ (void) ok; // avoid warnings
+ }
+ return css::uno::Any(seq);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/valueparser.hxx b/configmgr/source/valueparser.hxx
new file mode 100644
index 000000000..33404b838
--- /dev/null
+++ b/configmgr/source/valueparser.hxx
@@ -0,0 +1,85 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <vector>
+
+#include <rtl/ref.hxx>
+#include <rtl/string.hxx>
+#include <rtl/ustring.hxx>
+#include <xmlreader/pad.hxx>
+#include <xmlreader/xmlreader.hxx>
+
+#include "type.hxx"
+
+namespace com::sun::star::uno {
+ class Any;
+}
+namespace xmlreader { struct Span; }
+
+namespace configmgr {
+
+class Node;
+
+class ValueParser {
+public:
+ explicit ValueParser(int layer);
+
+ ~ValueParser();
+
+ xmlreader::XmlReader::Text getTextMode() const;
+
+ bool startElement(
+ xmlreader::XmlReader & reader, int nsId, xmlreader::Span const & name);
+
+ bool endElement();
+
+ void characters(xmlreader::Span const & text);
+
+ void start(
+ rtl::Reference< Node > const & property,
+ OUString const & localizedName = OUString());
+
+ int getLayer() const { return layer_;}
+
+ Type type_;
+ OString separator_;
+
+private:
+ ValueParser(const ValueParser&) = delete;
+ ValueParser& operator=(const ValueParser&) = delete;
+
+ template< typename T > css::uno::Any convertItems();
+
+ enum class State { Text, TextUnicode, IT, ITUnicode };
+
+ int layer_;
+ rtl::Reference< Node > node_;
+ OUString localizedName_;
+ State state_;
+ xmlreader::Pad pad_;
+ std::vector< css::uno::Any > items_;
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/winreg.cxx b/configmgr/source/winreg.cxx
new file mode 100644
index 000000000..381150fc2
--- /dev/null
+++ b/configmgr/source/winreg.cxx
@@ -0,0 +1,331 @@
+/* -*- 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/.
+ *
+ */
+
+#include <cwchar>
+#include <memory>
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <msiquery.h>
+
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include <com/sun/star/uno/XInterface.hpp>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <osl/file.h>
+#include <osl/file.hxx>
+#include <o3tl/char16_t2wchar_t.hxx>
+#include "winreg.hxx"
+#include "writemodfile.hxx"
+
+#define MAX_KEY_LENGTH 255
+
+namespace configmgr {
+
+namespace {
+// This is not a generic registry reader. We assume the following structure:
+// Last element of Key becomes prop, first part is the path and optionally nodes,
+// when the node has oor:op attribute.
+// Values can be the following: Value (string), Type (string, optional),
+// Final (dword, optional), External (dword, optional), ExternalBackend (string, optional),
+// Nil (dword, optional)
+//
+// For example the following registry setting:
+// [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\LibreOffice\org.openoffice.UserProfile\Data\o]
+// "Value"="Example Corp."
+// "Final"=dword:00000001
+// becomes the following in configuration:
+// <!-- set the Company name -->
+// <item oor:path="/org.openoffice.UserProfile/Data">
+// <prop oor:name="o" oor:finalized="true">
+// <value>Example Corp.</value>
+// </prop>
+// </item>
+//
+// Another example:
+// [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\LibreOffice\org.openoffice.Office.OptionsDialog\OptionsDialogGroups\ProductName/#fuse\Pages\Java/#fuse\Hide]
+// "Value"="true"
+// becomes the following in configuration:
+// <!-- Hide Tools - Options - LibreOffice - Advanced panel -->
+// <item oor:path="/org.openoffice.Office.OptionsDialog/OptionsDialogGroups">
+// <node oor:name="ProductName" oor:op="fuse">
+// <node oor:name="Pages">
+// <node oor:name="Java" oor:op="fuse">
+// <prop oor:name="Hide">
+// <value>true</value>
+// </prop>
+// </node>
+// </node>
+// </node>
+// </item>
+//
+// Third example (property of an extensible group -> needs type):
+// [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\LibreOffice\org.openoffice.Office.Jobs\Jobs\org.openoffice.Office.Jobs:Job['UpdateCheck']\Arguments\AutoCheckEnabled]
+// "Value"="false"
+// "Final"=dword:00000001
+// "Type"="xs:boolean"
+// becomes the following in configuration:
+// <item oor:path="/org.openoffice.Office.Jobs/Jobs/org.openoffice.Office.Jobs:Job['UpdateCheck']/Arguments">
+// <prop oor:name="AutoCheckEnabled" oor:type="xs:boolean" oor:finalized="true">
+// <value>false</value>
+// </prop>
+// </item>
+//
+// External (component data) example:
+// [HKEY_CURRENT_USER\Software\Policies\LibreOffice\org.openoffice.UserProfile\Data\o]
+// "Value"="company"
+// "Final"=dword:00000001
+// "External"=dword:00000001
+// "ExternalBackend"="com.sun.star.configuration.backend.LdapUserProfileBe"
+// becomes the following in configuration:
+// <item oor:path="/org.openoffice.UserProfile/Data">
+// <prop oor:name="o" oor:finalized="true">
+// <value oor:external="com.sun.star.configuration.backend.LdapUserProfileBe company"/>
+// </prop>
+// </item>
+//
+// Nil example:
+// Empty value (<value></value>) and nil value (<value xsi:nil="true"/>) are different.
+// In case of some path settings, the base path setting has to be cleared.
+// [HKEY_CURRENT_USER\Software\Policies\LibreOffice\org.openoffice.Office.Common\Path\Current\Work]
+// "Value"=""
+// "Final"=dword:00000001
+// "Nil"=dword:00000001
+// [HKEY_CURRENT_USER\Software\Policies\LibreOffice\org.openoffice.Office.Paths\Paths\org.openoffice.Office.Paths:NamedPath['Work']\WritePath]
+// "Value"="file:///H:/"
+// "Final"=dword:00000001
+// becomes the following in configuration:
+// <item oor:path="/org.openoffice.Office.Common/Path/Current">
+// <prop oor:name="Work" oor:finalized="true">
+// <value xsi:nil="true"/>
+// </prop>
+// </item>
+// <item oor:path="/org.openoffice.Office.Paths/Paths/org.openoffice.Office.Paths:NamedPath['Work']">
+// <prop oor:name="WritePath" oor:finalized="true">
+// <value>file:///H:/</value>
+// </prop>
+// </item>
+
+void dumpWindowsRegistryKey(HKEY hKey, OUString const & aKeyName, TempFile &aFileHandle)
+{
+ HKEY hCurKey;
+
+ if(RegOpenKeyExW(
+ hKey, o3tl::toW(aKeyName.getStr()), 0,
+ KEY_READ, &hCurKey)
+ == ERROR_SUCCESS)
+ {
+ DWORD nSubKeys = 0;
+ DWORD nValues = 0;
+ DWORD nLongestValueNameLen, nLongestValueLen;
+ // Query the number of subkeys
+ RegQueryInfoKeyW(hCurKey, nullptr, nullptr, nullptr, &nSubKeys, nullptr, nullptr, &nValues, &nLongestValueNameLen, &nLongestValueLen, nullptr, nullptr);
+ if(nSubKeys)
+ {
+ //Look for subkeys in this key
+ for(DWORD i = 0; i < nSubKeys; i++)
+ {
+ wchar_t buffKeyName[MAX_KEY_LENGTH];
+ buffKeyName[0] = '\0';
+ DWORD buffSize=MAX_KEY_LENGTH;
+ OUString aSubkeyName;
+ //Get subkey name
+ RegEnumKeyExW(hCurKey, i, buffKeyName, &buffSize, nullptr, nullptr, nullptr, nullptr);
+
+ //Make up full key name
+ if(aKeyName.isEmpty())
+ aSubkeyName = aKeyName + o3tl::toU(buffKeyName);
+ else
+ aSubkeyName = aKeyName + "\\" + o3tl::toU(buffKeyName);
+
+ //Recursion, until no more subkeys are found
+ dumpWindowsRegistryKey(hKey, aSubkeyName, aFileHandle);
+ }
+ }
+ else if(nValues)
+ {
+ // No more subkeys, we are at a leaf
+ auto pValueName = std::unique_ptr<wchar_t[]>(
+ new wchar_t[nLongestValueNameLen + 1]);
+ auto pValue = std::unique_ptr<wchar_t[]>(
+ new wchar_t[nLongestValueLen/sizeof(wchar_t) + 1]);
+
+ bool bFinal = false;
+ bool bExternal = false;
+ bool bNil = false;
+ OUString aValue;
+ OUString aType;
+ OUString aExternalBackend;
+
+ for(DWORD i = 0; i < nValues; ++i)
+ {
+ DWORD nValueNameLen = nLongestValueNameLen + 1;
+ DWORD nValueLen = nLongestValueLen + 1;
+
+ RegEnumValueW(hCurKey, i, pValueName.get(), &nValueNameLen, nullptr, nullptr, reinterpret_cast<LPBYTE>(pValue.get()), &nValueLen);
+
+ if (!wcscmp(pValueName.get(), L"Value"))
+ aValue = o3tl::toU(pValue.get());
+ else if (!wcscmp(pValueName.get(), L"Type"))
+ aType = o3tl::toU(pValue.get());
+ else if (!wcscmp(pValueName.get(), L"Final"))
+ {
+ if (*reinterpret_cast<DWORD*>(pValue.get()) == 1)
+ bFinal = true;
+ }
+ else if (!wcscmp(pValueName.get(), L"Nil"))
+ {
+ if (*reinterpret_cast<DWORD*>(pValue.get()) == 1)
+ bNil = true;
+ }
+ else if (!wcscmp(pValueName.get(), L"External"))
+ {
+ if (*reinterpret_cast<DWORD*>(pValue.get()) == 1)
+ bExternal = true;
+ }
+ else if (!wcscmp(pValueName.get(), L"ExternalBackend"))
+ aExternalBackend = o3tl::toU(pValue.get());
+ }
+ if (bExternal)
+ {
+ // type and external are mutually exclusive
+ aType.clear();
+
+ // Prepend backend, like in
+ // "com.sun.star.configuration.backend.LdapUserProfileBe company"
+ if (!aExternalBackend.isEmpty())
+ aValue = aExternalBackend + " " + aValue;
+ }
+
+ sal_Int32 aLastSeparator = aKeyName.lastIndexOf('\\');
+ OUString aPathAndNodes = aKeyName.copy(0, aLastSeparator);
+ OUString aProp = aKeyName.copy(aLastSeparator + 1);
+ bool bHasNode = false;
+ sal_Int32 nCloseNode = 0;
+
+ aFileHandle.writeString("<item oor:path=\"");
+ for(sal_Int32 nIndex = 0;;)
+ {
+ OUString aNextPathPart = aPathAndNodes.getToken(0, '\\', nIndex);
+
+ if(!aNextPathPart.isEmpty())
+ {
+ if((aNextPathPart.lastIndexOf("/#") != -1) || bHasNode)
+ {
+ bHasNode = true;
+ nCloseNode++;
+ aFileHandle.writeString("\"><node oor:name=\"");
+ sal_Int32 nCommandSeparator = aNextPathPart.lastIndexOf('#');
+ if(nCommandSeparator != -1)
+ {
+ OUString aNodeOp = aNextPathPart.copy(nCommandSeparator + 1);
+ writeAttributeValue(aFileHandle, aNextPathPart.subView(0, nCommandSeparator - 1));
+ aFileHandle.writeString("\" oor:op=\"");
+ writeAttributeValue(aFileHandle, aNodeOp);
+ }
+ else
+ {
+ writeAttributeValue(aFileHandle, aNextPathPart);
+ }
+ }
+ else
+ {
+ writeAttributeValue(
+ aFileHandle, OUStringConcatenation("/" + aNextPathPart));
+ }
+ }
+ else
+ {
+ aFileHandle.writeString("\">");
+ break;
+ }
+ }
+
+ aFileHandle.writeString("<prop oor:name=\"");
+ writeAttributeValue(aFileHandle, aProp);
+ aFileHandle.writeString("\"");
+ if(!aType.isEmpty())
+ {
+ aFileHandle.writeString(" oor:type=\"");
+ writeAttributeValue(aFileHandle, aType);
+ aFileHandle.writeString("\"");
+ }
+ if(bFinal)
+ aFileHandle.writeString(" oor:finalized=\"true\"");
+ aFileHandle.writeString("><value");
+ if (aValue.isEmpty() && bNil)
+ {
+ aFileHandle.writeString(" xsi:nil=\"true\"/");
+ }
+ else if (bExternal)
+ {
+ aFileHandle.writeString(" oor:external=\"");
+ writeAttributeValue(aFileHandle, aValue);
+ aFileHandle.writeString("\"/");
+ }
+ else
+ {
+ aFileHandle.writeString(">");
+ writeValueContent(aFileHandle, aValue);
+ aFileHandle.writeString("</value");
+ }
+ aFileHandle.writeString("></prop>");
+ for(; nCloseNode > 0; nCloseNode--)
+ aFileHandle.writeString("</node>");
+ aFileHandle.writeString("</item>\n");
+ }
+ RegCloseKey(hCurKey);
+ }
+}
+}
+
+bool dumpWindowsRegistry(OUString* pFileURL, WinRegType eType)
+{
+ HKEY hKey;
+ HKEY hDomain = eType == LOCAL_MACHINE ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
+ if(RegOpenKeyExW(hDomain, L"SOFTWARE\\Policies\\LibreOffice", 0, KEY_READ, &hKey) != ERROR_SUCCESS)
+ {
+ SAL_INFO(
+ "configmgr",
+ ("Windows registry settings do not exist in HKLM\\SOFTWARE\\Policies\\LibreOffice"));
+ return false;
+ }
+
+ TempFile aFileHandle;
+ switch (osl::FileBase::createTempFile(nullptr, &aFileHandle.handle, pFileURL)) {
+ case osl::FileBase::E_None:
+ break;
+ case osl::FileBase::E_ACCES:
+ SAL_INFO(
+ "configmgr",
+ ("cannot create temp Windows registry dump (E_ACCES)"));
+ return false;
+ default:
+ throw css::uno::RuntimeException(
+ "cannot create temporary file");
+ }
+ aFileHandle.url = *pFileURL;
+ aFileHandle.writeString(
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<oor:items"
+ " xmlns:oor=\"http://openoffice.org/2001/registry\""
+ " xmlns:xs=\"http://www.w3.org/2001/XMLSchema\""
+ " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n");
+ dumpWindowsRegistryKey(hKey, "", aFileHandle);
+ aFileHandle.writeString("</oor:items>");
+ oslFileError e = aFileHandle.closeWithoutUnlink();
+ if (e != osl_File_E_None)
+ SAL_WARN("configmgr", "osl_closeFile failed with " << +e);
+ RegCloseKey(hKey);
+ return true;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/winreg.hxx b/configmgr/source/winreg.hxx
new file mode 100644
index 000000000..132a1f6d4
--- /dev/null
+++ b/configmgr/source/winreg.hxx
@@ -0,0 +1,21 @@
+/* -*- 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/.
+ *
+ */
+
+#pragma once
+
+namespace configmgr {
+
+enum WinRegType { LOCAL_MACHINE, CURRENT_USER };
+
+bool dumpWindowsRegistry(OUString* pFileURL, WinRegType eType);
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/writemodfile.cxx b/configmgr/source/writemodfile.cxx
new file mode 100644
index 000000000..09fe0949b
--- /dev/null
+++ b/configmgr/source/writemodfile.cxx
@@ -0,0 +1,640 @@
+/* -*- 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 <cstddef>
+#include <limits>
+#include <string_view>
+
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/file.h>
+#include <osl/file.hxx>
+#include <rtl/string.h>
+#include <rtl/string.hxx>
+#include <rtl/textcvt.h>
+#include <rtl/textenc.h>
+#include <rtl/ustring.hxx>
+#include <rtl/strbuf.hxx>
+#include <sal/log.hxx>
+#include <sal/types.h>
+#include <xmlreader/span.hxx>
+
+#include "data.hxx"
+#include "groupnode.hxx"
+#include "localizedpropertynode.hxx"
+#include "localizedvaluenode.hxx"
+#include "modifications.hxx"
+#include "node.hxx"
+#include "nodemap.hxx"
+#include "propertynode.hxx"
+#include "type.hxx"
+#include "writemodfile.hxx"
+
+namespace configmgr {
+
+class Components;
+
+namespace {
+
+OString convertToUtf8(std::u16string_view text) {
+ OString s;
+ assert(text.size() <= o3tl::make_unsigned(std::numeric_limits<sal_Int32>::max()));
+ if (!rtl_convertUStringToString(
+ &s.pData, text.data(), text.size(),
+ RTL_TEXTENCODING_UTF8,
+ (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR |
+ RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR)))
+ {
+ throw css::uno::RuntimeException(
+ "cannot convert to UTF-8");
+ }
+ return s;
+}
+
+} // anonymous namespace
+
+TempFile::~TempFile() {
+ if (handle == nullptr)
+ return;
+
+ if (!closed) {
+ oslFileError e = osl_closeFile(handle);
+ if (e != osl_File_E_None) {
+ SAL_WARN("configmgr", "osl_closeFile failed with " << +e);
+ }
+ }
+ osl::FileBase::RC e = osl::File::remove(url);
+ if (e != osl::FileBase::E_None) {
+ SAL_WARN(
+ "configmgr",
+ "osl::File::remove(" << url << ") failed with " << +e);
+ }
+}
+
+#ifdef _WIN32
+oslFileError TempFile::closeWithoutUnlink() {
+ flush();
+ oslFileError e = osl_closeFile(handle);
+ handle = nullptr;
+ closed = true;
+ return e;
+}
+#endif
+
+void TempFile::closeAndRename(const OUString &_url) {
+ oslFileError e = flush();
+ if (e != osl_File_E_None) {
+ throw css::uno::RuntimeException(
+ "cannot write to " + url);
+ }
+ e = osl_closeFile(handle);
+ closed = true;
+ if (e != osl_File_E_None) {
+ throw css::uno::RuntimeException(
+ "cannot close " + url);
+ }
+ if (osl::File::replace(url, _url) != osl::FileBase::E_None) {
+ throw css::uno::RuntimeException(
+ "cannot move " + url);
+ }
+ handle = nullptr;
+}
+
+oslFileError TempFile::flush() {
+ oslFileError e = osl_File_E_None;
+ if (!buffer.isEmpty()) {
+ sal_uInt64 nBytesWritten = 0;
+ e = osl_writeFile(handle, buffer.getStr(),
+ static_cast< sal_uInt32 >(buffer.getLength()),
+ &nBytesWritten);
+ if (nBytesWritten != static_cast< sal_uInt32 >(buffer.getLength())) {
+ // queue up any error / exception until close.
+ buffer.remove(0, static_cast< sal_Int32 >( nBytesWritten ) );
+ } else {
+ buffer.setLength(0);
+ }
+ }
+ return e;
+}
+
+void TempFile::writeString(std::string_view text) {
+ buffer.append(text.data(), text.size());
+ if (buffer.getLength() > 0x10000)
+ flush();
+}
+
+namespace {
+
+void writeValueContent_(TempFile &, bool) = delete;
+ // silence loplugin:salbool
+void writeValueContent_(TempFile &handle, sal_Bool value) {
+ if (value) {
+ handle.writeString("true");
+ } else {
+ handle.writeString("false");
+ }
+}
+
+void writeValueContent_(TempFile &handle, sal_Int16 value) {
+ handle.writeString(OString::number(value));
+}
+
+void writeValueContent_(TempFile &handle, sal_Int32 value) {
+ handle.writeString(OString::number(value));
+}
+
+void writeValueContent_(TempFile &handle, sal_Int64 value) {
+ handle.writeString(OString::number(value));
+}
+
+void writeValueContent_(TempFile &handle, double value) {
+ handle.writeString(OString::number(value));
+}
+
+void writeValueContent_(TempFile &handle, std::u16string_view value) {
+ writeValueContent(handle, value);
+}
+
+void writeValueContent_(
+ TempFile &handle, css::uno::Sequence< sal_Int8 > const & value)
+{
+ for (const auto & v : value) {
+ static char const hexDigit[16] = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C',
+ 'D', 'E', 'F' };
+ handle.writeString(
+ std::string_view(hexDigit + ((v >> 4) & 0xF), 1));
+ handle.writeString(std::string_view(hexDigit + (v & 0xF), 1));
+ }
+}
+
+template< typename T > void writeSingleValue(
+ TempFile &handle, css::uno::Any const & value)
+{
+ handle.writeString(">");
+ T val = T();
+ value >>= val;
+ writeValueContent_(handle, val);
+ handle.writeString("</value>");
+}
+
+template< typename T > void writeListValue(
+ TempFile &handle, css::uno::Any const & value)
+{
+ handle.writeString(">");
+ css::uno::Sequence< T > val;
+ value >>= val;
+ for (sal_Int32 i = 0; i < val.getLength(); ++i) {
+ if (i != 0) {
+ handle.writeString(" ");
+ }
+ writeValueContent_(handle, std::as_const(val)[i]);
+ }
+ handle.writeString("</value>");
+}
+
+template< typename T > void writeItemListValue(
+ TempFile &handle, css::uno::Any const & value)
+{
+ handle.writeString(">");
+ css::uno::Sequence< T > val;
+ value >>= val;
+ for (const auto & i : std::as_const(val)) {
+ handle.writeString("<it>");
+ writeValueContent_(handle, i);
+ handle.writeString("</it>");
+ }
+ handle.writeString("</value>");
+}
+
+void writeValue(TempFile &handle, Type type, css::uno::Any const & value) {
+ switch (type) {
+ case TYPE_BOOLEAN:
+ writeSingleValue< sal_Bool >(handle, value);
+ break;
+ case TYPE_SHORT:
+ writeSingleValue< sal_Int16 >(handle, value);
+ break;
+ case TYPE_INT:
+ writeSingleValue< sal_Int32 >(handle, value);
+ break;
+ case TYPE_LONG:
+ writeSingleValue< sal_Int64 >(handle, value);
+ break;
+ case TYPE_DOUBLE:
+ writeSingleValue< double >(handle, value);
+ break;
+ case TYPE_STRING:
+ writeSingleValue< OUString >(handle, value);
+ break;
+ case TYPE_HEXBINARY:
+ writeSingleValue< css::uno::Sequence< sal_Int8 > >(handle, value);
+ break;
+ case TYPE_BOOLEAN_LIST:
+ writeListValue< sal_Bool >(handle, value);
+ break;
+ case TYPE_SHORT_LIST:
+ writeListValue< sal_Int16 >(handle, value);
+ break;
+ case TYPE_INT_LIST:
+ writeListValue< sal_Int32 >(handle, value);
+ break;
+ case TYPE_LONG_LIST:
+ writeListValue< sal_Int64 >(handle, value);
+ break;
+ case TYPE_DOUBLE_LIST:
+ writeListValue< double >(handle, value);
+ break;
+ case TYPE_STRING_LIST:
+ writeItemListValue< OUString >(handle, value);
+ break;
+ case TYPE_HEXBINARY_LIST:
+ writeItemListValue< css::uno::Sequence< sal_Int8 > >(handle, value);
+ break;
+ default: // TYPE_ERROR, TYPE_NIL, TYPE_ANY
+ assert(false); // this cannot happen
+ }
+}
+
+void writeNode(
+ Components & components, TempFile &handle,
+ rtl::Reference< Node > const & parent, std::u16string_view name,
+ rtl::Reference< Node > const & node)
+{
+ static xmlreader::Span const typeNames[] = {
+ xmlreader::Span(), xmlreader::Span(), xmlreader::Span(),
+ // TYPE_ERROR, TYPE_NIL, TYPE_ANY
+ xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("xs:boolean")),
+ xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("xs:short")),
+ xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("xs:int")),
+ xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("xs:long")),
+ xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("xs:double")),
+ xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("xs:string")),
+ xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("xs:hexBinary")),
+ xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("oor:boolean-list")),
+ xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("oor:short-list")),
+ xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("oor:int-list")),
+ xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("oor:long-list")),
+ xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("oor:double-list")),
+ xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("oor:string-list")),
+ xmlreader::Span(RTL_CONSTASCII_STRINGPARAM("oor:hexBinary-list")) };
+ switch (node->kind()) {
+ case Node::KIND_PROPERTY:
+ {
+ PropertyNode * prop = static_cast< PropertyNode * >(node.get());
+ handle.writeString("<prop oor:name=\"");
+ writeAttributeValue(handle, name);
+ handle.writeString("\" oor:op=\"fuse\"");
+ Type type = prop->getStaticType();
+ Type dynType = getDynamicType(prop->getValue(components));
+ assert(dynType != TYPE_ERROR);
+ if (type == TYPE_ANY) {
+ type = dynType;
+ if (type != TYPE_NIL) {
+ handle.writeString(" oor:type=\"");
+ handle.writeString(
+ std::string_view(
+ typeNames[type].begin, typeNames[type].length));
+ handle.writeString("\"");
+ }
+ }
+ handle.writeString("><value");
+ if (dynType == TYPE_NIL) {
+ handle.writeString(" xsi:nil=\"true\"/>");
+ } else {
+ writeValue(handle, type, prop->getValue(components));
+ }
+ handle.writeString("</prop>");
+ }
+ break;
+ case Node::KIND_LOCALIZED_PROPERTY:
+ handle.writeString("<prop oor:name=\"");
+ writeAttributeValue(handle, name);
+ handle.writeString("\" oor:op=\"fuse\">");
+ for (auto const& member : node->getMembers())
+ {
+ writeNode(components, handle, node, member.first, member.second);
+ }
+ handle.writeString("</prop>");
+ break;
+ case Node::KIND_LOCALIZED_VALUE:
+ {
+ handle.writeString("<value");
+ if (!name.empty()) {
+ handle.writeString(" xml:lang=\"");
+ writeAttributeValue(handle, name);
+ handle.writeString("\"");
+ }
+ Type type = static_cast< LocalizedPropertyNode * >(parent.get())->
+ getStaticType();
+ css::uno::Any value(
+ static_cast< LocalizedValueNode * >(node.get())->getValue());
+ Type dynType = getDynamicType(value);
+ assert(dynType != TYPE_ERROR);
+ if (type == TYPE_ANY) {
+ type = dynType;
+ if (type != TYPE_NIL) {
+ handle.writeString(" oor:type=\"");
+ handle.writeString(
+ std::string_view(
+ typeNames[type].begin, typeNames[type].length));
+ handle.writeString("\"");
+ }
+ }
+ if (dynType == TYPE_NIL) {
+ handle.writeString(" xsi:nil=\"true\"/>");
+ } else {
+ writeValue(handle, type, value);
+ }
+ }
+ break;
+ case Node::KIND_GROUP:
+ case Node::KIND_SET:
+ handle.writeString("<node oor:name=\"");
+ writeAttributeValue(handle, name);
+ if (!node->getTemplateName().isEmpty()) { // set member
+ handle.writeString("\" oor:op=\"replace");
+ }
+ handle.writeString("\">");
+ for (auto const& member : node->getMembers())
+ {
+ writeNode(components, handle, node, member.first, member.second);
+ }
+ handle.writeString("</node>");
+ break;
+ case Node::KIND_ROOT:
+ assert(false); // this cannot happen
+ break;
+ }
+}
+
+// helpers to allow sorting of configmgr::Modifications::Node
+typedef std::pair< const OUString, configmgr::Modifications::Node > ModNodePairEntry;
+struct PairEntrySorter
+{
+ bool operator() (const ModNodePairEntry* pValue1, const ModNodePairEntry* pValue2) const
+ {
+ return pValue1->first.compareTo(pValue2->first) < 0;
+ }
+};
+
+void writeModifications(
+ Components & components, TempFile &handle,
+ std::u16string_view parentPathRepresentation,
+ rtl::Reference< Node > const & parent, OUString const & nodeName,
+ rtl::Reference< Node > const & node,
+ Modifications::Node const & modifications)
+{
+ // It is never necessary to write oor:finalized or oor:mandatory attributes,
+ // as they cannot be set via the UNO API.
+ if (modifications.children.empty()) {
+ assert(parent.is());
+ // components themselves have no parent but must have children
+ handle.writeString("<item oor:path=\"");
+ writeAttributeValue(handle, parentPathRepresentation);
+ handle.writeString("\">");
+ if (node.is()) {
+ writeNode(components, handle, parent, nodeName, node);
+ } else {
+ switch (parent->kind()) {
+ case Node::KIND_LOCALIZED_PROPERTY:
+ handle.writeString("<value");
+ if (!nodeName.isEmpty()) {
+ handle.writeString(" xml:lang=\"");
+ writeAttributeValue(handle, nodeName);
+ handle.writeString("\"");
+ }
+ handle.writeString(" oor:op=\"remove\"/>");
+ break;
+ case Node::KIND_GROUP:
+ assert(
+ static_cast< GroupNode * >(parent.get())->isExtensible());
+ handle.writeString("<prop oor:name=\"");
+ writeAttributeValue(handle, nodeName);
+ handle.writeString("\" oor:op=\"remove\"/>");
+ break;
+ case Node::KIND_SET:
+ handle.writeString("<node oor:name=\"");
+ writeAttributeValue(handle, nodeName);
+ handle.writeString("\" oor:op=\"remove\"/>");
+ break;
+ default:
+ assert(false); // this cannot happen
+ break;
+ }
+ }
+ handle.writeString("</item>\n");
+ } else {
+ assert(node.is());
+ OUString pathRep(
+ OUString::Concat(parentPathRepresentation) + "/" +
+ Data::createSegment(node->getTemplateName(), nodeName));
+
+ // copy configmgr::Modifications::Node's to a sortable list. Use pointers
+ // to just reference the data instead of copying it
+ std::vector< const ModNodePairEntry* > ModNodePairEntryVector;
+ ModNodePairEntryVector.reserve(modifications.children.size());
+
+ for (const auto& rCand : modifications.children)
+ {
+ ModNodePairEntryVector.push_back(&rCand);
+ }
+
+ // sort the list
+ std::sort(ModNodePairEntryVector.begin(), ModNodePairEntryVector.end(), PairEntrySorter());
+
+ // now use the list to write entries in sorted order
+ // instead of random as from the unordered map
+ for (const auto & i : ModNodePairEntryVector)
+ {
+ writeModifications(
+ components, handle, pathRep, node, i->first,
+ node->getMember(i->first), i->second);
+ }
+ }
+}
+
+}
+
+void writeAttributeValue(TempFile &handle, std::u16string_view value) {
+ std::size_t i = 0;
+ std::size_t j = i;
+ for (; j != value.size(); ++j) {
+ assert(
+ value[j] == 0x0009 || value[j] == 0x000A || value[j] == 0x000D ||
+ (value[j] >= 0x0020 && value[j] != 0xFFFE && value[j] != 0xFFFF));
+ switch(value[j]) {
+ case '\x09':
+ handle.writeString(convertToUtf8(value.substr(i, j - i)));
+ handle.writeString("&#9;");
+ i = j + 1;
+ break;
+ case '\x0A':
+ handle.writeString(convertToUtf8(value.substr(i, j - i)));
+ handle.writeString("&#xA;");
+ i = j + 1;
+ break;
+ case '\x0D':
+ handle.writeString(convertToUtf8(value.substr(i, j - i)));
+ handle.writeString("&#xD;");
+ i = j + 1;
+ break;
+ case '"':
+ handle.writeString(convertToUtf8(value.substr(i, j - i)));
+ handle.writeString("&quot;");
+ i = j + 1;
+ break;
+ case '&':
+ handle.writeString(convertToUtf8(value.substr(i, j - i)));
+ handle.writeString("&amp;");
+ i = j + 1;
+ break;
+ case '<':
+ handle.writeString(convertToUtf8(value.substr(i, j - i)));
+ handle.writeString("&lt;");
+ i = j + 1;
+ break;
+ default:
+ break;
+ }
+ }
+ handle.writeString(convertToUtf8(value.substr(i, j - i)));
+}
+
+void writeValueContent(TempFile &handle, std::u16string_view value) {
+ std::size_t i = 0;
+ std::size_t j = i;
+ for (; j != value.size(); ++j) {
+ char16_t c = value[j];
+ if ((c < 0x0020 && c != 0x0009 && c != 0x000A && c != 0x000D) ||
+ c == 0xFFFE || c == 0xFFFF)
+ {
+ handle.writeString(convertToUtf8(value.substr(i, j - i)));
+ handle.writeString("<unicode oor:scalar=\"");
+ handle.writeString(OString::number(c));
+ handle.writeString("\"/>");
+ i = j + 1;
+ } else if (c == '\x0D') {
+ handle.writeString(convertToUtf8(value.substr(i, j - i)));
+ handle.writeString("&#xD;");
+ i = j + 1;
+ } else if (c == '&') {
+ handle.writeString(convertToUtf8(value.substr(i, j - i)));
+ handle.writeString("&amp;");
+ i = j + 1;
+ } else if (c == '<') {
+ handle.writeString(convertToUtf8(value.substr(i, j - i)));
+ handle.writeString("&lt;");
+ i = j + 1;
+ } else if (c == '>') {
+ // "MUST, for compatibility, be escaped [...] when it appears in the
+ // string ']]>'":
+ handle.writeString(convertToUtf8(value.substr(i, j - i)));
+ handle.writeString("&gt;");
+ i = j + 1;
+ }
+ }
+ handle.writeString(convertToUtf8(value.substr(i, j - i)));
+}
+
+void writeModFile(
+ Components & components, OUString const & url, Data const & data)
+{
+ sal_Int32 i = url.lastIndexOf('/');
+ assert(i != -1);
+ OUString dir(url.copy(0, i));
+ switch (osl::Directory::createPath(dir)) {
+ case osl::FileBase::E_None:
+ case osl::FileBase::E_EXIST:
+ break;
+ case osl::FileBase::E_ACCES:
+ SAL_INFO(
+ "configmgr",
+ ("cannot create registrymodifications.xcu path (E_ACCES); changes"
+ " will be lost"));
+ return;
+ default:
+ throw css::uno::RuntimeException(
+ "cannot create directory " + dir);
+ }
+ TempFile tmp;
+ switch (osl::FileBase::createTempFile(&dir, &tmp.handle, &tmp.url)) {
+ case osl::FileBase::E_None:
+ break;
+ case osl::FileBase::E_ACCES:
+ SAL_INFO(
+ "configmgr",
+ ("cannot create temp registrymodifications.xcu (E_ACCES); changes"
+ " will be lost"));
+ return;
+ default:
+ throw css::uno::RuntimeException(
+ "cannot create temporary file in " + dir);
+ }
+ tmp.writeString(
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<oor:items"
+ " xmlns:oor=\"http://openoffice.org/2001/registry\""
+ " xmlns:xs=\"http://www.w3.org/2001/XMLSchema\""
+ " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n");
+ //TODO: Do not write back information about those removed items that did not
+ // come from the .xcs/.xcu files, anyway (but had been added dynamically
+ // instead):
+
+ // For profilesafemode it is necessary to detect changes in the
+ // registrymodifications file, this is done based on file size in bytes and crc32.
+ // Unfortunately this write is based on writing unordered map entries, which creates
+ // valid and semantically equal XML-Files, bubt with different crc32 checksums. For
+ // the future usage it will be preferable to have easily comparable config files
+ // which is guaranteed by writing the entries in sorted order. Indeed with this change
+ // (and in the recursive writeModifications call) the same config files get written
+
+ // copy configmgr::Modifications::Node's to a sortable list. Use pointers
+ // to just reference the data instead of copying it
+ std::vector< const ModNodePairEntry* > ModNodePairEntryVector;
+ ModNodePairEntryVector.reserve(data.modifications.getRoot().children.size());
+
+ for (const auto& rCand : data.modifications.getRoot().children)
+ {
+ ModNodePairEntryVector.push_back(&rCand);
+ }
+
+ // sort the list
+ std::sort(ModNodePairEntryVector.begin(), ModNodePairEntryVector.end(), PairEntrySorter());
+
+ // now use the list to write entries in sorted order
+ // instead of random as from the unordered map
+ for (const auto& j : ModNodePairEntryVector)
+ {
+ writeModifications(
+ components, tmp, u"", rtl::Reference< Node >(), j->first,
+ data.getComponents().findNode(Data::NO_LAYER, j->first),
+ j->second);
+ }
+ tmp.writeString("</oor:items>\n");
+ tmp.closeAndRename(url);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/writemodfile.hxx b/configmgr/source/writemodfile.hxx
new file mode 100644
index 000000000..d663321d4
--- /dev/null
+++ b/configmgr/source/writemodfile.hxx
@@ -0,0 +1,63 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <rtl/strbuf.hxx>
+#include <rtl/ustring.hxx>
+#include <osl/file.h>
+
+namespace configmgr {
+
+class Components;
+struct Data;
+
+struct TempFile {
+ OUString url;
+ oslFileHandle handle;
+ bool closed;
+ OStringBuffer buffer;
+
+ TempFile(): handle(nullptr), closed(false) {}
+ ~TempFile();
+ void closeAndRename(const OUString &url);
+ oslFileError flush();
+#ifdef _WIN32
+ oslFileError closeWithoutUnlink();
+#endif
+ void writeString(std::string_view text);
+
+private:
+ TempFile(const TempFile&) = delete;
+ TempFile& operator=(const TempFile&) = delete;
+};
+
+void writeAttributeValue(TempFile &handle, std::u16string_view value);
+void writeValueContent(TempFile &handle, std::u16string_view value);
+
+void writeModFile(
+ Components & components, OUString const & url, Data const & data);
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/xcdparser.cxx b/configmgr/source/xcdparser.cxx
new file mode 100644
index 000000000..a069c6b99
--- /dev/null
+++ b/configmgr/source/xcdparser.cxx
@@ -0,0 +1,174 @@
+/* -*- 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 <climits>
+#include <set>
+
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include <rtl/ustring.hxx>
+#include <xmlreader/span.hxx>
+#include <xmlreader/xmlreader.hxx>
+
+#include "parsemanager.hxx"
+#include "xcdparser.hxx"
+#include "xcsparser.hxx"
+#include "xcuparser.hxx"
+#include "xmldata.hxx"
+
+namespace configmgr {
+
+XcdParser::XcdParser(
+ int layer, std::set< OUString > const & processedDependencies, Data & data):
+ layer_(layer), processedDependencies_(processedDependencies), data_(data),
+ state_(STATE_START), dependencyOptional_(), nesting_()
+{}
+
+XcdParser::~XcdParser() {}
+
+xmlreader::XmlReader::Text XcdParser::getTextMode() {
+ return nestedParser_.is()
+ ? nestedParser_->getTextMode() : xmlreader::XmlReader::Text::NONE;
+}
+
+bool XcdParser::startElement(
+ xmlreader::XmlReader & reader, int nsId, xmlreader::Span const & name,
+ std::set< OUString > const * existingDependencies)
+{
+ if (nestedParser_.is()) {
+ assert(nesting_ != LONG_MAX);
+ ++nesting_;
+ return nestedParser_->startElement(
+ reader, nsId, name, existingDependencies);
+ }
+ switch (state_) {
+ case STATE_START:
+ if (nsId == ParseManager::NAMESPACE_OOR && name == "data") {
+ state_ = STATE_DEPENDENCIES;
+ return true;
+ }
+ break;
+ case STATE_DEPENDENCIES:
+ if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
+ name == "dependency")
+ {
+ if (dependencyFile_.isEmpty()) {
+ dependencyOptional_ = false;
+ xmlreader::Span attrFile;
+ for (;;) {
+ int attrNsId;
+ xmlreader::Span attrLn;
+ if (!reader.nextAttribute(&attrNsId, &attrLn)) {
+ break;
+ }
+ if (attrNsId == xmlreader::XmlReader::NAMESPACE_NONE &&
+ //TODO: _OOR
+ attrLn == "file")
+ {
+ attrFile = reader.getAttributeValue(false);
+ } else if ((attrNsId ==
+ xmlreader::XmlReader::NAMESPACE_NONE) &&
+ attrLn == "optional")
+ {
+ dependencyOptional_ = xmldata::parseBoolean(
+ reader.getAttributeValue(true));
+ }
+ }
+ if (!attrFile.is()) {
+ throw css::uno::RuntimeException(
+ "no dependency file attribute in " + reader.getUrl());
+ }
+ dependencyFile_ = attrFile.convertFromUtf8();
+ if (dependencyFile_.isEmpty()) {
+ throw css::uno::RuntimeException(
+ "bad dependency file attribute in " + reader.getUrl());
+ }
+ }
+ if ((processedDependencies_.find(dependencyFile_) ==
+ processedDependencies_.end()) &&
+ (!dependencyOptional_ || existingDependencies == nullptr ||
+ (existingDependencies->find(dependencyFile_) !=
+ existingDependencies->end())))
+ {
+ return false;
+ }
+ state_ = STATE_DEPENDENCY;
+ dependencyFile_.clear();
+ return true;
+ }
+ state_ = STATE_COMPONENTS;
+ [[fallthrough]];
+ case STATE_COMPONENTS:
+ if (nsId == ParseManager::NAMESPACE_OOR &&
+ name == "component-schema")
+ {
+ nestedParser_ = new XcsParser(layer_, data_);
+ nesting_ = 1;
+ return nestedParser_->startElement(
+ reader, nsId, name, existingDependencies);
+ }
+ if (nsId == ParseManager::NAMESPACE_OOR &&
+ (name == "component-data" || name == "items"))
+ {
+ nestedParser_ = new XcuParser(layer_ + 1, data_, nullptr, nullptr, nullptr);
+ nesting_ = 1;
+ return nestedParser_->startElement(
+ reader, nsId, name, existingDependencies);
+ }
+ break;
+ default: // STATE_DEPENDENCY
+ assert(false); // this cannot happen
+ break;
+ }
+ throw css::uno::RuntimeException(
+ "bad member <" + name.convertFromUtf8() + "> in " + reader.getUrl());
+}
+
+void XcdParser::endElement(xmlreader::XmlReader const & reader) {
+ if (nestedParser_.is()) {
+ nestedParser_->endElement(reader);
+ if (--nesting_ == 0) {
+ nestedParser_.clear();
+ }
+ } else {
+ switch (state_) {
+ case STATE_DEPENDENCY:
+ state_ = STATE_DEPENDENCIES;
+ break;
+ case STATE_DEPENDENCIES:
+ case STATE_COMPONENTS:
+ break;
+ default:
+ assert(false); // this cannot happen
+ break;
+ }
+ }
+}
+
+void XcdParser::characters(xmlreader::Span const & text) {
+ if (nestedParser_.is()) {
+ nestedParser_->characters(text);
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/xcdparser.hxx b/configmgr/source/xcdparser.hxx
new file mode 100644
index 000000000..1ca32931e
--- /dev/null
+++ b/configmgr/source/xcdparser.hxx
@@ -0,0 +1,72 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <set>
+
+#include <rtl/ref.hxx>
+#include <rtl/ustring.hxx>
+#include <xmlreader/xmlreader.hxx>
+
+#include "parser.hxx"
+
+namespace xmlreader { struct Span; }
+
+namespace configmgr {
+
+struct Data;
+
+class XcdParser: public Parser {
+public:
+ XcdParser(
+ int layer, std::set< OUString > const & processedDependencies,
+ Data & data);
+
+private:
+ virtual ~XcdParser() override;
+
+ virtual xmlreader::XmlReader::Text getTextMode() override;
+
+ virtual bool startElement(
+ xmlreader::XmlReader & reader, int nsId, xmlreader::Span const & name,
+ std::set< OUString > const * existingDependencies) override;
+
+ virtual void endElement(xmlreader::XmlReader const & reader) override;
+
+ virtual void characters(xmlreader::Span const & text) override;
+
+ enum State {
+ STATE_START, STATE_DEPENDENCIES, STATE_DEPENDENCY, STATE_COMPONENTS };
+
+ int layer_;
+ std::set< OUString > const & processedDependencies_;
+ Data & data_;
+ State state_;
+ OUString dependencyFile_;
+ bool dependencyOptional_;
+ rtl::Reference< Parser > nestedParser_;
+ long nesting_;
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/xcsparser.cxx b/configmgr/source/xcsparser.cxx
new file mode 100644
index 000000000..947792c0a
--- /dev/null
+++ b/configmgr/source/xcsparser.cxx
@@ -0,0 +1,596 @@
+/* -*- 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 <set>
+
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include <rtl/ref.hxx>
+#include <rtl/strbuf.hxx>
+#include <rtl/string.hxx>
+#include <rtl/ustring.hxx>
+#include <xmlreader/span.hxx>
+#include <xmlreader/xmlreader.hxx>
+
+#include "data.hxx"
+#include "localizedpropertynode.hxx"
+#include "groupnode.hxx"
+#include "node.hxx"
+#include "nodemap.hxx"
+#include "parsemanager.hxx"
+#include "propertynode.hxx"
+#include "setnode.hxx"
+#include "xcsparser.hxx"
+#include "xmldata.hxx"
+
+namespace configmgr {
+
+namespace {
+
+// Conservatively merge a template or component (and its recursive parts) into
+// an existing instance:
+void merge(
+ rtl::Reference< Node > const & original,
+ rtl::Reference< Node > const & update)
+{
+ assert(
+ original.is() && update.is() && original->kind() == update->kind() &&
+ update->getFinalized() == Data::NO_LAYER);
+ if (update->getLayer() < original->getLayer() ||
+ update->getLayer() > original->getFinalized())
+ return;
+
+ switch (original->kind()) {
+ case Node::KIND_PROPERTY:
+ case Node::KIND_LOCALIZED_PROPERTY:
+ case Node::KIND_LOCALIZED_VALUE:
+ break; //TODO: merge certain parts?
+ case Node::KIND_GROUP:
+ for (auto const& updateMember : update->getMembers())
+ {
+ NodeMap & members = original->getMembers();
+ NodeMap::iterator i1(members.find(updateMember.first));
+ if (i1 == members.end()) {
+ if (updateMember.second->kind() == Node::KIND_PROPERTY &&
+ static_cast< GroupNode * >(
+ original.get())->isExtensible())
+ {
+ members.insert(updateMember);
+ }
+ } else if (updateMember.second->kind() == i1->second->kind()) {
+ merge(i1->second, updateMember.second);
+ }
+ }
+ break;
+ case Node::KIND_SET:
+ for (auto const& updateMember : update->getMembers())
+ {
+ NodeMap & members = original->getMembers();
+ NodeMap::iterator i1(members.find(updateMember.first));
+ if (i1 == members.end()) {
+ if (static_cast< SetNode * >(original.get())->
+ isValidTemplate(updateMember.second->getTemplateName()))
+ {
+ members.insert(updateMember);
+ }
+ } else if (updateMember.second->kind() == i1->second->kind() &&
+ (updateMember.second->getTemplateName() ==
+ i1->second->getTemplateName()))
+ {
+ merge(i1->second, updateMember.second);
+ }
+ }
+ break;
+ case Node::KIND_ROOT:
+ assert(false); // this cannot happen
+ break;
+ }
+}
+
+}
+
+XcsParser::XcsParser(int layer, Data & data):
+ valueParser_(layer), data_(data), state_(STATE_START), ignoring_()
+{}
+
+XcsParser::~XcsParser() {}
+
+xmlreader::XmlReader::Text XcsParser::getTextMode() {
+ return valueParser_.getTextMode();
+}
+
+bool XcsParser::startElement(
+ xmlreader::XmlReader & reader, int nsId, xmlreader::Span const & name,
+ std::set< OUString > const * /*existingDependencies*/)
+{
+ if (valueParser_.startElement(reader, nsId, name)) {
+ return true;
+ }
+ if (state_ == STATE_START) {
+ if (nsId == ParseManager::NAMESPACE_OOR &&
+ name == "component-schema")
+ {
+ handleComponentSchema(reader);
+ state_ = STATE_COMPONENT_SCHEMA;
+ ignoring_ = 0;
+ return true;
+ }
+ } else {
+ //TODO: ignoring component-schema import, component-schema uses, and
+ // prop constraints; accepting all four at illegal places (and with
+ // illegal content):
+ if (ignoring_ > 0 ||
+ (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
+ (name == "info" || name == "import" ||
+ name == "uses" || name == "constraints")))
+ {
+ assert(ignoring_ < LONG_MAX);
+ ++ignoring_;
+ return true;
+ }
+ switch (state_) {
+ case STATE_COMPONENT_SCHEMA:
+ if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
+ name == "templates")
+ {
+ state_ = STATE_TEMPLATES;
+ return true;
+ }
+ [[fallthrough]];
+ case STATE_TEMPLATES_DONE:
+ if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
+ name == "component")
+ {
+ state_ = STATE_COMPONENT;
+ assert(elements_.empty());
+ elements_.push(
+ Element(
+ new GroupNode(valueParser_.getLayer(), false, ""),
+ componentName_));
+ return true;
+ }
+ break;
+ case STATE_TEMPLATES:
+ if (elements_.empty()) {
+ if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
+ name == "group")
+ {
+ handleGroup(reader, true);
+ return true;
+ }
+ if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
+ name == "set")
+ {
+ handleSet(reader, true);
+ return true;
+ }
+ break;
+ }
+ [[fallthrough]];
+ case STATE_COMPONENT:
+ assert(!elements_.empty());
+ switch (elements_.top().node->kind()) {
+ case Node::KIND_PROPERTY:
+ case Node::KIND_LOCALIZED_PROPERTY:
+ if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
+ name == "value")
+ {
+ handlePropValue(reader, elements_.top().node);
+ return true;
+ }
+ break;
+ case Node::KIND_GROUP:
+ if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
+ name == "prop")
+ {
+ handleProp(reader);
+ return true;
+ }
+ if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
+ name == "node-ref")
+ {
+ handleNodeRef(reader);
+ return true;
+ }
+ if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
+ name == "group")
+ {
+ handleGroup(reader, false);
+ return true;
+ }
+ if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
+ name == "set")
+ {
+ handleSet(reader, false);
+ return true;
+ }
+ break;
+ case Node::KIND_SET:
+ if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
+ name == "item")
+ {
+ handleSetItem(
+ reader,
+ static_cast< SetNode * >(elements_.top().node.get()));
+ return true;
+ }
+ break;
+ default: // Node::KIND_LOCALIZED_VALUE
+ assert(false); // this cannot happen
+ break;
+ }
+ break;
+ case STATE_COMPONENT_DONE:
+ break;
+ default: // STATE_START
+ assert(false); // this cannot happen
+ break;
+ }
+ }
+ throw css::uno::RuntimeException(
+ "bad member <" + name.convertFromUtf8() + "> in " + reader.getUrl());
+}
+
+void XcsParser::endElement(xmlreader::XmlReader const & reader) {
+ if (valueParser_.endElement()) {
+ return;
+ }
+ if (ignoring_ > 0) {
+ --ignoring_;
+ } else if (!elements_.empty()) {
+ Element top(std::move(elements_.top()));
+ elements_.pop();
+ if (top.node.is()) {
+ if (elements_.empty()) {
+ switch (state_) {
+ case STATE_TEMPLATES:
+ {
+ auto itPair = data_.templates.insert({top.name, top.node});
+ if (!itPair.second) {
+ merge(itPair.first->second, top.node);
+ }
+ }
+ break;
+ case STATE_COMPONENT:
+ {
+ NodeMap & components = data_.getComponents();
+ auto itPair = components.insert({top.name, top.node});
+ if (!itPair.second) {
+ merge(itPair.first->second, top.node);
+ }
+ state_ = STATE_COMPONENT_DONE;
+ }
+ break;
+ default:
+ assert(false);
+ throw css::uno::RuntimeException(
+ "this cannot happen");
+ }
+ } else {
+ if (!elements_.top().node->getMembers().insert(
+ NodeMap::value_type(top.name, top.node)).second)
+ {
+ throw css::uno::RuntimeException(
+ "duplicate " + top.name + " in " + reader.getUrl());
+ }
+ }
+ }
+ } else {
+ switch (state_) {
+ case STATE_COMPONENT_SCHEMA:
+ // To support old, broken extensions with .xcs files that contain
+ // empty <component-schema> elements:
+ state_ = STATE_COMPONENT_DONE;
+ break;
+ case STATE_TEMPLATES:
+ state_ = STATE_TEMPLATES_DONE;
+ break;
+ case STATE_TEMPLATES_DONE:
+ throw css::uno::RuntimeException(
+ "no component element in " + reader.getUrl());
+ case STATE_COMPONENT_DONE:
+ break;
+ default:
+ assert(false); // this cannot happen
+ }
+ }
+}
+
+void XcsParser::characters(xmlreader::Span const & text) {
+ valueParser_.characters(text);
+}
+
+void XcsParser::handleComponentSchema(xmlreader::XmlReader & reader) {
+ //TODO: oor:version, xml:lang attributes
+ OStringBuffer buf(256);
+ buf.append('.');
+ bool hasPackage = false;
+ bool hasName = false;
+ for (;;) {
+ int attrNsId;
+ xmlreader::Span attrLn;
+ if (!reader.nextAttribute(&attrNsId, &attrLn)) {
+ break;
+ }
+ if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn == "package")
+ {
+ if (hasPackage) {
+ throw css::uno::RuntimeException(
+ "multiple component-schema package attributes in " +
+ reader.getUrl());
+ }
+ hasPackage = true;
+ xmlreader::Span s(reader.getAttributeValue(false));
+ buf.insert(0, s.begin, s.length);
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "name")
+ {
+ if (hasName) {
+ throw css::uno::RuntimeException(
+ "multiple component-schema name attributes in " +
+ reader.getUrl());
+ }
+ hasName = true;
+ xmlreader::Span s(reader.getAttributeValue(false));
+ buf.append(s.begin, s.length);
+ }
+ }
+ if (!hasPackage) {
+ throw css::uno::RuntimeException(
+ "no component-schema package attribute in " + reader.getUrl());
+ }
+ if (!hasName) {
+ throw css::uno::RuntimeException(
+ "no component-schema name attribute in " + reader.getUrl());
+ }
+ componentName_ = xmlreader::Span(buf.getStr(), buf.getLength()).
+ convertFromUtf8();
+}
+
+void XcsParser::handleNodeRef(xmlreader::XmlReader & reader) {
+ bool hasName = false;
+ OUString name;
+ OUString component(componentName_);
+ bool hasNodeType = false;
+ OUString nodeType;
+ for (;;) {
+ int attrNsId;
+ xmlreader::Span attrLn;
+ if (!reader.nextAttribute(&attrNsId, &attrLn)) {
+ break;
+ }
+ if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn == "name") {
+ hasName = true;
+ name = reader.getAttributeValue(false).convertFromUtf8();
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "component")
+ {
+ component = reader.getAttributeValue(false).convertFromUtf8();
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "node-type")
+ {
+ hasNodeType = true;
+ nodeType = reader.getAttributeValue(false).convertFromUtf8();
+ }
+ }
+ if (!hasName) {
+ throw css::uno::RuntimeException(
+ "no node-ref name attribute in " + reader.getUrl());
+ }
+ rtl::Reference< Node > tmpl(
+ data_.getTemplate(
+ valueParser_.getLayer(),
+ xmldata::parseTemplateReference(
+ component, hasNodeType, nodeType, nullptr)));
+ if (!tmpl.is()) {
+ //TODO: this can erroneously happen as long as import/uses attributes
+ // are not correctly processed
+ throw css::uno::RuntimeException(
+ "unknown node-ref " + name + " in " + reader.getUrl());
+ }
+ rtl::Reference< Node > node(tmpl->clone(false));
+ node->setLayer(valueParser_.getLayer());
+ elements_.push(Element(node, name));
+}
+
+void XcsParser::handleProp(xmlreader::XmlReader & reader) {
+ bool hasName = false;
+ OUString name;
+ valueParser_.type_ = TYPE_ERROR;
+ bool localized = false;
+ bool nillable = true;
+ for (;;) {
+ int attrNsId;
+ xmlreader::Span attrLn;
+ if (!reader.nextAttribute(&attrNsId, &attrLn)) {
+ break;
+ }
+ if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn == "name") {
+ hasName = true;
+ name = reader.getAttributeValue(false).convertFromUtf8();
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "type")
+ {
+ valueParser_.type_ = xmldata::parseType(
+ reader, reader.getAttributeValue(true));
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "localized")
+ {
+ localized = xmldata::parseBoolean(reader.getAttributeValue(true));
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "nillable")
+ {
+ nillable = xmldata::parseBoolean(reader.getAttributeValue(true));
+ }
+ }
+ if (!hasName) {
+ throw css::uno::RuntimeException(
+ "no prop name attribute in " + reader.getUrl());
+ }
+ if (valueParser_.type_ == TYPE_ERROR) {
+ throw css::uno::RuntimeException(
+ "no prop type attribute in " + reader.getUrl());
+ }
+ elements_.push(
+ Element(
+ (localized
+ ? rtl::Reference< Node >(
+ new LocalizedPropertyNode(
+ valueParser_.getLayer(), valueParser_.type_, nillable))
+ : rtl::Reference< Node >(
+ new PropertyNode(
+ valueParser_.getLayer(), valueParser_.type_, nillable,
+ css::uno::Any(), false))),
+ name));
+}
+
+void XcsParser::handlePropValue(
+ xmlreader::XmlReader & reader, rtl::Reference< Node > const & property)
+{
+ xmlreader::Span attrSeparator;
+ for (;;) {
+ int attrNsId;
+ xmlreader::Span attrLn;
+ if (!reader.nextAttribute(&attrNsId, &attrLn)) {
+ break;
+ }
+ if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "separator")
+ {
+ attrSeparator = reader.getAttributeValue(false);
+ if (attrSeparator.length == 0) {
+ throw css::uno::RuntimeException(
+ "bad oor:separator attribute in " + reader.getUrl());
+ }
+ }
+ }
+ valueParser_.separator_ = OString(
+ attrSeparator.begin, attrSeparator.length);
+ valueParser_.start(property);
+}
+
+void XcsParser::handleGroup(xmlreader::XmlReader & reader, bool isTemplate) {
+ bool hasName = false;
+ OUString name;
+ bool extensible = false;
+ for (;;) {
+ int attrNsId;
+ xmlreader::Span attrLn;
+ if (!reader.nextAttribute(&attrNsId, &attrLn)) {
+ break;
+ }
+ if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn == "name") {
+ hasName = true;
+ name = reader.getAttributeValue(false).convertFromUtf8();
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "extensible")
+ {
+ extensible = xmldata::parseBoolean(reader.getAttributeValue(true));
+ }
+ }
+ if (!hasName) {
+ throw css::uno::RuntimeException(
+ "no group name attribute in " + reader.getUrl());
+ }
+ if (isTemplate) {
+ name = Data::fullTemplateName(componentName_, name);
+ }
+ elements_.push(
+ Element(
+ new GroupNode(
+ valueParser_.getLayer(), extensible,
+ isTemplate ? name : OUString()),
+ name));
+}
+
+void XcsParser::handleSet(xmlreader::XmlReader & reader, bool isTemplate) {
+ bool hasName = false;
+ OUString name;
+ OUString component(componentName_);
+ bool hasNodeType = false;
+ OUString nodeType;
+ for (;;) {
+ int attrNsId;
+ xmlreader::Span attrLn;
+ if (!reader.nextAttribute(&attrNsId, &attrLn)) {
+ break;
+ }
+ if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn == "name") {
+ hasName = true;
+ name = reader.getAttributeValue(false).convertFromUtf8();
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "component")
+ {
+ component = reader.getAttributeValue(false).convertFromUtf8();
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "node-type")
+ {
+ hasNodeType = true;
+ nodeType = reader.getAttributeValue(false).convertFromUtf8();
+ }
+ }
+ if (!hasName) {
+ throw css::uno::RuntimeException(
+ "no set name attribute in " + reader.getUrl());
+ }
+ if (isTemplate) {
+ name = Data::fullTemplateName(componentName_, name);
+ }
+ elements_.push(
+ Element(
+ new SetNode(
+ valueParser_.getLayer(),
+ xmldata::parseTemplateReference(
+ component, hasNodeType, nodeType, nullptr),
+ isTemplate ? name : OUString()),
+ name));
+}
+
+void XcsParser::handleSetItem(xmlreader::XmlReader & reader, SetNode * set) {
+ OUString component(componentName_);
+ bool hasNodeType = false;
+ OUString nodeType;
+ for (;;) {
+ int attrNsId;
+ xmlreader::Span attrLn;
+ if (!reader.nextAttribute(&attrNsId, &attrLn)) {
+ break;
+ }
+ if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "component")
+ {
+ component = reader.getAttributeValue(false).convertFromUtf8();
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "node-type")
+ {
+ hasNodeType = true;
+ nodeType = reader.getAttributeValue(false).convertFromUtf8();
+ }
+ }
+ set->getAdditionalTemplateNames().push_back(
+ xmldata::parseTemplateReference(component, hasNodeType, nodeType, nullptr));
+ elements_.push(Element(rtl::Reference< Node >(), ""));
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/xcsparser.hxx b/configmgr/source/xcsparser.hxx
new file mode 100644
index 000000000..f2c5c7742
--- /dev/null
+++ b/configmgr/source/xcsparser.hxx
@@ -0,0 +1,101 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <set>
+#include <stack>
+
+#include <rtl/ref.hxx>
+#include <rtl/ustring.hxx>
+#include <utility>
+#include <xmlreader/xmlreader.hxx>
+
+#include "node.hxx"
+#include "parser.hxx"
+#include "valueparser.hxx"
+
+namespace xmlreader { struct Span; }
+
+namespace configmgr {
+
+class SetNode;
+struct Data;
+
+class XcsParser: public Parser {
+public:
+ XcsParser(int layer, Data & data);
+
+private:
+ virtual ~XcsParser() override;
+
+ virtual xmlreader::XmlReader::Text getTextMode() override;
+
+ virtual bool startElement(
+ xmlreader::XmlReader & reader, int nsId, xmlreader::Span const & name,
+ std::set< OUString > const * existingDependencies) override;
+
+ virtual void endElement(xmlreader::XmlReader const & reader) override;
+
+ virtual void characters(xmlreader::Span const & text) override;
+
+ void handleComponentSchema(xmlreader::XmlReader & reader);
+
+ void handleNodeRef(xmlreader::XmlReader & reader);
+
+ void handleProp(xmlreader::XmlReader & reader);
+
+ void handlePropValue(
+ xmlreader::XmlReader & reader, rtl::Reference< Node > const & property);
+
+ void handleGroup(xmlreader::XmlReader & reader, bool isTemplate);
+
+ void handleSet(xmlreader::XmlReader & reader, bool isTemplate);
+
+ void handleSetItem(xmlreader::XmlReader & reader, SetNode * set);
+
+ enum State {
+ STATE_START, STATE_COMPONENT_SCHEMA, STATE_TEMPLATES,
+ STATE_TEMPLATES_DONE, STATE_COMPONENT, STATE_COMPONENT_DONE };
+
+ struct Element {
+ rtl::Reference< Node > node;
+ OUString name;
+
+ Element(
+ rtl::Reference< Node > theNode,
+ OUString theName):
+ node(std::move(theNode)), name(std::move(theName)) {}
+ };
+
+ typedef std::stack< Element > ElementStack;
+
+ ValueParser valueParser_;
+ Data & data_;
+ OUString componentName_;
+ State state_;
+ long ignoring_;
+ ElementStack elements_;
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/xcuparser.cxx b/configmgr/source/xcuparser.cxx
new file mode 100644
index 000000000..af21518ab
--- /dev/null
+++ b/configmgr/source/xcuparser.cxx
@@ -0,0 +1,968 @@
+/* -*- 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 <algorithm>
+#include <cassert>
+#include <set>
+
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include <rtl/ref.hxx>
+#include <rtl/strbuf.hxx>
+#include <rtl/string.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <xmlreader/span.hxx>
+#include <xmlreader/xmlreader.hxx>
+
+#include "data.hxx"
+#include "localizedpropertynode.hxx"
+#include "localizedvaluenode.hxx"
+#include "groupnode.hxx"
+#include "modifications.hxx"
+#include "node.hxx"
+#include "nodemap.hxx"
+#include "parsemanager.hxx"
+#include "partial.hxx"
+#include "propertynode.hxx"
+#include "setnode.hxx"
+#include "xcuparser.hxx"
+#include "xmldata.hxx"
+
+namespace configmgr {
+
+XcuParser::XcuParser(
+ int layer, Data & data, Partial const * partial,
+ Modifications * broadcastModifications, Additions * additions):
+ valueParser_(layer), data_(data),
+ partial_(partial), broadcastModifications_(broadcastModifications),
+ additions_(additions), recordModifications_(layer == Data::NO_LAYER),
+ trackPath_(
+ partial_ != nullptr || broadcastModifications_ != nullptr || additions_ != nullptr ||
+ recordModifications_)
+{}
+
+XcuParser::~XcuParser() {}
+
+xmlreader::XmlReader::Text XcuParser::getTextMode() {
+ return valueParser_.getTextMode();
+}
+
+bool XcuParser::startElement(
+ xmlreader::XmlReader & reader, int nsId, xmlreader::Span const & name,
+ std::set< OUString > const * /*existingDependencies*/)
+{
+ if (valueParser_.startElement(reader, nsId, name)) {
+ return true;
+ }
+ if (state_.empty()) {
+ if (nsId == ParseManager::NAMESPACE_OOR &&
+ name == "component-data")
+ {
+ handleComponentData(reader);
+ } else if (nsId == ParseManager::NAMESPACE_OOR && name == "items")
+ {
+ state_.push(State::Modify(rtl::Reference< Node >()));
+ } else {
+ throw css::uno::RuntimeException(
+ "bad root element <" + name.convertFromUtf8() + "> in " +
+ reader.getUrl());
+ }
+ } else if (state_.top().ignore) {
+ state_.push(State::Ignore(false));
+ } else if (!state_.top().node.is()) {
+ if (nsId != xmlreader::XmlReader::NAMESPACE_NONE || name != "item")
+ {
+ throw css::uno::RuntimeException(
+ "bad items node member <" + name.convertFromUtf8() + "> in " +
+ reader.getUrl());
+ }
+ handleItem(reader);
+ } else {
+ switch (state_.top().node->kind()) {
+ case Node::KIND_PROPERTY:
+ if (nsId != xmlreader::XmlReader::NAMESPACE_NONE ||
+ name != "value")
+ {
+ throw css::uno::RuntimeException(
+ "bad property node member <" + name.convertFromUtf8() +
+ "> in " + reader.getUrl());
+ }
+ handlePropValue(
+ reader,
+ static_cast< PropertyNode * >(state_.top().node.get()));
+ break;
+ case Node::KIND_LOCALIZED_PROPERTY:
+ if (nsId != xmlreader::XmlReader::NAMESPACE_NONE ||
+ name != "value")
+ {
+ throw css::uno::RuntimeException(
+ "bad localized property node member <" +
+ name.convertFromUtf8() + "> in " + reader.getUrl());
+ }
+ handleLocpropValue(
+ reader,
+ static_cast< LocalizedPropertyNode * >(
+ state_.top().node.get()));
+ break;
+ case Node::KIND_LOCALIZED_VALUE:
+ throw css::uno::RuntimeException(
+ "bad member <" + name.convertFromUtf8() + "> in " +
+ reader.getUrl());
+ case Node::KIND_GROUP:
+ if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
+ name == "prop")
+ {
+ handleGroupProp(
+ reader,
+ static_cast< GroupNode * >(state_.top().node.get()));
+ } else if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
+ name == "node")
+ {
+ handleGroupNode(reader, state_.top().node);
+ } else {
+ throw css::uno::RuntimeException(
+ "bad group node member <" + name.convertFromUtf8() +
+ "> in " + reader.getUrl());
+ }
+ break;
+ case Node::KIND_SET:
+ if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
+ name == "node")
+ {
+ handleSetNode(
+ reader, static_cast< SetNode * >(state_.top().node.get()));
+ } else if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
+ name == "prop")
+ {
+ SAL_WARN(
+ "configmgr",
+ "bad set node <prop> member in \"" << reader.getUrl()
+ << '"');
+ state_.push(State::Ignore(false));
+ } else {
+ throw css::uno::RuntimeException(
+ "bad set node member <" + name.convertFromUtf8() +
+ "> in " + reader.getUrl());
+ }
+ break;
+ case Node::KIND_ROOT:
+ assert(false); // this cannot happen
+ break;
+ }
+ }
+ return true;
+}
+
+void XcuParser::endElement(xmlreader::XmlReader const &) {
+ if (valueParser_.endElement()) {
+ return;
+ }
+ assert(!state_.empty());
+ bool pop = state_.top().pop;
+ rtl::Reference< Node > insert;
+ OUString name;
+ if (state_.top().insert) {
+ insert = state_.top().node;
+ assert(insert.is());
+ name = state_.top().name;
+ }
+ state_.pop();
+ if (insert.is()) {
+ assert(!state_.empty() && state_.top().node.is());
+ state_.top().node->getMembers()[name] = insert;
+ }
+ if (pop && !path_.empty()) {
+ path_.pop_back();
+ // </item> will pop less than <item> pushed, but that is harmless,
+ // as the next <item> will reset path_
+ }
+}
+
+void XcuParser::characters(xmlreader::Span const & text) {
+ valueParser_.characters(text);
+}
+
+XcuParser::Operation XcuParser::parseOperation(xmlreader::Span const & text) {
+ assert(text.is());
+ if (text == "modify") {
+ return OPERATION_MODIFY;
+ }
+ if (text == "replace") {
+ return OPERATION_REPLACE;
+ }
+ if (text == "fuse") {
+ return OPERATION_FUSE;
+ }
+ if (text == "remove") {
+ return OPERATION_REMOVE;
+ }
+ throw css::uno::RuntimeException(
+ "invalid op " + text.convertFromUtf8());
+}
+
+void XcuParser::handleComponentData(xmlreader::XmlReader & reader) {
+ OStringBuffer buf(256);
+ buf.append('.');
+ bool hasPackage = false;
+ bool hasName = false;
+ Operation op = OPERATION_MODIFY;
+ bool finalized = false;
+ for (;;) {
+ int attrNsId;
+ xmlreader::Span attrLn;
+ if (!reader.nextAttribute(&attrNsId, &attrLn)) {
+ break;
+ }
+ if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn == "package")
+ {
+ if (hasPackage) {
+ throw css::uno::RuntimeException(
+ "multiple component-update package attributes in " +
+ reader.getUrl());
+ }
+ hasPackage = true;
+ xmlreader::Span s(reader.getAttributeValue(false));
+ buf.insert(0, s.begin, s.length);
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "name")
+ {
+ if (hasName) {
+ throw css::uno::RuntimeException(
+ "multiple component-update name attributes in " +
+ reader.getUrl());
+ }
+ hasName = true;
+ xmlreader::Span s(reader.getAttributeValue(false));
+ buf.append(s.begin, s.length);
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "op")
+ {
+ op = parseOperation(reader.getAttributeValue(true));
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "finalized")
+ {
+ finalized = xmldata::parseBoolean(reader.getAttributeValue(true));
+ }
+ }
+ if (!hasPackage) {
+ throw css::uno::RuntimeException(
+ "no component-data package attribute in " + reader.getUrl());
+ }
+ if (!hasName) {
+ throw css::uno::RuntimeException(
+ "no component-data name attribute in " + reader.getUrl());
+ }
+ componentName_ = xmlreader::Span(buf.getStr(), buf.getLength()).
+ convertFromUtf8();
+ if (trackPath_) {
+ assert(path_.empty());
+ path_.push_back(componentName_);
+ if (partial_ != nullptr && partial_->contains(path_) == Partial::CONTAINS_NOT)
+ {
+ state_.push(State::Ignore(true));
+ return;
+ }
+ }
+ rtl::Reference< Node > node(
+ data_.getComponents().findNode(valueParser_.getLayer(),
+ componentName_));
+ if (!node.is()) {
+ SAL_WARN(
+ "configmgr",
+ "unknown component \"" << componentName_ << "\" in \""
+ << reader.getUrl() << '"');
+ state_.push(State::Ignore(true));
+ return;
+ }
+ switch (op) {
+ case OPERATION_MODIFY:
+ case OPERATION_FUSE:
+ break;
+ default:
+ throw css::uno::RuntimeException(
+ "invalid operation on root node in " + reader.getUrl());
+ }
+ int finalizedLayer = std::min(
+ finalized ? valueParser_.getLayer() : Data::NO_LAYER,
+ node->getFinalized());
+ node->setFinalized(finalizedLayer);
+ if (finalizedLayer < valueParser_.getLayer()) {
+ state_.push(State::Ignore(true));
+ return;
+ }
+ state_.push(State::Modify(node));
+}
+
+void XcuParser::handleItem(xmlreader::XmlReader & reader) {
+ xmlreader::Span attrPath;
+ for (;;) {
+ int attrNsId;
+ xmlreader::Span attrLn;
+ if (!reader.nextAttribute(&attrNsId, &attrLn)) {
+ break;
+ }
+ if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn == "path") {
+ attrPath = reader.getAttributeValue(false);
+ }
+ }
+ if (!attrPath.is()) {
+ throw css::uno::RuntimeException(
+ "missing path attribute in " + reader.getUrl());
+ }
+ OUString path(attrPath.convertFromUtf8());
+ int finalizedLayer;
+ rtl::Reference< Node > node(
+ data_.resolvePathRepresentation(
+ path, nullptr, &path_, &finalizedLayer));
+ if (!node.is()) {
+ SAL_WARN(
+ "configmgr",
+ "unknown item \"" << path << "\" in \"" << reader.getUrl() << '"');
+ state_.push(State::Ignore(true));
+ return;
+ }
+ assert(!path_.empty());
+ componentName_ = path_.front();
+ if (trackPath_) {
+ if (partial_ != nullptr && partial_->contains(path_) == Partial::CONTAINS_NOT)
+ {
+ state_.push(State::Ignore(true));
+ return;
+ }
+ } else {
+ path_.clear();
+ }
+ switch (node->kind()) {
+ case Node::KIND_PROPERTY:
+ case Node::KIND_LOCALIZED_VALUE:
+ SAL_WARN(
+ "configmgr",
+ "item of bad type \"" << path << "\" in \"" << reader.getUrl()
+ << '"');
+ state_.push(State::Ignore(true));
+ return;
+ case Node::KIND_LOCALIZED_PROPERTY:
+ valueParser_.type_ = static_cast< LocalizedPropertyNode * >(
+ node.get())->getStaticType();
+ break;
+ default:
+ break;
+ }
+ if (finalizedLayer < valueParser_.getLayer()) {
+ state_.push(State::Ignore(true));
+ return;
+ }
+ state_.push(State::Modify(node));
+}
+
+void XcuParser::handlePropValue(
+ xmlreader::XmlReader & reader, PropertyNode * prop)
+ {
+ bool nil = false;
+ OString separator;
+ OUString external;
+ for (;;) {
+ int attrNsId;
+ xmlreader::Span attrLn;
+ if (!reader.nextAttribute(&attrNsId, &attrLn)) {
+ break;
+ }
+ if (attrNsId == ParseManager::NAMESPACE_XSI && attrLn == "nil") {
+ nil = xmldata::parseBoolean(reader.getAttributeValue(true));
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "type")
+ {
+ Type type = xmldata::parseType(
+ reader, reader.getAttributeValue(true));
+ if (valueParser_.type_ != TYPE_ANY && type != valueParser_.type_) {
+ throw css::uno::RuntimeException(
+ "invalid value type in " + reader.getUrl());
+ }
+ valueParser_.type_ = type;
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "separator")
+ {
+ xmlreader::Span s(reader.getAttributeValue(false));
+ if (s.length == 0) {
+ throw css::uno::RuntimeException(
+ "bad oor:separator attribute in " + reader.getUrl());
+ }
+ separator = OString(s.begin, s.length);
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "external")
+ {
+ external = reader.getAttributeValue(true).convertFromUtf8();
+ if (external.isEmpty()) {
+ throw css::uno::RuntimeException(
+ "bad oor:external attribute value in " + reader.getUrl());
+ }
+ }
+ }
+ if (nil) {
+ if (!prop->isNillable()) {
+ throw css::uno::RuntimeException(
+ "xsi:nil attribute for non-nillable prop in " + reader.getUrl());
+ }
+ if (!external.isEmpty()) {
+ throw css::uno::RuntimeException(
+ "xsi:nil and oor:external attributes for prop in " +
+ reader.getUrl());
+ }
+ prop->setValue(valueParser_.getLayer(), css::uno::Any());
+ state_.push(State::Ignore(false));
+ } else if (external.isEmpty()) {
+ valueParser_.separator_ = separator;
+ valueParser_.start(prop);
+ } else {
+ prop->setExternal(valueParser_.getLayer(), external);
+ state_.push(State::Ignore(false));
+ }
+}
+
+void XcuParser::handleLocpropValue(
+ xmlreader::XmlReader & reader, LocalizedPropertyNode * locprop)
+{
+ OUString name;
+ bool nil = false;
+ OString separator;
+ Operation op = OPERATION_FUSE;
+ for (;;) {
+ int attrNsId;
+ xmlreader::Span attrLn;
+ if (!reader.nextAttribute(&attrNsId, &attrLn)) {
+ break;
+ }
+ if (attrNsId == xmlreader::XmlReader::NAMESPACE_XML &&
+ attrLn == "lang")
+ {
+ name = reader.getAttributeValue(false).convertFromUtf8();
+ } else if (attrNsId == ParseManager::NAMESPACE_XSI &&
+ attrLn == "nil")
+ {
+ nil = xmldata::parseBoolean(reader.getAttributeValue(true));
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "type")
+ {
+ Type type = xmldata::parseType(
+ reader, reader.getAttributeValue(true));
+ if (valueParser_.type_ != TYPE_ANY && type != valueParser_.type_) {
+ throw css::uno::RuntimeException(
+ "invalid value type in " + reader.getUrl());
+ }
+ valueParser_.type_ = type;
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "separator")
+ {
+ xmlreader::Span s(reader.getAttributeValue(false));
+ if (s.length == 0) {
+ throw css::uno::RuntimeException(
+ "bad oor:separator attribute in " + reader.getUrl());
+ }
+ separator = OString(s.begin, s.length);
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "op")
+ {
+ op = parseOperation(reader.getAttributeValue(true));
+ }
+ }
+ if (trackPath_) {
+ path_.push_back(name);
+ if (partial_ != nullptr &&
+ partial_->contains(path_) != Partial::CONTAINS_NODE)
+ {
+ state_.push(State::Ignore(true));
+ return;
+ }
+ }
+ NodeMap & members = locprop->getMembers();
+ NodeMap::iterator i(members.find(name));
+ if (i != members.end() && i->second->getLayer() > valueParser_.getLayer()) {
+ state_.push(State::Ignore(true));
+ return;
+ }
+ if (nil && !locprop->isNillable()) {
+ throw css::uno::RuntimeException(
+ "xsi:nil attribute for non-nillable prop in " + reader.getUrl());
+ }
+ switch (op) {
+ case OPERATION_FUSE:
+ {
+ bool pop = false;
+ if (nil) {
+ if (i == members.end()) {
+ members[name] = new LocalizedValueNode(
+ valueParser_.getLayer(), css::uno::Any());
+ } else {
+ static_cast< LocalizedValueNode * >(
+ i->second.get())->setValue(
+ valueParser_.getLayer(), css::uno::Any());
+ }
+ state_.push(State::Ignore(true));
+ } else {
+ valueParser_.separator_ = separator;
+ valueParser_.start(locprop, name);
+ pop = true;
+ }
+ if (trackPath_) {
+ recordModification(false);
+ if (pop) {
+ path_.pop_back();
+ }
+ }
+ }
+ break;
+ case OPERATION_REMOVE:
+ //TODO: only allow if parent.op == OPERATION_FUSE
+ //TODO: disallow removing when e.g. lang=""?
+ if (i != members.end()) {
+ members.erase(i);
+ }
+ state_.push(State::Ignore(true));
+ recordModification(false);
+ break;
+ default:
+ throw css::uno::RuntimeException(
+ "bad op attribute for value element in " + reader.getUrl());
+ }
+}
+
+void XcuParser::handleGroupProp(
+ xmlreader::XmlReader & reader, GroupNode * group)
+{
+ bool hasName = false;
+ OUString name;
+ Type type = TYPE_ERROR;
+ Operation op = OPERATION_MODIFY;
+ bool finalized = false;
+ for (;;) {
+ int attrNsId;
+ xmlreader::Span attrLn;
+ if (!reader.nextAttribute(&attrNsId, &attrLn)) {
+ break;
+ }
+ if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn == "name") {
+ hasName = true;
+ name = reader.getAttributeValue(false).convertFromUtf8();
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "type")
+ {
+ type = xmldata::parseType(reader, reader.getAttributeValue(true));
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "op")
+ {
+ op = parseOperation(reader.getAttributeValue(true));
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "finalized")
+ {
+ finalized = xmldata::parseBoolean(reader.getAttributeValue(true));
+ }
+ }
+ if (!hasName) {
+ throw css::uno::RuntimeException(
+ "no prop name attribute in " + reader.getUrl());
+ }
+ if (trackPath_) {
+ path_.push_back(name);
+ //TODO: This ignores locprop values for which specific include paths
+ // exist (i.e., for which contains(locprop path) = CONTAINS_SUBNODES):
+ if (partial_ != nullptr &&
+ partial_->contains(path_) != Partial::CONTAINS_NODE)
+ {
+ state_.push(State::Ignore(true));
+ return;
+ }
+ }
+ NodeMap & members = group->getMembers();
+ NodeMap::iterator i(members.find(name));
+ if (i == members.end()) {
+ handleUnknownGroupProp(reader, group, name, type, op, finalized);
+ } else {
+ switch (i->second->kind()) {
+ case Node::KIND_PROPERTY:
+ handlePlainGroupProp(reader, group, i, name, type, op, finalized);
+ break;
+ case Node::KIND_LOCALIZED_PROPERTY:
+ handleLocalizedGroupProp(
+ reader,
+ static_cast< LocalizedPropertyNode * >(i->second.get()), name,
+ type, op, finalized);
+ break;
+ default:
+ throw css::uno::RuntimeException(
+ "inappropriate prop " + name + " in " + reader.getUrl());
+ }
+ }
+}
+
+void XcuParser::handleUnknownGroupProp(
+ xmlreader::XmlReader const & reader, GroupNode const * group,
+ OUString const & name, Type type, Operation operation, bool finalized)
+{
+ switch (operation) {
+ case OPERATION_REPLACE:
+ case OPERATION_FUSE:
+ if (group->isExtensible()) {
+ if (type == TYPE_ERROR) {
+ throw css::uno::RuntimeException(
+ "missing type attribute for prop " + name + " in " +
+ reader.getUrl());
+ }
+ valueParser_.type_ = type;
+ rtl::Reference< Node > prop(
+ new PropertyNode(
+ valueParser_.getLayer(), TYPE_ANY, true, css::uno::Any(),
+ true));
+ if (finalized) {
+ prop->setFinalized(valueParser_.getLayer());
+ }
+ state_.push(State::Insert(prop, name));
+ recordModification(false);
+ break;
+ }
+ [[fallthrough]];
+ default:
+ SAL_WARN(
+ "configmgr",
+ "unknown property \"" << name << "\" in \"" << reader.getUrl()
+ << '"');
+ state_.push(State::Ignore(true));
+ break;
+ }
+}
+
+void XcuParser::handlePlainGroupProp(
+ xmlreader::XmlReader const & reader, GroupNode * group,
+ NodeMap::iterator const & propertyIndex, std::u16string_view name,
+ Type type, Operation operation, bool finalized)
+{
+ PropertyNode * property = static_cast< PropertyNode * >(
+ propertyIndex->second.get());
+ if (property->getLayer() > valueParser_.getLayer()) {
+ state_.push(State::Ignore(true));
+ return;
+ }
+ int finalizedLayer = std::min(
+ finalized ? valueParser_.getLayer() : Data::NO_LAYER,
+ property->getFinalized());
+ property->setFinalized(finalizedLayer);
+ if (finalizedLayer < valueParser_.getLayer()) {
+ state_.push(State::Ignore(true));
+ return;
+ }
+ if (type != TYPE_ERROR && property->getStaticType() != TYPE_ANY &&
+ type != property->getStaticType())
+ {
+ throw css::uno::RuntimeException(
+ OUString::Concat("invalid type for prop ") + name + " in " + reader.getUrl());
+ }
+ valueParser_.type_ = type == TYPE_ERROR ? property->getStaticType() : type;
+ switch (operation) {
+ case OPERATION_MODIFY:
+ case OPERATION_REPLACE:
+ case OPERATION_FUSE:
+ state_.push(State::Modify(property));
+ recordModification(false);
+ break;
+ case OPERATION_REMOVE:
+ if (!property->isExtension()) {
+ throw css::uno::RuntimeException(
+ OUString::Concat("invalid remove of non-extension prop ") + name + " in " +
+ reader.getUrl());
+ }
+ group->getMembers().erase(propertyIndex);
+ state_.push(State::Ignore(true));
+ recordModification(false);
+ break;
+ }
+}
+
+void XcuParser::handleLocalizedGroupProp(
+ xmlreader::XmlReader const & reader, LocalizedPropertyNode * property,
+ OUString const & name, Type type, Operation operation, bool finalized)
+{
+ if (property->getLayer() > valueParser_.getLayer()) {
+ state_.push(State::Ignore(true));
+ return;
+ }
+ int finalizedLayer = std::min(
+ finalized ? valueParser_.getLayer() : Data::NO_LAYER,
+ property->getFinalized());
+ property->setFinalized(finalizedLayer);
+ if (finalizedLayer < valueParser_.getLayer()) {
+ state_.push(State::Ignore(true));
+ return;
+ }
+ if (type != TYPE_ERROR && property->getStaticType() != TYPE_ANY &&
+ type != property->getStaticType())
+ {
+ throw css::uno::RuntimeException(
+ "invalid type for prop " + name + " in " + reader.getUrl());
+ }
+ valueParser_.type_ = type == TYPE_ERROR ? property->getStaticType() : type;
+ switch (operation) {
+ case OPERATION_MODIFY:
+ case OPERATION_FUSE:
+ state_.push(State::Modify(property));
+ break;
+ case OPERATION_REPLACE:
+ {
+ rtl::Reference< Node > replacement(
+ new LocalizedPropertyNode(
+ valueParser_.getLayer(), property->getStaticType(),
+ property->isNillable()));
+ replacement->setFinalized(property->getFinalized());
+ state_.push(State::Insert(replacement, name));
+ recordModification(false);
+ }
+ break;
+ case OPERATION_REMOVE:
+ throw css::uno::RuntimeException(
+ "invalid remove of non-extension prop " + name + " in " +
+ reader.getUrl());
+ }
+}
+
+void XcuParser::handleGroupNode(
+ xmlreader::XmlReader & reader, rtl::Reference< Node > const & group)
+{
+ bool hasName = false;
+ OUString name;
+ Operation op = OPERATION_MODIFY;
+ bool finalized = false;
+ for (;;) {
+ int attrNsId;
+ xmlreader::Span attrLn;
+ if (!reader.nextAttribute(&attrNsId, &attrLn)) {
+ break;
+ }
+ if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn == "name") {
+ hasName = true;
+ name = reader.getAttributeValue(false).convertFromUtf8();
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "op")
+ {
+ op = parseOperation(reader.getAttributeValue(true));
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "finalized")
+ {
+ finalized = xmldata::parseBoolean(reader.getAttributeValue(true));
+ }
+ }
+ if (!hasName) {
+ throw css::uno::RuntimeException(
+ "no node name attribute in " + reader.getUrl());
+ }
+ if (trackPath_) {
+ path_.push_back(name);
+ if (partial_ != nullptr && partial_->contains(path_) == Partial::CONTAINS_NOT)
+ {
+ state_.push(State::Ignore(true));
+ return;
+ }
+ }
+ rtl::Reference< Node > child(
+ group->getMembers().findNode(valueParser_.getLayer(), name));
+ if (!child.is()) {
+ SAL_WARN(
+ "configmgr",
+ "unknown node \"" << name << "\" in \"" << reader.getUrl() << '"');
+ state_.push(State::Ignore(true));
+ return;
+ }
+ Node::Kind kind = child->kind();
+ if (kind != Node::KIND_GROUP && kind != Node::KIND_SET) {
+ throw css::uno::RuntimeException(
+ "bad <node> \"" + name + "\" of non group/set kind in " +
+ reader.getUrl());
+ }
+ if (op != OPERATION_MODIFY && op != OPERATION_FUSE) {
+ throw css::uno::RuntimeException(
+ "invalid operation on group node in " + reader.getUrl());
+ }
+ int finalizedLayer = std::min(
+ finalized ? valueParser_.getLayer() : Data::NO_LAYER,
+ child->getFinalized());
+ child->setFinalized(finalizedLayer);
+ if (finalizedLayer < valueParser_.getLayer()) {
+ state_.push(State::Ignore(true));
+ return;
+ }
+ state_.push(State::Modify(child));
+}
+
+void XcuParser::handleSetNode(xmlreader::XmlReader & reader, SetNode * set) {
+ bool hasName = false;
+ OUString name;
+ OUString component(componentName_);
+ bool hasNodeType = false;
+ OUString nodeType;
+ Operation op = OPERATION_MODIFY;
+ bool finalized = false;
+ bool mandatory = false;
+ for (;;) {
+ int attrNsId;
+ xmlreader::Span attrLn;
+ if (!reader.nextAttribute(&attrNsId, &attrLn)) {
+ break;
+ }
+ if (attrNsId == ParseManager::NAMESPACE_OOR && attrLn == "name") {
+ hasName = true;
+ name = reader.getAttributeValue(false).convertFromUtf8();
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "component")
+ {
+ component = reader.getAttributeValue(false).convertFromUtf8();
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "node-type")
+ {
+ hasNodeType = true;
+ nodeType = reader.getAttributeValue(false).convertFromUtf8();
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "op")
+ {
+ op = parseOperation(reader.getAttributeValue(true));
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "finalized")
+ {
+ finalized = xmldata::parseBoolean(reader.getAttributeValue(true));
+ } else if (attrNsId == ParseManager::NAMESPACE_OOR &&
+ attrLn == "mandatory")
+ {
+ mandatory = xmldata::parseBoolean(reader.getAttributeValue(true));
+ }
+ }
+ if (!hasName) {
+ throw css::uno::RuntimeException(
+ "no node name attribute in " + reader.getUrl());
+ }
+ if (trackPath_) {
+ path_.push_back(name);
+ if (partial_ != nullptr && partial_->contains(path_) == Partial::CONTAINS_NOT)
+ {
+ state_.push(State::Ignore(true));
+ return;
+ }
+ }
+ OUString templateName(
+ xmldata::parseTemplateReference(
+ component, hasNodeType, nodeType, &set->getDefaultTemplateName()));
+ if (!set->isValidTemplate(templateName)) {
+ throw css::uno::RuntimeException(
+ "set member node " + name + " references invalid template " +
+ templateName + " in " + reader.getUrl());
+ }
+ rtl::Reference< Node > tmpl(
+ data_.getTemplate(valueParser_.getLayer(), templateName));
+ if (!tmpl.is()) {
+ throw css::uno::RuntimeException(
+ "set member node " + name + " references undefined template " +
+ templateName + " in " + reader.getUrl());
+ }
+ int finalizedLayer = finalized ? valueParser_.getLayer() : Data::NO_LAYER;
+ int mandatoryLayer = mandatory ? valueParser_.getLayer() : Data::NO_LAYER;
+ NodeMap & members = set->getMembers();
+ NodeMap::iterator i(members.find(name));
+ if (i != members.end()) {
+ finalizedLayer = std::min(finalizedLayer, i->second->getFinalized());
+ i->second->setFinalized(finalizedLayer);
+ mandatoryLayer = std::min(mandatoryLayer, i->second->getMandatory());
+ i->second->setMandatory(mandatoryLayer);
+ if (i->second->getLayer() > valueParser_.getLayer()) {
+ state_.push(State::Ignore(true));
+ return;
+ }
+ }
+ if (finalizedLayer < valueParser_.getLayer()) {
+ state_.push(State::Ignore(true));
+ return;
+ }
+ switch (op) {
+ case OPERATION_MODIFY:
+ if (i == members.end()) {
+ SAL_WARN(
+ "configmgr",
+ "ignoring modify of unknown set member node \"" << name
+ << "\" in \"" << reader.getUrl() << '"');
+ state_.push(State::Ignore(true));
+ } else {
+ state_.push(State::Modify(i->second));
+ }
+ break;
+ case OPERATION_REPLACE:
+ {
+ rtl::Reference< Node > member(tmpl->clone(true));
+ member->setLayer(valueParser_.getLayer());
+ member->setFinalized(finalizedLayer);
+ member->setMandatory(mandatoryLayer);
+ state_.push(State::Insert(member, name));
+ recordModification(i == members.end());
+ }
+ break;
+ case OPERATION_FUSE:
+ if (i == members.end()) {
+ rtl::Reference< Node > member(tmpl->clone(true));
+ member->setLayer(valueParser_.getLayer());
+ member->setFinalized(finalizedLayer);
+ member->setMandatory(mandatoryLayer);
+ state_.push(State::Insert(member, name));
+ recordModification(true);
+ } else {
+ state_.push(State::Modify(i->second));
+ }
+ break;
+ case OPERATION_REMOVE:
+ {
+ // Ignore removal of unknown members and members made mandatory in
+ // this or a lower layer; forget about user-layer removals that no
+ // longer remove anything (so that paired additions/removals in the
+ // user layer do not grow registrymodifications.xcu unbounded):
+ bool known = i != members.end();
+ if (known &&
+ (mandatoryLayer == Data::NO_LAYER ||
+ mandatoryLayer > valueParser_.getLayer()))
+ {
+ members.erase(i);
+ }
+ state_.push(State::Ignore(true));
+ if (known) {
+ recordModification(false);
+ }
+ break;
+ }
+ }
+}
+
+void XcuParser::recordModification(bool addition) {
+ if (broadcastModifications_ != nullptr) {
+ broadcastModifications_->add(path_);
+ }
+ if (addition && additions_ != nullptr) {
+ additions_->push_back(path_);
+ }
+ if (recordModifications_) {
+ data_.modifications.add(path_);
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/xcuparser.hxx b/configmgr/source/xcuparser.hxx
new file mode 100644
index 000000000..e50b7b5a0
--- /dev/null
+++ b/configmgr/source/xcuparser.hxx
@@ -0,0 +1,152 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <set>
+#include <stack>
+#include <string_view>
+
+#include <rtl/ref.hxx>
+#include <rtl/ustring.hxx>
+#include <utility>
+#include <xmlreader/xmlreader.hxx>
+
+#include "additions.hxx"
+#include "node.hxx"
+#include "nodemap.hxx"
+#include "parser.hxx"
+#include "type.hxx"
+#include "valueparser.hxx"
+
+namespace xmlreader { struct Span; }
+
+namespace configmgr {
+
+class GroupNode;
+class LocalizedPropertyNode;
+class Modifications;
+class Partial;
+class PropertyNode;
+class SetNode;
+struct Data;
+
+class XcuParser: public Parser {
+public:
+ XcuParser(
+ int layer, Data & data, Partial const * partial,
+ Modifications * broadcastModifications, Additions * additions);
+
+private:
+ virtual ~XcuParser() override;
+
+ virtual xmlreader::XmlReader::Text getTextMode() override;
+
+ virtual bool startElement(
+ xmlreader::XmlReader & reader, int nsId, xmlreader::Span const & name,
+ std::set< OUString > const * existingDependencies) override;
+
+ virtual void endElement(xmlreader::XmlReader const & reader) override;
+
+ virtual void characters(xmlreader::Span const & span) override;
+
+ enum Operation {
+ OPERATION_MODIFY, OPERATION_REPLACE, OPERATION_FUSE, OPERATION_REMOVE };
+
+ static Operation parseOperation(xmlreader::Span const & text);
+
+ void handleComponentData(xmlreader::XmlReader & reader);
+
+ void handleItem(xmlreader::XmlReader & reader);
+
+ void handlePropValue(xmlreader::XmlReader & reader, PropertyNode * prop);
+
+ void handleLocpropValue(
+ xmlreader::XmlReader & reader, LocalizedPropertyNode * locprop);
+
+ void handleGroupProp(xmlreader::XmlReader & reader, GroupNode * group);
+
+ void handleUnknownGroupProp(
+ xmlreader::XmlReader const & reader, GroupNode const * group,
+ OUString const & name, Type type, Operation operation,
+ bool finalized);
+
+ void handlePlainGroupProp(
+ xmlreader::XmlReader const & reader, GroupNode * group,
+ NodeMap::iterator const & propertyIndex, std::u16string_view name,
+ Type type, Operation operation, bool finalized);
+
+ void handleLocalizedGroupProp(
+ xmlreader::XmlReader const & reader, LocalizedPropertyNode * property,
+ OUString const & name, Type type, Operation operation,
+ bool finalized);
+
+ void handleGroupNode(
+ xmlreader::XmlReader & reader, rtl::Reference< Node > const & group);
+
+ void handleSetNode(xmlreader::XmlReader & reader, SetNode * set);
+
+ void recordModification(bool addition);
+
+ struct State {
+ rtl::Reference< Node > node; // empty if ignore or <items>
+ OUString name; // empty and ignored if !insert
+ bool ignore;
+ bool insert;
+ bool pop;
+
+ static State Ignore(bool thePop) { return State(thePop); }
+
+ static State Modify(rtl::Reference< Node > const & theNode)
+ { return State(theNode); }
+
+ static State Insert(
+ rtl::Reference< Node > const & theNode, OUString const & theName)
+ { return State(theNode, theName); }
+
+ private:
+ explicit State(bool thePop): ignore(true), insert(false), pop(thePop) {}
+
+ explicit State(rtl::Reference< Node > theNode):
+ node(std::move(theNode)), ignore(false), insert(false), pop(true)
+ {}
+
+ State(
+ rtl::Reference< Node > theNode, OUString theName):
+ node(std::move(theNode)), name(std::move(theName)), ignore(false), insert(true), pop(true)
+ {}
+ };
+
+ ValueParser valueParser_;
+ Data & data_;
+ Partial const * partial_;
+ Modifications * broadcastModifications_;
+ Additions * additions_;
+ bool recordModifications_;
+ bool trackPath_;
+ OUString componentName_;
+ std::stack< State > state_;
+ std::vector<OUString> path_;
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/xmldata.cxx b/configmgr/source/xmldata.cxx
new file mode 100644
index 000000000..ecb4dacab
--- /dev/null
+++ b/configmgr/source/xmldata.cxx
@@ -0,0 +1,133 @@
+/* -*- 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 <com/sun/star/uno/RuntimeException.hpp>
+#include <rtl/string.h>
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+#include <xmlreader/span.hxx>
+#include <xmlreader/xmlreader.hxx>
+
+#include "data.hxx"
+#include "parsemanager.hxx"
+#include "type.hxx"
+#include "xmldata.hxx"
+
+namespace configmgr::xmldata {
+
+Type parseType(
+ xmlreader::XmlReader const & reader, xmlreader::Span const & text)
+{
+ assert(text.is());
+ sal_Int32 i = rtl_str_indexOfChar_WithLength(text.begin, text.length, ':');
+ if (i >= 0) {
+ xmlreader::Span token(text.begin + i + 1, text.length - (i + 1));
+ switch (reader.getNamespaceId(xmlreader::Span(text.begin, i))) {
+ case ParseManager::NAMESPACE_OOR:
+ if (token == "any")
+ {
+ return TYPE_ANY;
+ } else if (token == "boolean-list")
+ {
+ return TYPE_BOOLEAN_LIST;
+ } else if (token == "short-list")
+ {
+ return TYPE_SHORT_LIST;
+ } else if (token == "int-list")
+ {
+ return TYPE_INT_LIST;
+ } else if (token == "long-list")
+ {
+ return TYPE_LONG_LIST;
+ } else if (token == "double-list")
+ {
+ return TYPE_DOUBLE_LIST;
+ } else if (token == "string-list")
+ {
+ return TYPE_STRING_LIST;
+ } else if (token == "hexBinary-list")
+ {
+ return TYPE_HEXBINARY_LIST;
+ }
+ break;
+ case ParseManager::NAMESPACE_XS:
+ if (token == "boolean")
+ {
+ return TYPE_BOOLEAN;
+ } else if (token =="short")
+ {
+ return TYPE_SHORT;
+ } else if (token =="int")
+ {
+ return TYPE_INT;
+ } else if (token =="long")
+ {
+ return TYPE_LONG;
+ } else if (token =="double")
+ {
+ return TYPE_DOUBLE;
+ } else if (token =="string")
+ {
+ return TYPE_STRING;
+ } else if (token =="hexBinary")
+ {
+ return TYPE_HEXBINARY;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ throw css::uno::RuntimeException(
+ "invalid type " + text.convertFromUtf8());
+}
+
+bool parseBoolean(xmlreader::Span const & text) {
+ assert(text.is());
+ if (text == "true") {
+ return true;
+ }
+ if (text == "false") {
+ return false;
+ }
+ throw css::uno::RuntimeException(
+ "invalid boolean " + text.convertFromUtf8());
+}
+
+OUString parseTemplateReference(
+ std::u16string_view component, bool hasNodeType,
+ std::u16string_view nodeType, OUString const * defaultTemplateName)
+{
+ if (!hasNodeType) {
+ if (defaultTemplateName != nullptr) {
+ return *defaultTemplateName;
+ }
+ throw css::uno::RuntimeException(
+ "missing node-type attribute");
+ }
+ return Data::fullTemplateName(component, nodeType);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/xmldata.hxx b/configmgr/source/xmldata.hxx
new file mode 100644
index 000000000..44296b38d
--- /dev/null
+++ b/configmgr/source/xmldata.hxx
@@ -0,0 +1,46 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <rtl/ustring.hxx>
+
+#include "type.hxx"
+
+namespace xmlreader {
+ class XmlReader;
+ struct Span;
+}
+
+namespace configmgr::xmldata {
+
+Type parseType(
+ xmlreader::XmlReader const & reader, xmlreader::Span const & text);
+
+bool parseBoolean(xmlreader::Span const & text);
+
+OUString parseTemplateReference(
+ std::u16string_view component, bool hasNodeType,
+ std::u16string_view nodeType, OUString const * defaultTemplateName);
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */