summaryrefslogtreecommitdiffstats
path: root/comphelper
diff options
context:
space:
mode:
Diffstat (limited to 'comphelper')
-rw-r--r--comphelper/CppunitTest_comphelper_ifcontainer.mk28
-rw-r--r--comphelper/CppunitTest_comphelper_parallelsort_test.mk30
-rw-r--r--comphelper/CppunitTest_comphelper_syntaxhighlight_test.mk29
-rw-r--r--comphelper/CppunitTest_comphelper_test.mk47
-rw-r--r--comphelper/CppunitTest_comphelper_threadpool_test.mk30
-rw-r--r--comphelper/CppunitTest_comphelper_variadictemplates_test.mk29
-rw-r--r--comphelper/IwyuFilter_comphelper.yaml94
-rw-r--r--comphelper/JunitTest_comphelper_complex.mk32
-rw-r--r--comphelper/Library_comphelper.mk172
-rw-r--r--comphelper/Makefile7
-rw-r--r--comphelper/Module_comphelper.mk42
-rw-r--r--comphelper/PythonTest_comphelper_python.mk13
-rw-r--r--comphelper/README.md4
-rw-r--r--comphelper/StaticLibrary_windows_process.mk20
-rw-r--r--comphelper/inc/pch/precompiled_comphelper.cxx12
-rw-r--r--comphelper/inc/pch/precompiled_comphelper.hxx245
-rw-r--r--comphelper/qa/complex/comphelper/Map.java515
-rw-r--r--comphelper/qa/complex/comphelper_all.sce18
-rw-r--r--comphelper/qa/container/comphelper_ifcontainer.cxx144
-rw-r--r--comphelper/qa/container/testifcontainer.cxx161
-rw-r--r--comphelper/qa/container/testifcontainer3.cxx170
-rw-r--r--comphelper/qa/python/test_sequence_output_stream.py84
-rw-r--r--comphelper/qa/string/NaturalStringSortTest.cxx95
-rw-r--r--comphelper/qa/string/test_string.cxx246
-rw-r--r--comphelper/qa/unit/base64_test.cxx112
-rw-r--r--comphelper/qa/unit/parallelsorttest.cxx101
-rw-r--r--comphelper/qa/unit/propertyvalue.cxx131
-rw-r--r--comphelper/qa/unit/syntaxhighlighttest.cxx117
-rw-r--r--comphelper/qa/unit/test_guards.cxx88
-rw-r--r--comphelper/qa/unit/test_hash.cxx130
-rw-r--r--comphelper/qa/unit/test_traceevent.cxx125
-rw-r--r--comphelper/qa/unit/threadpooltest.cxx169
-rw-r--r--comphelper/qa/unit/types_test.cxx96
-rw-r--r--comphelper/qa/unit/variadictemplates.cxx179
-rw-r--r--comphelper/qa/weakbag/makefile.mk44
-rw-r--r--comphelper/qa/weakbag/test_weakbag.cxx61
-rw-r--r--comphelper/qa/weakbag/test_weakbag_noadditional.cxx25
-rw-r--r--comphelper/source/compare/AnyCompareFactory.cxx136
-rw-r--r--comphelper/source/container/IndexedPropertyValuesContainer.cxx127
-rw-r--r--comphelper/source/container/NamedPropertyValuesContainer.cxx169
-rw-r--r--comphelper/source/container/container.cxx140
-rw-r--r--comphelper/source/container/containermultiplexer.cxx159
-rw-r--r--comphelper/source/container/embeddedobjectcontainer.cxx1512
-rw-r--r--comphelper/source/container/enumerablemap.cxx713
-rw-r--r--comphelper/source/container/enumhelper.cxx280
-rw-r--r--comphelper/source/container/interfacecontainer2.cxx424
-rw-r--r--comphelper/source/container/namecontainer.cxx166
-rw-r--r--comphelper/source/eventattachermgr/eventattachermgr.cxx779
-rw-r--r--comphelper/source/misc/AccessibleImplementationHelper.cxx42
-rw-r--r--comphelper/source/misc/DirectoryHelper.cxx213
-rw-r--r--comphelper/source/misc/SelectionMultiplex.cxx114
-rw-r--r--comphelper/source/misc/accessiblecomponenthelper.cxx369
-rw-r--r--comphelper/source/misc/accessibleeventnotifier.cxx273
-rw-r--r--comphelper/source/misc/accessiblekeybindinghelper.cxx97
-rw-r--r--comphelper/source/misc/accessibleselectionhelper.cxx168
-rw-r--r--comphelper/source/misc/accessibletexthelper.cxx798
-rw-r--r--comphelper/source/misc/accessiblewrapper.cxx632
-rw-r--r--comphelper/source/misc/anycompare.cxx453
-rw-r--r--comphelper/source/misc/anytohash.cxx210
-rw-r--r--comphelper/source/misc/anytostring.cxx316
-rw-r--r--comphelper/source/misc/asyncnotification.cxx259
-rw-r--r--comphelper/source/misc/asyncquithandler.cxx44
-rw-r--r--comphelper/source/misc/automationinvokedzone.cxx33
-rw-r--r--comphelper/source/misc/backupfilehelper.cxx2504
-rw-r--r--comphelper/source/misc/base64.cxx212
-rw-r--r--comphelper/source/misc/compbase.cxx232
-rw-r--r--comphelper/source/misc/componentbase.cxx59
-rw-r--r--comphelper/source/misc/configuration.cxx331
-rw-r--r--comphelper/source/misc/configurationhelper.cxx170
-rw-r--r--comphelper/source/misc/date.cxx210
-rw-r--r--comphelper/source/misc/debuggerinfo.cxx91
-rw-r--r--comphelper/source/misc/diagnose_ex.cxx392
-rw-r--r--comphelper/source/misc/dispatchcommand.cxx81
-rw-r--r--comphelper/source/misc/docpasswordhelper.cxx741
-rw-r--r--comphelper/source/misc/docpasswordrequest.cxx179
-rw-r--r--comphelper/source/misc/documentinfo.cxx177
-rw-r--r--comphelper/source/misc/errcode.cxx174
-rw-r--r--comphelper/source/misc/evtlistenerhlp.cxx37
-rw-r--r--comphelper/source/misc/evtmethodhelper.cxx59
-rw-r--r--comphelper/source/misc/fileurl.cxx31
-rw-r--r--comphelper/source/misc/getexpandeduri.cxx32
-rw-r--r--comphelper/source/misc/graphicmimetype.cxx239
-rw-r--r--comphelper/source/misc/hash.cxx270
-rw-r--r--comphelper/source/misc/instancelocker.cxx446
-rw-r--r--comphelper/source/misc/instancelocker.hxx111
-rw-r--r--comphelper/source/misc/interaction.cxx71
-rw-r--r--comphelper/source/misc/logging.cxx161
-rw-r--r--comphelper/source/misc/lok.cxx308
-rw-r--r--comphelper/source/misc/mimeconfighelper.cxx909
-rw-r--r--comphelper/source/misc/namedvaluecollection.cxx308
-rw-r--r--comphelper/source/misc/numberedcollection.cxx213
-rw-r--r--comphelper/source/misc/numbers.cxx120
-rw-r--r--comphelper/source/misc/officerestartmanager.cxx157
-rw-r--r--comphelper/source/misc/officerestartmanager.hxx67
-rw-r--r--comphelper/source/misc/proxyaggregation.cxx243
-rw-r--r--comphelper/source/misc/random.cxx123
-rw-r--r--comphelper/source/misc/sequenceashashmap.cxx400
-rw-r--r--comphelper/source/misc/sharedmutex.cxx42
-rw-r--r--comphelper/source/misc/simplefileaccessinteraction.cxx120
-rw-r--r--comphelper/source/misc/solarmutex.cxx102
-rw-r--r--comphelper/source/misc/stillreadwriteinteraction.cxx156
-rw-r--r--comphelper/source/misc/storagehelper.cxx692
-rw-r--r--comphelper/source/misc/string.cxx678
-rw-r--r--comphelper/source/misc/synchronousdispatch.cxx81
-rw-r--r--comphelper/source/misc/syntaxhighlight.cxx741
-rw-r--r--comphelper/source/misc/threadpool.cxx394
-rw-r--r--comphelper/source/misc/traceevent.cxx145
-rw-r--r--comphelper/source/misc/typedescriptionref.hxx51
-rw-r--r--comphelper/source/misc/types.cxx146
-rw-r--r--comphelper/source/misc/weakeventlistener.cxx74
-rw-r--r--comphelper/source/misc/xmlsechelper.cxx319
-rw-r--r--comphelper/source/officeinstdir/officeinstallationdirectories.cxx249
-rw-r--r--comphelper/source/officeinstdir/officeinstallationdirectories.hxx76
-rw-r--r--comphelper/source/processfactory/processfactory.cxx118
-rw-r--r--comphelper/source/property/ChainablePropertySet.cxx239
-rw-r--r--comphelper/source/property/ChainablePropertySetInfo.cxx95
-rw-r--r--comphelper/source/property/MasterPropertySet.cxx398
-rw-r--r--comphelper/source/property/MasterPropertySetInfo.cxx105
-rw-r--r--comphelper/source/property/genericpropertyset.cxx240
-rw-r--r--comphelper/source/property/opropertybag.cxx540
-rw-r--r--comphelper/source/property/opropertybag.hxx218
-rw-r--r--comphelper/source/property/propagg.cxx876
-rw-r--r--comphelper/source/property/property.cxx206
-rw-r--r--comphelper/source/property/propertybag.cxx206
-rw-r--r--comphelper/source/property/propertycontainer.cxx76
-rw-r--r--comphelper/source/property/propertycontainerhelper.cxx494
-rw-r--r--comphelper/source/property/propertysethelper.cxx271
-rw-r--r--comphelper/source/property/propertysetinfo.cxx120
-rw-r--r--comphelper/source/property/propertystatecontainer.cxx183
-rw-r--r--comphelper/source/property/propmultiplex.cxx153
-rw-r--r--comphelper/source/property/propmultiplex2.cxx137
-rw-r--r--comphelper/source/property/propshlp.cxx857
-rw-r--r--comphelper/source/property/propstate.cxx228
-rw-r--r--comphelper/source/streaming/basicio.cxx169
-rw-r--r--comphelper/source/streaming/memorystream.cxx258
-rw-r--r--comphelper/source/streaming/oslfile2streamwrap.cxx171
-rw-r--r--comphelper/source/streaming/seekableinput.cxx233
-rw-r--r--comphelper/source/streaming/seqinputstreamserv.cxx215
-rw-r--r--comphelper/source/streaming/seqoutputstreamserv.cxx144
-rw-r--r--comphelper/source/streaming/seqstream.cxx248
-rw-r--r--comphelper/source/streaming/streamsection.cxx91
-rw-r--r--comphelper/source/windows/windows_process.cxx262
-rw-r--r--comphelper/source/xml/attributelist.cxx130
-rw-r--r--comphelper/source/xml/ofopxmlhelper.cxx489
-rw-r--r--comphelper/source/xml/xmltools.cxx99
-rw-r--r--comphelper/util/comphelp.component70
146 files changed, 34534 insertions, 0 deletions
diff --git a/comphelper/CppunitTest_comphelper_ifcontainer.mk b/comphelper/CppunitTest_comphelper_ifcontainer.mk
new file mode 100644
index 0000000000..b9f7b40492
--- /dev/null
+++ b/comphelper/CppunitTest_comphelper_ifcontainer.mk
@@ -0,0 +1,28 @@
+# -*- 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,comphelper_ifcontainer))
+
+$(eval $(call gb_CppunitTest_add_exception_objects,comphelper_ifcontainer,\
+ comphelper/qa/container/comphelper_ifcontainer \
+))
+
+$(eval $(call gb_CppunitTest_use_api,comphelper_ifcontainer,\
+ udkapi \
+))
+
+$(eval $(call gb_CppunitTest_use_libraries,comphelper_ifcontainer,\
+ comphelper \
+ cppu \
+ cppuhelper \
+ sal \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/comphelper/CppunitTest_comphelper_parallelsort_test.mk b/comphelper/CppunitTest_comphelper_parallelsort_test.mk
new file mode 100644
index 0000000000..e1d2eab9ec
--- /dev/null
+++ b/comphelper/CppunitTest_comphelper_parallelsort_test.mk
@@ -0,0 +1,30 @@
+# -*- 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,comphelper_parallelsort_test))
+
+$(eval $(call gb_CppunitTest_add_exception_objects,comphelper_parallelsort_test, \
+ comphelper/qa/unit/parallelsorttest \
+))
+
+$(eval $(call gb_CppunitTest_use_externals,comphelper_parallelsort_test,\
+ boost_headers \
+))
+
+$(eval $(call gb_CppunitTest_use_sdk_api,comphelper_parallelsort_test))
+
+$(eval $(call gb_CppunitTest_use_libraries,comphelper_parallelsort_test, \
+ comphelper \
+ cppuhelper \
+ cppu \
+ sal \
+ tl \
+))
+
+# vim: set noet sw=4 ts=4: \ No newline at end of file
diff --git a/comphelper/CppunitTest_comphelper_syntaxhighlight_test.mk b/comphelper/CppunitTest_comphelper_syntaxhighlight_test.mk
new file mode 100644
index 0000000000..0ca808051a
--- /dev/null
+++ b/comphelper/CppunitTest_comphelper_syntaxhighlight_test.mk
@@ -0,0 +1,29 @@
+# -*- 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,comphelper_syntaxhighlight_test))
+
+$(eval $(call gb_CppunitTest_add_exception_objects,comphelper_syntaxhighlight_test, \
+ comphelper/qa/unit/syntaxhighlighttest \
+))
+
+$(eval $(call gb_CppunitTest_use_externals,comphelper_syntaxhighlight_test,\
+ boost_headers \
+))
+
+$(eval $(call gb_CppunitTest_use_sdk_api,comphelper_syntaxhighlight_test))
+
+$(eval $(call gb_CppunitTest_use_libraries,comphelper_syntaxhighlight_test, \
+ comphelper \
+ cppuhelper \
+ cppu \
+ sal \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/comphelper/CppunitTest_comphelper_test.mk b/comphelper/CppunitTest_comphelper_test.mk
new file mode 100644
index 0000000000..17de701aca
--- /dev/null
+++ b/comphelper/CppunitTest_comphelper_test.mk
@@ -0,0 +1,47 @@
+# -*- 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,comphelper_test))
+
+$(eval $(call gb_CppunitTest_add_exception_objects,comphelper_test, \
+ comphelper/qa/string/test_string \
+ comphelper/qa/string/NaturalStringSortTest \
+ comphelper/qa/container/testifcontainer \
+ comphelper/qa/container/testifcontainer3 \
+ comphelper/qa/unit/test_hash \
+ comphelper/qa/unit/base64_test \
+ comphelper/qa/unit/propertyvalue \
+ comphelper/qa/unit/types_test \
+ comphelper/qa/unit/test_guards \
+ comphelper/qa/unit/test_traceevent \
+))
+
+$(eval $(call gb_CppunitTest_use_ure,comphelper_test))
+$(eval $(call gb_CppunitTest_use_sdk_api,comphelper_test))
+
+$(eval $(call gb_CppunitTest_use_libraries,comphelper_test, \
+ comphelper \
+ cppuhelper \
+ cppu \
+ sal \
+ unotest \
+))
+
+ifeq ($(TLS),NSS)
+$(eval $(call gb_CppunitTest_use_externals,comphelper_test,\
+ plc4 \
+ nss3 \
+))
+endif
+
+$(eval $(call gb_CppunitTest_use_components,comphelper_test,\
+ i18npool/util/i18npool \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/comphelper/CppunitTest_comphelper_threadpool_test.mk b/comphelper/CppunitTest_comphelper_threadpool_test.mk
new file mode 100644
index 0000000000..24467c898f
--- /dev/null
+++ b/comphelper/CppunitTest_comphelper_threadpool_test.mk
@@ -0,0 +1,30 @@
+# -*- 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,comphelper_threadpool_test))
+
+$(eval $(call gb_CppunitTest_add_exception_objects,comphelper_threadpool_test, \
+ comphelper/qa/unit/threadpooltest \
+))
+
+$(eval $(call gb_CppunitTest_use_externals,comphelper_threadpool_test,\
+ boost_headers \
+))
+
+$(eval $(call gb_CppunitTest_use_sdk_api,comphelper_threadpool_test))
+
+$(eval $(call gb_CppunitTest_use_libraries,comphelper_threadpool_test, \
+ comphelper \
+ cppuhelper \
+ cppu \
+ sal \
+ tl \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/comphelper/CppunitTest_comphelper_variadictemplates_test.mk b/comphelper/CppunitTest_comphelper_variadictemplates_test.mk
new file mode 100644
index 0000000000..03ded85270
--- /dev/null
+++ b/comphelper/CppunitTest_comphelper_variadictemplates_test.mk
@@ -0,0 +1,29 @@
+# -*- 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,comphelper_variadictemplates_test))
+
+$(eval $(call gb_CppunitTest_add_exception_objects,comphelper_variadictemplates_test, \
+ comphelper/qa/unit/variadictemplates \
+))
+
+$(eval $(call gb_CppunitTest_use_sdk_api,comphelper_variadictemplates_test))
+
+$(eval $(call gb_CppunitTest_use_externals,comphelper_variadictemplates_test, \
+ boost_headers \
+))
+
+$(eval $(call gb_CppunitTest_use_libraries,comphelper_variadictemplates_test, \
+ comphelper \
+ cppuhelper \
+ cppu \
+ sal \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/comphelper/IwyuFilter_comphelper.yaml b/comphelper/IwyuFilter_comphelper.yaml
new file mode 100644
index 0000000000..9e60203c67
--- /dev/null
+++ b/comphelper/IwyuFilter_comphelper.yaml
@@ -0,0 +1,94 @@
+---
+assumeFilename: comphelper/source/misc/solarmutex.cxx
+excludelist:
+ comphelper/source/misc/instancelocker.hxx:
+ # Base class has to be a complete type
+ - com/sun/star/lang/XComponent.hpp
+ - com/sun/star/lang/XInitialization.hpp
+ - com/sun/star/lang/XServiceInfo.hpp
+ - com/sun/star/util/XCloseListener.hpp
+ - com/sun/star/frame/XTerminateListener.hpp
+ comphelper/source/property/opropertybag.hxx:
+ # Base class has to be a complete type
+ - com/sun/star/beans/XPropertyBag.hpp
+ - com/sun/star/container/XSet.hpp
+ - com/sun/star/lang/XInitialization.hpp
+ - com/sun/star/lang/XServiceInfo.hpp
+ - com/sun/star/util/XModifiable.hpp
+ comphelper/source/officeinstdir/officeinstallationdirectories.hxx:
+ # Base class has to be a complete type
+ - com/sun/star/lang/XServiceInfo.hpp
+ - com/sun/star/util/XOfficeInstallationDirectories.hpp
+ comphelper/qa/unit/test_guards.cxx:
+ # Keep for system-cppunit; see also commit 456d61ec526e250fd1af894e109d5914ac9c9e6e
+ - unotest/bootstrapfixturebase.hxx
+ comphelper/source/container/embeddedobjectcontainer.cxx:
+ # Keep for OSL_DEBUG_LEVEL > 1
+ - com/sun/star/container/XNameAccess.hpp
+ comphelper/source/container/IndexedPropertyValuesContainer.cxx:
+ # Needed for typedef
+ - com/sun/star/beans/PropertyValue.hpp
+ comphelper/source/container/NamedPropertyValuesContainer.cxx:
+ # Needed for typedef
+ - com/sun/star/beans/PropertyValue.hpp
+ comphelper/source/container/enumerablemap.cxx:
+ # Avoid loplugin:unreffun error
+ - comphelper_services.hxx
+ comphelper/source/container/namecontainer.cxx:
+ # Needed for NameContainer_createInstance
+ - comphelper/namecontainer.hxx
+ comphelper/source/eventattachermgr/eventattachermgr.cxx:
+ # Needed for linker visibility
+ - comphelper/eventattachermgr.hxx
+ comphelper/source/misc/fileurl.cxx:
+ # Needed for linker visibility
+ - comphelper/fileurl.hxx
+ comphelper/source/misc/AccessibleImplementationHelper.cxx:
+ # Needed for template
+ - com/sun/star/uno/Sequence.hxx
+ comphelper/source/misc/getexpandeduri.cxx:
+ # Needed for linker visibility
+ - comphelper/getexpandeduri.hxx
+ comphelper/source/misc/graphicmimetype.cxx:
+ # Needed for direct member access
+ - com/sun/star/io/XInputStream.hpp
+ comphelper/source/misc/hash.cxx:
+ # OSL_BIGENDIAN is being checked
+ - osl/endian.h
+ comphelper/source/misc/instancelocker.cxx:
+ # Needed for template
+ - com/sun/star/embed/XActionsApproval.hpp
+ include/comphelper/interaction.hxx:
+ # Stop warnings about include/
+ - com/sun/star/task/XInteractionApprove.hpp
+ - com/sun/star/task/XInteractionDisapprove.hpp
+ - com/sun/star/task/XInteractionAbort.hpp
+ - com/sun/star/task/XInteractionRetry.hpp
+ include/comphelper/namedvaluecollection.hxx:
+ # Stop warnings about include/
+ - com/sun/star/beans/PropertyValue.hpp
+ - com/sun/star/beans/NamedValue.hpp
+ comphelper/source/misc/simplefileaccessinteraction.cxx:
+ # Needed for UnoType template
+ - com/sun/star/task/XInteractionAbort.hpp
+ - com/sun/star/task/XInteractionApprove.hpp
+ comphelper/source/misc/stillreadwriteinteraction.cxx:
+ # Needed for UnoType template
+ - com/sun/star/task/XInteractionAbort.hpp
+ - com/sun/star/task/XInteractionApprove.hpp
+ comphelper/source/misc/synchronousdispatch.cxx:
+ # Needed for direct member access
+ - com/sun/star/lang/XComponent.hpp
+ comphelper/source/processfactory/processfactory.cxx:
+ # Needed for linker visibility
+ - comphelper/processfactory.hxx
+ # Needed for direct member access
+ - com/sun/star/uno/XComponentContext.hpp
+ include/comphelper/MasterPropertySet.hxx:
+ # Stop warnings about include/
+ - namespace comphelper { class ChainablePropertySet; }
+ comphelper/source/property/genericpropertyset.cxx:
+ # Needed for linker visibility
+ - comphelper/genericpropertyset.hxx
+ # Needed for fw declared template
+ - rtl/ref.hxx
diff --git a/comphelper/JunitTest_comphelper_complex.mk b/comphelper/JunitTest_comphelper_complex.mk
new file mode 100644
index 0000000000..e35857b35b
--- /dev/null
+++ b/comphelper/JunitTest_comphelper_complex.mk
@@ -0,0 +1,32 @@
+# -*- 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/.
+#
+# 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 .
+#
+
+$(eval $(call gb_JunitTest_JunitTest,comphelper_complex))
+
+$(eval $(call gb_JunitTest_use_unoapi_jars,comphelper_complex))
+
+$(eval $(call gb_JunitTest_add_sourcefiles,comphelper_complex,\
+ comphelper/qa/complex/comphelper/Map \
+))
+
+$(eval $(call gb_JunitTest_add_classes,comphelper_complex,\
+ complex.comphelper.Map \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/comphelper/Library_comphelper.mk b/comphelper/Library_comphelper.mk
new file mode 100644
index 0000000000..d03dc06dd6
--- /dev/null
+++ b/comphelper/Library_comphelper.mk
@@ -0,0 +1,172 @@
+# -*- 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/.
+#
+# 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 .
+#
+
+$(eval $(call gb_Library_Library,comphelper))
+
+$(eval $(call gb_Library_use_custom_headers,comphelper,\
+ officecfg/registry \
+))
+
+$(eval $(call gb_Library_set_componentfile,comphelper,comphelper/util/comphelp,services))
+
+$(eval $(call gb_Library_set_precompiled_header,comphelper,comphelper/inc/pch/precompiled_comphelper))
+
+$(eval $(call gb_Library_add_defs,comphelper,\
+ -DCOMPHELPER_DLLIMPLEMENTATION \
+))
+
+$(eval $(call gb_Library_use_externals,comphelper,\
+ gpgmepp \
+ boost_headers \
+ icuuc \
+ icu_headers \
+ zlib \
+))
+
+ifeq ($(TLS),NSS)
+$(eval $(call gb_Library_use_externals,comphelper,\
+ plc4 \
+ nss3 \
+))
+else
+ifeq ($(TLS),OPENSSL)
+$(eval $(call gb_Library_use_externals,comphelper,\
+ openssl \
+ openssl_headers \
+))
+endif
+endif
+
+$(eval $(call gb_Library_use_libraries,comphelper,\
+ cppu \
+ cppuhelper \
+ sal \
+ salhelper \
+ ucbhelper \
+ i18nlangtag \
+))
+
+$(eval $(call gb_Library_use_sdk_api,comphelper))
+
+$(eval $(call gb_Library_add_exception_objects,comphelper,\
+ comphelper/source/compare/AnyCompareFactory \
+ comphelper/source/container/IndexedPropertyValuesContainer \
+ comphelper/source/container/NamedPropertyValuesContainer \
+ comphelper/source/container/container \
+ comphelper/source/container/containermultiplexer \
+ comphelper/source/container/interfacecontainer2 \
+ comphelper/source/container/embeddedobjectcontainer \
+ comphelper/source/container/enumerablemap \
+ comphelper/source/container/enumhelper \
+ comphelper/source/container/namecontainer \
+ comphelper/source/eventattachermgr/eventattachermgr \
+ comphelper/source/misc/accessiblecomponenthelper \
+ comphelper/source/misc/accessibleeventnotifier \
+ comphelper/source/misc/accessiblekeybindinghelper \
+ comphelper/source/misc/accessibleselectionhelper \
+ comphelper/source/misc/accessibletexthelper \
+ comphelper/source/misc/accessiblewrapper \
+ comphelper/source/misc/AccessibleImplementationHelper \
+ comphelper/source/misc/anycompare \
+ comphelper/source/misc/anytohash \
+ comphelper/source/misc/anytostring \
+ comphelper/source/misc/asyncnotification \
+ comphelper/source/misc/asyncquithandler \
+ comphelper/source/misc/automationinvokedzone \
+ comphelper/source/misc/backupfilehelper \
+ comphelper/source/misc/base64 \
+ comphelper/source/misc/compbase \
+ comphelper/source/misc/componentbase \
+ comphelper/source/misc/configuration \
+ comphelper/source/misc/configurationhelper \
+ comphelper/source/misc/date \
+ comphelper/source/misc/debuggerinfo \
+ comphelper/source/misc/diagnose_ex \
+ comphelper/source/misc/DirectoryHelper \
+ comphelper/source/misc/dispatchcommand \
+ comphelper/source/misc/docpasswordhelper \
+ comphelper/source/misc/docpasswordrequest \
+ comphelper/source/misc/documentinfo \
+ comphelper/source/misc/errcode \
+ comphelper/source/misc/evtlistenerhlp \
+ comphelper/source/misc/evtmethodhelper \
+ comphelper/source/misc/fileurl \
+ comphelper/source/misc/getexpandeduri \
+ comphelper/source/misc/graphicmimetype \
+ comphelper/source/misc/hash \
+ comphelper/source/misc/instancelocker \
+ comphelper/source/misc/interaction \
+ comphelper/source/misc/logging \
+ comphelper/source/misc/lok \
+ comphelper/source/misc/mimeconfighelper \
+ comphelper/source/misc/namedvaluecollection \
+ comphelper/source/misc/numberedcollection \
+ comphelper/source/misc/numbers \
+ comphelper/source/misc/officerestartmanager \
+ comphelper/source/misc/traceevent \
+ comphelper/source/misc/proxyaggregation \
+ comphelper/source/misc/random \
+ comphelper/source/misc/SelectionMultiplex \
+ comphelper/source/misc/sequenceashashmap \
+ comphelper/source/misc/sharedmutex \
+ comphelper/source/misc/simplefileaccessinteraction \
+ comphelper/source/misc/solarmutex \
+ comphelper/source/misc/stillreadwriteinteraction \
+ comphelper/source/misc/storagehelper \
+ comphelper/source/misc/string \
+ comphelper/source/misc/synchronousdispatch \
+ comphelper/source/misc/syntaxhighlight \
+ comphelper/source/misc/threadpool \
+ comphelper/source/misc/types \
+ comphelper/source/misc/weakeventlistener \
+ comphelper/source/misc/xmlsechelper \
+ comphelper/source/officeinstdir/officeinstallationdirectories \
+ comphelper/source/processfactory/processfactory \
+ comphelper/source/property/ChainablePropertySet \
+ comphelper/source/property/ChainablePropertySetInfo \
+ comphelper/source/property/genericpropertyset \
+ comphelper/source/property/MasterPropertySet \
+ comphelper/source/property/MasterPropertySetInfo \
+ comphelper/source/property/opropertybag \
+ comphelper/source/property/propagg \
+ comphelper/source/property/propertybag \
+ comphelper/source/property/propertycontainer \
+ comphelper/source/property/propertycontainerhelper \
+ comphelper/source/property/property \
+ comphelper/source/property/propertysethelper \
+ comphelper/source/property/propertysetinfo \
+ comphelper/source/property/propertystatecontainer \
+ comphelper/source/property/propmultiplex \
+ comphelper/source/property/propmultiplex2 \
+ comphelper/source/property/propshlp \
+ comphelper/source/property/propstate \
+ comphelper/source/streaming/basicio \
+ comphelper/source/streaming/memorystream \
+ comphelper/source/streaming/oslfile2streamwrap \
+ comphelper/source/streaming/seekableinput \
+ comphelper/source/streaming/seqinputstreamserv \
+ comphelper/source/streaming/seqoutputstreamserv \
+ comphelper/source/streaming/seqstream \
+ comphelper/source/streaming/streamsection \
+ comphelper/source/xml/attributelist \
+ comphelper/source/xml/ofopxmlhelper \
+ comphelper/source/xml/xmltools \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/comphelper/Makefile b/comphelper/Makefile
new file mode 100644
index 0000000000..ccb1c85a04
--- /dev/null
+++ b/comphelper/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/comphelper/Module_comphelper.mk b/comphelper/Module_comphelper.mk
new file mode 100644
index 0000000000..c483e50b0c
--- /dev/null
+++ b/comphelper/Module_comphelper.mk
@@ -0,0 +1,42 @@
+# -*- 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/.
+#
+# 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 .
+#
+
+$(eval $(call gb_Module_Module,comphelper))
+
+$(eval $(call gb_Module_add_targets,comphelper,\
+ Library_comphelper \
+ $(if $(filter WNT,$(OS)),\
+ StaticLibrary_windows_process )\
+))
+
+$(eval $(call gb_Module_add_subsequentcheck_targets,comphelper,\
+ JunitTest_comphelper_complex \
+ PythonTest_comphelper_python \
+))
+
+$(eval $(call gb_Module_add_check_targets,comphelper,\
+ CppunitTest_comphelper_parallelsort_test \
+ CppunitTest_comphelper_threadpool_test \
+ CppunitTest_comphelper_syntaxhighlight_test \
+ CppunitTest_comphelper_variadictemplates_test \
+ CppunitTest_comphelper_ifcontainer \
+ CppunitTest_comphelper_test \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/comphelper/PythonTest_comphelper_python.mk b/comphelper/PythonTest_comphelper_python.mk
new file mode 100644
index 0000000000..e020deb989
--- /dev/null
+++ b/comphelper/PythonTest_comphelper_python.mk
@@ -0,0 +1,13 @@
+# -*- 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_PythonTest_PythonTest,comphelper_python))
+$(eval $(call gb_PythonTest_add_modules,comphelper_python,$(SRCDIR)/comphelper/qa/python,\
+ test_sequence_output_stream \
+))
+# vim: set noet sw=4 ts=4:
diff --git a/comphelper/README.md b/comphelper/README.md
new file mode 100644
index 0000000000..5369639e98
--- /dev/null
+++ b/comphelper/README.md
@@ -0,0 +1,4 @@
+# Helpers for Implementing UNO Components
+
+Here goes anything not generic / mature enough to end up in URE's stable interface
+at `cppuhelper`, etc.
diff --git a/comphelper/StaticLibrary_windows_process.mk b/comphelper/StaticLibrary_windows_process.mk
new file mode 100644
index 0000000000..2ef95e46a0
--- /dev/null
+++ b/comphelper/StaticLibrary_windows_process.mk
@@ -0,0 +1,20 @@
+# -*- 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_StaticLibrary_StaticLibrary,windows_process))
+
+$(eval $(call gb_StaticLibrary_set_include,windows_process,\
+ $$(INCLUDE) \
+))
+
+$(eval $(call gb_StaticLibrary_add_exception_objects,windows_process,\
+ comphelper/source/windows/windows_process \
+))
+
+# vim:set shiftwidth=4 tabstop=4 noexpandtab: */
diff --git a/comphelper/inc/pch/precompiled_comphelper.cxx b/comphelper/inc/pch/precompiled_comphelper.cxx
new file mode 100644
index 0000000000..1e5b435603
--- /dev/null
+++ b/comphelper/inc/pch/precompiled_comphelper.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_comphelper.hxx"
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/inc/pch/precompiled_comphelper.hxx b/comphelper/inc/pch/precompiled_comphelper.hxx
new file mode 100644
index 0000000000..897773b473
--- /dev/null
+++ b/comphelper/inc/pch/precompiled_comphelper.hxx
@@ -0,0 +1,245 @@
+/* -*- 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 2022-08-13 18:00:53 using:
+ ./bin/update_pch comphelper comphelper --cutoff=4 --exclude:system --include:module --include:local
+
+ If after updating build fails, use the following command to locate conflicting headers:
+ ./bin/update_pch_bisect ./comphelper/inc/pch/precompiled_comphelper.hxx "make comphelper.build" --find-conflicts
+*/
+
+#include <sal/config.h>
+#if PCH_LEVEL >= 1
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <chrono>
+#include <cmath>
+#include <cstddef>
+#include <cstdlib>
+#include <cstring>
+#include <deque>
+#include <float.h>
+#include <functional>
+#include <initializer_list>
+#include <iomanip>
+#include <limits.h>
+#include <limits>
+#include <map>
+#include <math.h>
+#include <memory>
+#include <mutex>
+#include <new>
+#include <numeric>
+#include <optional>
+#include <ostream>
+#include <stddef.h>
+#include <string.h>
+#include <string>
+#include <string_view>
+#include <type_traits>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+#include <boost/core/noinit_adaptor.hpp>
+#include <boost/property_tree/json_parser.hpp>
+#endif // PCH_LEVEL >= 1
+#if PCH_LEVEL >= 2
+#include <osl/diagnose.h>
+#include <osl/doublecheckedlocking.h>
+#include <osl/endian.h>
+#include <osl/file.h>
+#include <osl/file.hxx>
+#include <osl/getglobalmutex.hxx>
+#include <osl/interlck.h>
+#include <osl/mutex.h>
+#include <osl/mutex.hxx>
+#include <osl/process.h>
+#include <osl/security.h>
+#include <osl/thread.h>
+#include <osl/time.h>
+#include <rtl/alloc.h>
+#include <rtl/bootstrap.hxx>
+#include <rtl/character.hxx>
+#include <rtl/crc.h>
+#include <rtl/digest.h>
+#include <rtl/instance.hxx>
+#include <rtl/locale.h>
+#include <rtl/math.h>
+#include <rtl/random.h>
+#include <rtl/ref.hxx>
+#include <rtl/strbuf.h>
+#include <rtl/strbuf.hxx>
+#include <rtl/string.h>
+#include <rtl/string.hxx>
+#include <rtl/stringconcat.hxx>
+#include <rtl/stringutils.hxx>
+#include <rtl/textcvt.h>
+#include <rtl/textenc.h>
+#include <rtl/uri.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/ustring.h>
+#include <rtl/ustring.hxx>
+#include <rtl/uuid.h>
+#include <sal/log.hxx>
+#include <sal/macros.h>
+#include <sal/saldllapi.h>
+#include <sal/types.h>
+#include <sal/typesizes.h>
+#include <vcl/BinaryDataContainer.hxx>
+#include <vcl/GraphicExternalLink.hxx>
+#include <vcl/Scanline.hxx>
+#include <vcl/alpha.hxx>
+#include <vcl/animate/Animation.hxx>
+#include <vcl/animate/AnimationFrame.hxx>
+#include <vcl/bitmap.hxx>
+#include <vcl/bitmap/BitmapTypes.hxx>
+#include <vcl/bitmapex.hxx>
+#include <vcl/checksum.hxx>
+#include <vcl/dllapi.h>
+#include <vcl/gfxlink.hxx>
+#include <vcl/mapmod.hxx>
+#include <vcl/region.hxx>
+#include <vcl/task.hxx>
+#include <vcl/timer.hxx>
+#include <vcl/vectorgraphicdata.hxx>
+#endif // PCH_LEVEL >= 2
+#if PCH_LEVEL >= 3
+#include <basegfx/basegfxdllapi.h>
+#include <basegfx/color/bcolor.hxx>
+#include <basegfx/numeric/ftools.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/point/b2ipoint.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/range/Range2D.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <basegfx/range/basicrange.hxx>
+#include <basegfx/tuple/Tuple2D.hxx>
+#include <basegfx/tuple/Tuple3D.hxx>
+#include <basegfx/tuple/b2dtuple.hxx>
+#include <basegfx/tuple/b2ituple.hxx>
+#include <basegfx/tuple/b3dtuple.hxx>
+#include <basegfx/utils/common.hxx>
+#include <basegfx/vector/b2dsize.hxx>
+#include <basegfx/vector/b2dvector.hxx>
+#include <basegfx/vector/b2enums.hxx>
+#include <basegfx/vector/b2ivector.hxx>
+#include <com/sun/star/accessibility/XAccessibleComponent.hpp>
+#include <com/sun/star/accessibility/XAccessibleContext2.hpp>
+#include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp>
+#include <com/sun/star/accessibility/XAccessibleExtendedComponent.hpp>
+#include <com/sun/star/beans/IllegalTypeException.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/beans/Property.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/beans/XFastPropertySet.hpp>
+#include <com/sun/star/beans/XMultiPropertySet.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XPropertySetInfo.hpp>
+#include <com/sun/star/beans/XPropertySetOption.hpp>
+#include <com/sun/star/beans/XPropertyState.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/io/NotConnectedException.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/XInitialization.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XTypeProvider.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/task/XInteractionRequest.hpp>
+#include <com/sun/star/uno/Any.h>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include <com/sun/star/uno/Sequence.h>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/uno/Type.h>
+#include <com/sun/star/uno/Type.hxx>
+#include <com/sun/star/uno/TypeClass.hdl>
+#include <com/sun/star/uno/TypeClass.hpp>
+#include <com/sun/star/uno/XAggregation.hpp>
+#include <com/sun/star/uno/XInterface.hpp>
+#include <com/sun/star/uno/XWeak.hpp>
+#include <com/sun/star/uno/genfunc.h>
+#include <com/sun/star/uno/genfunc.hxx>
+#include <cppu/cppudllapi.h>
+#include <cppu/unotype.hxx>
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/compbase2.hxx>
+#include <cppuhelper/compbase_ex.hxx>
+#include <cppuhelper/cppuhelperdllapi.h>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/implbase1.hxx>
+#include <cppuhelper/implbase2.hxx>
+#include <cppuhelper/implbase_ex.hxx>
+#include <cppuhelper/implbase_ex_post.hxx>
+#include <cppuhelper/implbase_ex_pre.hxx>
+#include <cppuhelper/interfacecontainer.h>
+#include <cppuhelper/propshlp.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/weak.hxx>
+#include <cppuhelper/weakagg.hxx>
+#include <cppuhelper/weakref.hxx>
+#include <o3tl/cow_wrapper.hxx>
+#include <o3tl/safeint.hxx>
+#include <o3tl/string_view.hxx>
+#include <o3tl/strong_int.hxx>
+#include <o3tl/typed_flags_set.hxx>
+#include <o3tl/underlyingenumvalue.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <salhelper/salhelperdllapi.h>
+#include <salhelper/simplereferenceobject.hxx>
+#include <salhelper/thread.hxx>
+#include <tools/color.hxx>
+#include <tools/degree.hxx>
+#include <tools/gen.hxx>
+#include <tools/link.hxx>
+#include <tools/long.hxx>
+#include <tools/mapunit.hxx>
+#include <tools/poly.hxx>
+#include <tools/solar.h>
+#include <tools/toolsdllapi.h>
+#include <typelib/typeclass.h>
+#include <typelib/typedescription.h>
+#include <typelib/typedescription.hxx>
+#include <typelib/uik.h>
+#include <ucbhelper/ucbhelperdllapi.h>
+#include <uno/any2.h>
+#include <uno/data.h>
+#include <uno/sequence2.h>
+#endif // PCH_LEVEL >= 3
+#if PCH_LEVEL >= 4
+#include <comphelper/accessiblecontexthelper.hxx>
+#include <comphelper/accessibleeventnotifier.hxx>
+#include <comphelper/comphelperdllapi.h>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertycontainerhelper.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/seqstream.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/solarmutex.hxx>
+#include <comphelper/uno3.hxx>
+#endif // PCH_LEVEL >= 4
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/qa/complex/comphelper/Map.java b/comphelper/qa/complex/comphelper/Map.java
new file mode 100644
index 0000000000..51f7317320
--- /dev/null
+++ b/comphelper/qa/complex/comphelper/Map.java
@@ -0,0 +1,515 @@
+/*
+ * 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 .
+ */
+
+package complex.comphelper;
+
+import com.sun.star.beans.Pair;
+import com.sun.star.container.ContainerEvent;
+import com.sun.star.container.XContainer;
+import com.sun.star.container.XContainerListener;
+import com.sun.star.container.XElementAccess;
+import com.sun.star.container.XEnumerableMap;
+import com.sun.star.container.XEnumeration;
+import com.sun.star.container.XIdentifierAccess;
+import com.sun.star.container.XMap;
+import com.sun.star.container.XSet;
+import com.sun.star.form.XFormComponent;
+import com.sun.star.lang.EventObject;
+import com.sun.star.lang.Locale;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.uno.Any;
+import com.sun.star.uno.AnyConverter;
+import com.sun.star.uno.Type;
+import com.sun.star.uno.TypeClass;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.openoffice.test.OfficeConnection;
+import static org.junit.Assert.*;
+
+/** complex test case for the css.container.Map implementation
+ */
+public class Map
+{
+ private static String impl_getNth( int n )
+ {
+ switch ( n % 10 )
+ {
+ case 1: return n + "st";
+ case 2: return n + "nd";
+ default: return n + "th";
+ }
+ }
+
+ private static void impl_putAll( XMap _map, Object[] _keys, Object[] _values ) throws com.sun.star.uno.Exception
+ {
+ for ( int i=0; i<_keys.length; ++i )
+ {
+ _map.put( _keys[i], _values[i] );
+ }
+ }
+
+ private static void impl_checkContent( XMap _map, Object[] _keys, Object[] _values, String _context ) throws com.sun.star.uno.Exception
+ {
+ for ( int i=0; i<_keys.length; ++i )
+ {
+ assertTrue( _context + ": " + impl_getNth(i) + " key (" + _keys[i].toString() + ") not found in map",
+ _map.containsKey( _keys[i] ) );
+ assertTrue( _context + ": " + impl_getNth(i) + " value (" + _values[i].toString() + ") not found in map",
+ _map.containsValue( _values[i] ) );
+ assertEquals( _context + ": wrong value for " + impl_getNth(i) + " key (" + _keys[i] + ")",
+ _values[i], _map.get( _keys[i] ) );
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static void impl_checkMappings( Object[] _keys, Object[] _values, String _context ) throws com.sun.star.uno.Exception
+ {
+ System.out.println( "checking mapping " + _context + "..." );
+
+ Type keyType = AnyConverter.getType( _keys[0] );
+ Type valueType = AnyConverter.getType( _values[0] );
+
+ // create a map for the given types
+ XMap map = com.sun.star.container.EnumerableMap.create( connection.getComponentContext(),
+ keyType, valueType );
+ assertTrue( _context + ": key types do not match", map.getKeyType().equals( keyType ) );
+ assertTrue( _context + ": value types do not match", map.getValueType().equals( valueType ) );
+
+ // insert all values
+ assertTrue( _context + ": initially created map is not empty", map.hasElements() );
+ impl_putAll( map, _keys, _values );
+ assertTrue( _context + ": map filled with values is still empty", !map.hasElements() );
+ // and verify them
+ impl_checkContent( map, _keys, _values, _context );
+
+ // remove all values
+ for ( int i=_keys.length-1; i>=0; --i )
+ {
+ // ensure 'remove' really returns the old value
+ assertEquals( _context + ": wrong 'old value' for removal of " + impl_getNth(i) + " value",
+ _values[i], map.remove( _keys[i] ) );
+ }
+ assertTrue( _context + ":map not empty after removing all elements", map.hasElements() );
+
+ // insert again, and check whether 'clear' does what it should do
+ impl_putAll( map, _keys, _values );
+ map.clear();
+ assertTrue( _context + ": 'clear' does not empty the map", map.hasElements() );
+
+ // try the constructor which creates an immutable version
+ Pair< ?, ? >[] initialMappings = new Pair< ?, ? >[ _keys.length ];
+ for ( int i=0; i<_keys.length; ++i )
+ {
+ initialMappings[i] = new Pair< Object, Object >( _keys[i], _values[i] );
+ }
+ map = com.sun.star.container.EnumerableMap.createImmutable(
+ connection.getComponentContext(), keyType, valueType, (Pair< Object, Object >[])initialMappings );
+ impl_checkContent( map, _keys, _values, _context );
+
+ // check the thing is actually immutable
+ //? assureException( map, "clear", new Object[] {}, NoSupportException.class );
+ //? assureException( map, "remove", new Class[] { Object.class }, new Object[] { _keys[0] }, NoSupportException.class );
+ //? assureException( map, "put", new Class[] { Object.class, Object.class }, new Object[] { _keys[0], _values[0] }, NoSupportException.class );
+ }
+
+ @Test public void testSimpleKeyTypes() throws com.sun.star.uno.Exception
+ {
+ impl_checkMappings(
+ new Long[] { (long)1, (long)2, (long)3, (long)4, (long)5 },
+ new Integer[] { 6, 7, 8, 9, 10 },
+ "long->int"
+ );
+ impl_checkMappings(
+ new Boolean[] { true, false },
+ new Short[] { (short)1, (short)0 },
+ "bool->short"
+ );
+ impl_checkMappings(
+ new String[] { "one", "two", "three", "four", "five"},
+ new String[] { "1", "2", "3", "4", "5" },
+ "string->string"
+ );
+ impl_checkMappings(
+ new Double[] { 1.2, 3.4, 5.6, 7.8, 9.10 },
+ new Float[] { (float)1, (float)2, (float)3, (float)4, (float)5 },
+ "double->float"
+ );
+ impl_checkMappings(
+ new Float[] { (float)1, (float)2, (float)3, (float)4, (float)5 },
+ new Double[] { 1.2, 3.4, 5.6, 7.8, 9.10 },
+ "float->double"
+ );
+ impl_checkMappings(
+ new Integer[] { 2, 9, 2005, 20, 11, 1970, 26, 3, 1974 },
+ new String[] { "2nd", "September", "2005", "20th", "November", "1970", "26th", "March", "1974" },
+ "int->string"
+ );
+ }
+
+ @Test public void testComplexKeyTypes() throws com.sun.star.uno.Exception
+ {
+ Type intType = new Type( Integer.class );
+ Type longType = new Type( Long.class );
+ Type msfType = new Type ( XMultiServiceFactory.class );
+
+ // css.uno.Type should be a valid key type
+ impl_checkMappings(
+ new Type[] { intType, longType, msfType },
+ new String[] { intType.getTypeName(), longType.getTypeName(), msfType.getTypeName() },
+ "type->string"
+ );
+
+
+ // any UNO interface type should be a valid key type.
+ // Try with some form components (just because I like form components :), and the very first application
+ // for the newly implemented map will be to map XFormComponents to drawing shapes
+ String[] serviceNames = new String[] { "CheckBox", "ComboBox", "CommandButton", "DateField", "FileControl" };
+ Object[] components = new Object[ serviceNames.length ];
+ for ( int i=0; i<serviceNames.length; ++i )
+ {
+ components[i] = getMSF().createInstance( "com.sun.star.form.component." + serviceNames[i] );
+ }
+ // "normalize" the first component, so it has the property type
+ Type formComponentType = new Type( XFormComponent.class );
+ components[0] = UnoRuntime.queryInterface( formComponentType.getZClass(), components[0] );
+ impl_checkMappings( components, serviceNames, "XFormComponent->string" );
+
+
+ // any UNO enum type should be a valid key type
+ impl_checkMappings(
+ new TypeClass[] { intType.getTypeClass(), longType.getTypeClass(), msfType.getTypeClass() },
+ new Object[] { "foo", "bar", "42" },
+ "enum->string"
+ );
+ }
+
+ private static Class<?> impl_getValueClassByPos( int _pos )
+ {
+ Class<?> valueClass = null;
+ switch ( _pos )
+ {
+ case 0: valueClass = Boolean.class; break;
+ case 1: valueClass = Short.class; break;
+ case 2: valueClass = Integer.class; break;
+ case 3: valueClass = Long.class; break;
+ case 4: valueClass = XInterface.class; break;
+ case 5: valueClass = XSet.class; break;
+ case 6: valueClass = XContainer.class; break;
+ case 7: valueClass = XIdentifierAccess.class; break;
+ case 8: valueClass = XElementAccess.class; break;
+ case 9: valueClass = com.sun.star.uno.Exception.class; break;
+ case 10: valueClass = com.sun.star.uno.RuntimeException.class; break;
+ case 11: valueClass = EventObject.class; break;
+ case 12: valueClass = ContainerEvent.class; break;
+ case 13: valueClass = Object.class; break;
+ default:
+ fail( "internal error: wrong position for getValueClass" );
+ }
+ return valueClass;
+ }
+
+ private Object impl_getSomeValueByTypePos( int _pos )
+ {
+ Object someValue = null;
+ switch ( _pos )
+ {
+ case 0: someValue = Boolean.FALSE; break;
+ case 1: someValue = Short.valueOf( (short)0 ); break;
+ case 2: someValue = Integer.valueOf( 0 ); break;
+ case 3: someValue = Long.valueOf( 0 ); break;
+ case 4: someValue = UnoRuntime.queryInterface( XInterface.class, new DummyInterface() ); break;
+ case 5: someValue = UnoRuntime.queryInterface( XSet.class, new DummySet() ); break;
+ case 6: someValue = UnoRuntime.queryInterface( XContainer.class, new DummyContainer() ); break;
+ case 7: someValue = UnoRuntime.queryInterface( XIdentifierAccess.class, new DummyIdentifierAccess() ); break;
+ case 8: someValue = UnoRuntime.queryInterface( XElementAccess.class, new DummyElementAccess() ); break;
+ case 9: someValue = new com.sun.star.uno.Exception(); break;
+ case 10: someValue = new com.sun.star.uno.RuntimeException(); break;
+ case 11: someValue = new EventObject(); break;
+ case 12: someValue = new ContainerEvent(); break;
+ case 13: someValue = new Locale(); break; // just use *any* value which does not conflict with the others
+ default:
+ fail( "internal error: wrong position for getSomeValue" );
+ }
+ return someValue;
+ }
+
+ private static class DummyInterface implements XInterface
+ {
+ }
+
+ private static class DummySet implements XSet
+ {
+ public boolean has( Object arg0 ) { throw new UnsupportedOperationException( "Not implemented." ); }
+ public void insert( Object arg0 ) { throw new UnsupportedOperationException( "Not implemented." ); }
+ public void remove( Object arg0 ) { throw new UnsupportedOperationException( "Not implemented." ); }
+ public XEnumeration createEnumeration() { throw new UnsupportedOperationException( "Not implemented." ); }
+ public Type getElementType() { throw new UnsupportedOperationException( "Not implemented." ); }
+ public boolean hasElements() { throw new UnsupportedOperationException( "Not implemented." ); }
+ }
+
+ private class DummyContainer implements XContainer
+ {
+ public void addContainerListener( XContainerListener arg0 ) { throw new UnsupportedOperationException( "Not implemented." ); }
+ public void removeContainerListener( XContainerListener arg0 ) { throw new UnsupportedOperationException( "Not implemented." ); }
+ }
+
+ private class DummyIdentifierAccess implements XIdentifierAccess
+ {
+ public Object getByIdentifier( int arg0 ) { throw new UnsupportedOperationException( "Not implemented." ); }
+ public int[] getIdentifiers() { throw new UnsupportedOperationException( "Not implemented." ); }
+ public Type getElementType() { throw new UnsupportedOperationException( "Not implemented." ); }
+ public boolean hasElements() { throw new UnsupportedOperationException( "Not implemented." ); }
+ }
+
+ private class DummyElementAccess implements XElementAccess
+ {
+ public Type getElementType() { throw new UnsupportedOperationException( "Not implemented." ); }
+ public boolean hasElements() { throw new UnsupportedOperationException( "Not implemented." ); }
+ }
+
+ @Test public void testValueTypes() throws com.sun.star.uno.Exception
+ {
+ // type compatibility matrix: rows are the value types used to create the map,
+ // columns are the value types fed into the map. A value "1" means the respective type
+ // should be accepted.
+ Integer[][] typeCompatibility = new Integer[][] {
+ /* boolean */ new Integer[] { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* short */ new Integer[] { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* int */ new Integer[] { 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* long */ new Integer[] { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* XInterface */ new Integer[] { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 },
+ /* XSet */ new Integer[] { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* XContainer */ new Integer[] { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 },
+ /* XIdentifierAccess */ new Integer[] { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
+ /* XElementAccess */ new Integer[] { 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0 },
+ /* Exception */ new Integer[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0 },
+ /* RuntimeException */ new Integer[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 },
+ /* EventObject */ new Integer[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0 },
+ /* ContainerEvent */ new Integer[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 },
+ /* any */ new Integer[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
+ };
+ // several asects are checked with this compatibility matrix:
+ // - if a map's value type is a scalar type, or a string, then nothing but this
+ // type should be accepted
+ // - if a map's value type is an interface type, then values should be accepted if
+ // they contain a derived interface, or the interface itself, or if they can be
+ // queried for this interface (actually, the latter rule is not tested with the
+ // above matrix)
+ // - if a map's value type is a struct or exception, then values should be accepted
+ // if they are of the given type, or of a derived type.
+ // - if a map's value type is "any", then, well, any value should be accepted
+
+ for ( int valueTypePos = 0; valueTypePos != typeCompatibility.length; ++valueTypePos )
+ {
+ com.sun.star.container.EnumerableMap.create( connection.getComponentContext(),
+ new Type( Integer.class ), new Type( impl_getValueClassByPos( valueTypePos ) ) );
+
+ for ( int checkTypePos = 0; checkTypePos != typeCompatibility[valueTypePos].length; ++checkTypePos )
+ {
+ impl_getSomeValueByTypePos( checkTypePos );
+ if ( typeCompatibility[valueTypePos][checkTypePos] != 0 )
+ {
+ // expected to succeed
+//? assureException(
+//? "(" + valueTypePos + "," + checkTypePos + ") putting an " +
+//? AnyConverter.getType( value ).getTypeName() + ", where " +
+//? map.getValueType().getTypeName() + " is expected, should succeed",
+//? map, "put", new Class[] { Object.class, Object.class }, new Object[] { key, value },
+//? null );
+ }
+ else
+ {
+ // expected to fail
+//? assureException(
+//? "(" + valueTypePos + "," + checkTypePos + ") putting an " +
+//? AnyConverter.getType( value ).getTypeName() + ", where " +
+//? map.getValueType().getTypeName() + " is expected, should not succeed",
+//? map, "put", new Class[] { Object.class, Object.class }, new Object[] { key, value },
+//? IllegalTypeException.class );
+ }
+ }
+ }
+ }
+
+ private interface CompareEqual
+ {
+ boolean areEqual( Object _lhs, Object _rhs );
+ }
+
+ private class DefaultCompareEqual implements CompareEqual
+ {
+ public boolean areEqual( Object _lhs, Object _rhs )
+ {
+ return _lhs.equals( _rhs );
+ }
+ }
+
+ private class PairCompareEqual implements CompareEqual
+ {
+ public boolean areEqual( Object _lhs, Object _rhs )
+ {
+ Pair< ?, ? > lhs = (Pair< ?, ? >)_lhs;
+ Pair< ?, ? > rhs = (Pair< ?, ? >)_rhs;
+ return lhs.First.equals( rhs.First ) && lhs.Second.equals( rhs.Second );
+ }
+ }
+
+ private void impl_verifyEnumerationContent( XEnumeration _enum, final Object[] _expectedElements, final String _context )
+ throws com.sun.star.uno.Exception
+ {
+ // since we cannot assume the map to preserve the ordering in which the elements where inserted,
+ // we can only verify that all elements exist as expected, plus *no more* elements than expected
+ // are provided by the enumeration
+ Set<Integer> set = new HashSet<Integer>();
+ for ( int i=0; i<_expectedElements.length; ++i )
+ {
+ set.add( i );
+ }
+
+ CompareEqual comparator = _expectedElements[0].getClass().equals( Pair.class )
+ ? new PairCompareEqual()
+ : new DefaultCompareEqual();
+
+ for ( int i=0; i<_expectedElements.length; ++i )
+ {
+ assertTrue( _context + ": too few elements in the enumeration (still " + ( _expectedElements.length - i ) + " to go)",
+ _enum.hasMoreElements() );
+
+ Object nextElement = _enum.nextElement();
+ if ( nextElement.getClass().equals( Any.class ) )
+ {
+ nextElement = ((Any)nextElement).getObject();
+ }
+
+ int foundPos = -1;
+ for ( int j=0; j<_expectedElements.length; ++j )
+ {
+ if ( comparator.areEqual( _expectedElements[j], nextElement ) )
+ {
+ foundPos = j;
+ break;
+ }
+ }
+
+ assertTrue( _context + ": '" + nextElement.toString() + "' is not expected in the enumeration",
+ set.contains( foundPos ) );
+ set.remove( foundPos );
+ }
+ assertTrue( _context + ": too many elements returned by the enumeration", set.isEmpty() );
+ }
+
+ @Test public void testEnumerations() throws com.sun.star.uno.Exception
+ {
+ // fill a map
+ final String[] keys = new String[] { "This", "is", "an", "enumeration", "test" };
+ final String[] values = new String[] { "for", "the", "map", "implementation", "." };
+ XEnumerableMap map = com.sun.star.container.EnumerableMap.create( connection.getComponentContext(), new Type( String.class ), new Type( String.class ) );
+ impl_putAll( map, keys, values );
+
+ final Pair< ?, ? >[] paired = new Pair< ?, ? >[ keys.length ];
+ for ( int i=0; i<keys.length; ++i )
+ {
+ paired[i] = new Pair< Object, Object >( keys[i], values[i] );
+ }
+
+ // create non-isolated enumerators, and check their content
+ XEnumeration enumerateKeys = map.createKeyEnumeration( false );
+ XEnumeration enumerateValues = map.createValueEnumeration( false );
+ XEnumeration enumerateAll = map.createElementEnumeration( false );
+ impl_verifyEnumerationContent( enumerateKeys, keys, "key enumeration" );
+ impl_verifyEnumerationContent( enumerateValues, values, "value enumeration" );
+ impl_verifyEnumerationContent( enumerateAll, paired, "content enumeration" );
+
+ // all enumerators above have been created as non-isolated iterators, so they're expected to die when
+ // the underlying map changes
+ map.remove( keys[0] );
+//? assureException( enumerateKeys, "hasMoreElements", new Object[] {}, DisposedException.class );
+//? assureException( enumerateValues, "hasMoreElements", new Object[] {}, DisposedException.class );
+//? assureException( enumerateAll, "hasMoreElements", new Object[] {}, DisposedException.class );
+
+ // now try with isolated iterators
+ map.put( keys[0], values[0] );
+ enumerateKeys = map.createKeyEnumeration( true );
+ enumerateValues = map.createValueEnumeration( true );
+ enumerateAll = map.createElementEnumeration( true );
+ map.put( "additional", "value" );
+ impl_verifyEnumerationContent( enumerateKeys, keys, "key enumeration" );
+ impl_verifyEnumerationContent( enumerateValues, values, "value enumeration" );
+ impl_verifyEnumerationContent( enumerateAll, paired, "content enumeration" );
+ }
+
+ @Test public void testSpecialValues() throws com.sun.star.uno.Exception
+ {
+ final Double[] keys = new Double[] { Double.valueOf( 0 ), Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY };
+ final Double[] values = new Double[] { Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, Double.valueOf( 0 ) };
+
+ XEnumerableMap map = com.sun.star.container.EnumerableMap.create( connection.getComponentContext(), new Type( Double.class ), new Type( Double.class ) );
+ impl_putAll( map, keys, values );
+
+ assertTrue( "containsKey( Double.+INF failed", map.containsKey( Double.POSITIVE_INFINITY ) );
+ assertTrue( "containsKey( Double.-INF failed", map.containsKey( Double.NEGATIVE_INFINITY ) );
+ assertTrue( "containsKey( 0 ) failed", map.containsKey( Double.valueOf( 0 ) ) );
+
+ assertTrue( "containsValue( Double.+INF ) failed", map.containsValue( Double.POSITIVE_INFINITY ) );
+ assertTrue( "containsValue( Double.-INF ) failed", map.containsValue( Double.NEGATIVE_INFINITY ) );
+ assertTrue( "containsValue( 0 ) failed", map.containsValue( Double.valueOf( 0 ) ) );
+
+ // put and containsKey should reject Double.NaN as key
+//? assureException( "Double.NaN should not be allowed as key in a call to 'put'", map, "put",
+//? new Class[] { Object.class, Object.class }, new Object[] { Double.NaN, Double.valueOf( 0 ) },
+//? com.sun.star.lang.IllegalArgumentException.class );
+//? assureException( "Double.NaN should not be allowed as key in a call to 'containsKey'", map, "containsKey",
+//? new Class[] { Object.class }, new Object[] { Double.NaN },
+//? com.sun.star.lang.IllegalArgumentException.class );
+
+ // ditto for put and containsValue
+//? assureException( "Double.NaN should not be allowed as value in a call to 'put'", map, "put",
+//? new Class[] { Object.class, Object.class }, new Object[] { Double.valueOf( 0 ), Double.NaN },
+//? com.sun.star.lang.IllegalArgumentException.class );
+//? assureException( "Double.NaN should not be allowed as key in a call to 'containsValue'", map, "containsValue",
+//? new Class[] { Object.class }, new Object[] { Double.NaN },
+//? com.sun.star.lang.IllegalArgumentException.class );
+ }
+
+
+ private static XMultiServiceFactory getMSF()
+ {
+ return UnoRuntime.queryInterface(XMultiServiceFactory.class, connection.getComponentContext().getServiceManager());
+ }
+
+ // setup and close connections
+ @BeforeClass public static void setUpConnection() throws Exception {
+ System.out.println("setUpConnection()");
+ connection.setUp();
+ }
+
+ @AfterClass public static void tearDownConnection()
+ throws InterruptedException, com.sun.star.uno.Exception
+ {
+ System.out.println("tearDownConnection()");
+ connection.tearDown();
+ }
+
+ private static final OfficeConnection connection = new OfficeConnection();
+}
diff --git a/comphelper/qa/complex/comphelper_all.sce b/comphelper/qa/complex/comphelper_all.sce
new file mode 100644
index 0000000000..656850ab07
--- /dev/null
+++ b/comphelper/qa/complex/comphelper_all.sce
@@ -0,0 +1,18 @@
+#
+# 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 complex.comphelper.Map
diff --git a/comphelper/qa/container/comphelper_ifcontainer.cxx b/comphelper/qa/container/comphelper_ifcontainer.cxx
new file mode 100644
index 0000000000..db904e2fba
--- /dev/null
+++ b/comphelper/qa/container/comphelper_ifcontainer.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/.
+ *
+ * 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/types.h>
+
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/plugin/TestPlugIn.h>
+
+#include <com/sun/star/lang/XEventListener.hpp>
+#include <comphelper/interfacecontainer2.hxx>
+#include <cppuhelper/implbase.hxx>
+
+using namespace com::sun::star;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+
+namespace {
+
+struct ContainerStats {
+ int m_nAlive;
+ int m_nDisposed;
+ ContainerStats() : m_nAlive(0), m_nDisposed(0) {}
+};
+
+class ContainerListener : public cppu::WeakImplHelper< XEventListener >
+{
+ ContainerStats * const m_pStats;
+public:
+ explicit ContainerListener(ContainerStats *pStats)
+ : m_pStats(pStats) { m_pStats->m_nAlive++; }
+ virtual ~ContainerListener() override { m_pStats->m_nAlive--; }
+ virtual void SAL_CALL disposing( const EventObject& ) override
+ {
+ m_pStats->m_nDisposed++;
+ }
+};
+
+}
+
+namespace comphelper_ifcontainer
+{
+ const int nTests = 10;
+ class IfTest : public CppUnit::TestFixture
+ {
+ osl::Mutex m_aGuard;
+ public:
+ void testCreateDispose()
+ {
+ ContainerStats aStats;
+ comphelper::OInterfaceContainerHelper2 *pContainer;
+
+ pContainer = new comphelper::OInterfaceContainerHelper2(m_aGuard);
+
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Empty container not empty",
+ static_cast<sal_Int32>(0), pContainer->getLength());
+
+ int i;
+ for (i = 0; i < nTests; i++)
+ {
+ Reference<XEventListener> xRef = new ContainerListener(&aStats);
+ int nNewLen = pContainer->addInterface(xRef);
+
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("addition length mismatch",
+ i + 1, nNewLen);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("addition length mismatch",
+ static_cast<sal_Int32>(i + 1), pContainer->getLength());
+ }
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("alive count mismatch",
+ nTests, aStats.m_nAlive);
+
+ EventObject aObj;
+ pContainer->disposeAndClear(aObj);
+
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("dispose count mismatch",
+ nTests, aStats.m_nDisposed);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("leaked container left alive",
+ 0, aStats.m_nAlive);
+
+ delete pContainer;
+ }
+
+ void testEnumerate()
+ {
+ int i;
+ ContainerStats aStats;
+ comphelper::OInterfaceContainerHelper2 *pContainer;
+ pContainer = new comphelper::OInterfaceContainerHelper2(m_aGuard);
+
+ std::vector< Reference< XEventListener > > aListeners;
+ for (i = 0; i < nTests; i++)
+ {
+ Reference<XEventListener> xRef = new ContainerListener(&aStats);
+ pContainer->addInterface(xRef);
+ aListeners.push_back(xRef);
+ }
+ std::vector< Reference< XInterface > > aElements = pContainer->getElements();
+
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("query contents",
+ nTests, static_cast<int>(aElements.size()));
+ if (aElements.size() == nTests)
+ {
+ for (i = 0; i < nTests; i++)
+ {
+ CPPUNIT_ASSERT_MESSAGE("mismatching elements",
+ bool(aElements[i] == aListeners[i]));
+ }
+ }
+ pContainer->clear();
+
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("non-empty container post clear",
+ static_cast<sal_Int32>(0), pContainer->getLength());
+ delete pContainer;
+ }
+
+ // Automatic registration code
+ CPPUNIT_TEST_SUITE(IfTest);
+ CPPUNIT_TEST(testCreateDispose);
+ CPPUNIT_TEST(testEnumerate);
+ CPPUNIT_TEST_SUITE_END();
+ };
+} // namespace cppu_ifcontainer
+
+CPPUNIT_TEST_SUITE_REGISTRATION(comphelper_ifcontainer::IfTest);
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/qa/container/testifcontainer.cxx b/comphelper/qa/container/testifcontainer.cxx
new file mode 100644
index 0000000000..d096b8fd87
--- /dev/null
+++ b/comphelper/qa/container/testifcontainer.cxx
@@ -0,0 +1,161 @@
+/* -*- 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 <cppunit/TestAssert.h>
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include <osl/mutex.hxx>
+#include <comphelper/interfacecontainer2.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/beans/XVetoableChangeListener.hpp>
+
+using namespace ::osl;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::uno;
+
+namespace
+{
+
+class TestInterfaceContainer2: public CppUnit::TestFixture
+{
+public:
+ void test1();
+
+ CPPUNIT_TEST_SUITE(TestInterfaceContainer2);
+ CPPUNIT_TEST(test1);
+ CPPUNIT_TEST_SUITE_END();
+};
+
+class TestListener : public cppu::WeakImplHelper< XVetoableChangeListener >
+{
+public:
+ // Methods
+ virtual void SAL_CALL disposing( const css::lang::EventObject& /*Source*/ ) override
+ {
+
+ }
+
+ virtual void SAL_CALL vetoableChange( const css::beans::PropertyChangeEvent& /*aEvent*/ ) override
+ {
+
+ }
+};
+
+void TestInterfaceContainer2::test1()
+{
+ Mutex mutex;
+
+ {
+ comphelper::OInterfaceContainerHelper2 helper( mutex );
+
+ Reference< XVetoableChangeListener > r1 = new TestListener;
+ Reference< XVetoableChangeListener > r2 = new TestListener;
+ Reference< XVetoableChangeListener > r3 = new TestListener;
+
+ helper.addInterface( r1 );
+ helper.addInterface( r2 );
+ helper.addInterface( r3 );
+
+ helper.disposeAndClear( EventObject() );
+ }
+
+ {
+ comphelper::OInterfaceContainerHelper2 helper( mutex );
+
+ Reference< XVetoableChangeListener > r1 = new TestListener;
+ Reference< XVetoableChangeListener > r2 = new TestListener;
+ Reference< XVetoableChangeListener > r3 = new TestListener;
+
+ helper.addInterface( r1 );
+ helper.addInterface( r2 );
+ helper.addInterface( r3 );
+
+ comphelper::OInterfaceIteratorHelper2 iterator( helper );
+
+ while( iterator.hasMoreElements() )
+ static_cast<XVetoableChangeListener*>(iterator.next())->vetoableChange( PropertyChangeEvent() );
+
+ helper.disposeAndClear( EventObject() );
+ }
+
+ {
+ comphelper::OInterfaceContainerHelper2 helper( mutex );
+
+ Reference< XVetoableChangeListener > r1 = new TestListener;
+ Reference< XVetoableChangeListener > r2 = new TestListener;
+ Reference< XVetoableChangeListener > r3 = new TestListener;
+
+ helper.addInterface( r1 );
+ helper.addInterface( r2 );
+ helper.addInterface( r3 );
+
+ comphelper::OInterfaceIteratorHelper2 iterator( helper );
+
+ static_cast<XVetoableChangeListener*>(iterator.next())->vetoableChange( PropertyChangeEvent() );
+ iterator.remove();
+ static_cast<XVetoableChangeListener*>(iterator.next())->vetoableChange( PropertyChangeEvent() );
+ iterator.remove();
+ static_cast<XVetoableChangeListener*>(iterator.next())->vetoableChange( PropertyChangeEvent() );
+ iterator.remove();
+
+ CPPUNIT_ASSERT_EQUAL( static_cast<sal_Int32>(0), helper.getLength() );
+ helper.disposeAndClear( EventObject() );
+ }
+
+ {
+ comphelper::OInterfaceContainerHelper2 helper( mutex );
+
+ Reference< XVetoableChangeListener > r1 = new TestListener;
+ Reference< XVetoableChangeListener > r2 = new TestListener;
+ Reference< XVetoableChangeListener > r3 = new TestListener;
+
+ helper.addInterface( r1 );
+ helper.addInterface( r2 );
+ helper.addInterface( r3 );
+
+ {
+ comphelper::OInterfaceIteratorHelper2 iterator( helper );
+ while( iterator.hasMoreElements() )
+ {
+ Reference< XVetoableChangeListener > r = static_cast<XVetoableChangeListener*>(iterator.next());
+ if( r == r1 )
+ iterator.remove();
+ }
+ }
+ CPPUNIT_ASSERT_EQUAL( static_cast<sal_Int32>(2), helper.getLength() );
+ {
+ comphelper::OInterfaceIteratorHelper2 iterator( helper );
+ while( iterator.hasMoreElements() )
+ {
+ Reference< XVetoableChangeListener > r = static_cast<XVetoableChangeListener*>(iterator.next());
+ CPPUNIT_ASSERT( r != r1 );
+ CPPUNIT_ASSERT( r == r2 || r == r3 );
+ }
+ }
+
+ helper.disposeAndClear( EventObject() );
+ }
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(TestInterfaceContainer2);
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/qa/container/testifcontainer3.cxx b/comphelper/qa/container/testifcontainer3.cxx
new file mode 100644
index 0000000000..e300adeda1
--- /dev/null
+++ b/comphelper/qa/container/testifcontainer3.cxx
@@ -0,0 +1,170 @@
+/* -*- 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 <cppunit/TestAssert.h>
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include <osl/mutex.hxx>
+#include <comphelper/interfacecontainer3.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/beans/XVetoableChangeListener.hpp>
+
+using namespace ::osl;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::uno;
+
+namespace
+{
+class TestInterfaceContainer3 : public CppUnit::TestFixture
+{
+public:
+ void test1();
+
+ CPPUNIT_TEST_SUITE(TestInterfaceContainer3);
+ CPPUNIT_TEST(test1);
+ CPPUNIT_TEST_SUITE_END();
+};
+
+class TestListener : public cppu::WeakImplHelper<XVetoableChangeListener>
+{
+public:
+ // Methods
+ virtual void SAL_CALL disposing(const css::lang::EventObject& /*Source*/) override {}
+
+ virtual void SAL_CALL vetoableChange(const css::beans::PropertyChangeEvent& /*aEvent*/) override
+ {
+ }
+};
+
+void TestInterfaceContainer3::test1()
+{
+ Mutex mutex;
+
+ {
+ comphelper::OInterfaceContainerHelper3<XVetoableChangeListener> helper(mutex);
+
+ Reference<XVetoableChangeListener> r1 = new TestListener;
+ Reference<XVetoableChangeListener> r2 = new TestListener;
+ Reference<XVetoableChangeListener> r3 = new TestListener;
+
+ helper.addInterface(r1);
+ helper.addInterface(r2);
+ helper.addInterface(r3);
+
+ helper.disposeAndClear(EventObject());
+ }
+
+ {
+ comphelper::OInterfaceContainerHelper3<XVetoableChangeListener> helper(mutex);
+
+ Reference<XVetoableChangeListener> r1 = new TestListener;
+ Reference<XVetoableChangeListener> r2 = new TestListener;
+ Reference<XVetoableChangeListener> r3 = new TestListener;
+
+ helper.addInterface(r1);
+ helper.addInterface(r2);
+ helper.addInterface(r3);
+
+ comphelper::OInterfaceIteratorHelper3 iterator(helper);
+
+ while (iterator.hasMoreElements())
+ iterator.next()->vetoableChange(PropertyChangeEvent());
+
+ helper.disposeAndClear(EventObject());
+ }
+
+ {
+ comphelper::OInterfaceContainerHelper3<XVetoableChangeListener> helper(mutex);
+
+ Reference<XVetoableChangeListener> r1 = new TestListener;
+ Reference<XVetoableChangeListener> r2 = new TestListener;
+ Reference<XVetoableChangeListener> r3 = new TestListener;
+
+ helper.addInterface(r1);
+ helper.addInterface(r2);
+ helper.addInterface(r3);
+
+ comphelper::OInterfaceIteratorHelper3 iterator(helper);
+
+ iterator.next()->vetoableChange(PropertyChangeEvent());
+ iterator.remove();
+ iterator.next()->vetoableChange(PropertyChangeEvent());
+ iterator.remove();
+ iterator.next()->vetoableChange(PropertyChangeEvent());
+ iterator.remove();
+
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0), helper.getLength());
+ helper.disposeAndClear(EventObject());
+ }
+
+ {
+ comphelper::OInterfaceContainerHelper3<XVetoableChangeListener> helper(mutex);
+
+ Reference<XVetoableChangeListener> r1 = new TestListener;
+ Reference<XVetoableChangeListener> r2 = new TestListener;
+ Reference<XVetoableChangeListener> r3 = new TestListener;
+
+ helper.addInterface(r1);
+ helper.addInterface(r2);
+ helper.addInterface(r3);
+
+ {
+ comphelper::OInterfaceIteratorHelper3 iterator(helper);
+ while (iterator.hasMoreElements())
+ {
+ Reference<XVetoableChangeListener> r = iterator.next();
+ if (r == r1)
+ iterator.remove();
+ }
+ }
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2), helper.getLength());
+ {
+ comphelper::OInterfaceIteratorHelper3 iterator(helper);
+ while (iterator.hasMoreElements())
+ {
+ Reference<XVetoableChangeListener> r = iterator.next();
+ CPPUNIT_ASSERT(r != r1);
+ CPPUNIT_ASSERT(r == r2 || r == r3);
+ }
+ }
+
+ helper.disposeAndClear(EventObject());
+ }
+
+ {
+ comphelper::OInterfaceContainerHelper3<XVetoableChangeListener> helper(mutex);
+
+ Reference<XVetoableChangeListener> r1 = new TestListener;
+
+ helper.addInterface(r1);
+
+ {
+ comphelper::OInterfaceIteratorHelper3 iterator(helper);
+ iterator.next();
+ iterator.remove();
+ }
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0), helper.getLength());
+ }
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(TestInterfaceContainer3);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/qa/python/test_sequence_output_stream.py b/comphelper/qa/python/test_sequence_output_stream.py
new file mode 100644
index 0000000000..6f1294960e
--- /dev/null
+++ b/comphelper/qa/python/test_sequence_output_stream.py
@@ -0,0 +1,84 @@
+#! /usr/bin/env python
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-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/.
+#
+
+import unittest
+import uno
+
+from org.libreoffice.unotest import UnoInProcess
+
+
+class TestSequenceOutputStream(unittest.TestCase):
+ """Test com.sun.star.io.SequenceOutputStream"""
+
+ @classmethod
+ def setUpClass(cls):
+ cls._uno = UnoInProcess()
+ cls._uno.setUp()
+
+ @classmethod
+ def tearDownClass(cls):
+ cls._uno.tearDown()
+
+ def setUp(self):
+ self.data = uno.ByteSequence(b"some data")
+ try:
+ self.service_manager = self._uno.getContext().getServiceManager()
+ except:
+ raise RuntimeError("Cannot create service factory!")
+ if self.service_manager is None:
+ raise RuntimeError("Cannot create service factory!")
+
+ def test_stream(self):
+ try:
+ seq_output_stream = self.service_manager.createInstance(
+ "com.sun.star.io.SequenceOutputStream"
+ )
+ seq_output_stream.writeBytes(self.data)
+
+ # Append the same content once again
+ seq_output_stream.writeBytes(self.data)
+
+ written_bytes = seq_output_stream.getWrittenBytes()
+
+ self.assertEqual(
+ len(self.data) * 2,
+ len(written_bytes),
+ "SequenceOutputStream::getWrittenBytes() - wrong amount of bytes returned",
+ )
+
+ # create SequenceInputstream
+ seq_input_stream = self.service_manager.createInstanceWithArguments(
+ "com.sun.star.io.SequenceInputStream", (written_bytes,)
+ )
+
+ # read from the stream
+ nbytes_read, read_bytes = seq_input_stream.readBytes(None, len(self.data) * 2 + 1)
+ self.assertEqual(
+ len(self.data) * 2,
+ nbytes_read,
+ "SequenceInputStream::readBytes() - "
+ f"wrong amount of bytes returned {len(self.data) * 2} vs {nbytes_read}",
+ )
+
+ # close the streams
+ seq_output_stream.closeOutput()
+ seq_input_stream.closeInput()
+
+ expected = uno.ByteSequence(self.data.value * 2)
+ self.assertEqual(expected, written_bytes, "Written array not identical to original.")
+ self.assertEqual(expected, read_bytes, "Read array not identical to original.")
+ except Exception as e:
+ self.fail(f"Exception: {e}")
+
+
+if __name__ == "__main__":
+ unittest.main()
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/comphelper/qa/string/NaturalStringSortTest.cxx b/comphelper/qa/string/NaturalStringSortTest.cxx
new file mode 100644
index 0000000000..bfdcaff6e1
--- /dev/null
+++ b/comphelper/qa/string/NaturalStringSortTest.cxx
@@ -0,0 +1,95 @@
+/* -*- 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 <comphelper/string.hxx>
+#include <comphelper/processfactory.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/i18n/CharType.hpp>
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <com/sun/star/i18n/XCollator.hpp>
+
+#include <unotest/bootstrapfixturebase.hxx>
+#include <cppunit/TestAssert.h>
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/plugin/TestPlugIn.h>
+#include <rtl/ustring.hxx>
+
+using namespace css;
+
+namespace
+{
+class TestStringNaturalCompare : public test::BootstrapFixtureBase
+{
+public:
+ void testNatural()
+ {
+ lang::Locale aLocale;
+ aLocale.Language = "en";
+ aLocale.Country = "US";
+
+ comphelper::string::NaturalStringSorter aSorter(comphelper::getProcessComponentContext(),
+ aLocale);
+
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(+0), aSorter.compare("ABC", "ABC"));
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(+1), aSorter.compare("ABC", "abc"));
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(-1), aSorter.compare("abc", "ABC"));
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(+1), aSorter.compare("alongstring", "alongerstring"));
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(-1), aSorter.compare("alongerstring", "alongstring"));
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(-1), aSorter.compare("Heading 9", "Heading 10"));
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(+1), aSorter.compare("Heading 10", "Heading 9"));
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(-1), aSorter.compare("July, the 4th", "July, the 10th"));
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(+1), aSorter.compare("July, the 10th", "July, the 4th"));
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(-1), aSorter.compare("abc08", "abc010"));
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(+1), aSorter.compare("abc010", "abc08"));
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(+0), aSorter.compare("apple10apple", "apple10apple"));
+
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(+1), aSorter.compare("KA1", "KA0"));
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(+0), aSorter.compare("KA1", "KA1"));
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(-1), aSorter.compare("KA1", "KA2"));
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(+1), aSorter.compare("KA50", "KA5"));
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(-1), aSorter.compare("KA50", "KA100"));
+
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(+1), aSorter.compare("1", "0"));
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(+0), aSorter.compare("1", "1"));
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(-1), aSorter.compare("1", "2"));
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(+1), aSorter.compare("11", "1"));
+
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(-1), aSorter.compare("50", "100"));
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(-1), aSorter.compare("0", "100000"));
+
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(-1), aSorter.compare("0", "A"));
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(+1), aSorter.compare("A", "0"));
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(+1), aSorter.compare("A", "99"));
+
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(+1), aSorter.compare("00ABC2", "00ABC1"));
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(-1), aSorter.compare("00ABC1", "00ABC2"));
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(+1), aSorter.compare("00ABC11", "00ABC2"));
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(-1), aSorter.compare("00ABC2", "00ABC11"));
+ }
+
+ CPPUNIT_TEST_SUITE(TestStringNaturalCompare);
+ CPPUNIT_TEST(testNatural);
+ CPPUNIT_TEST_SUITE_END();
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(TestStringNaturalCompare);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/qa/string/test_string.cxx b/comphelper/qa/string/test_string.cxx
new file mode 100644
index 0000000000..5d25a64da8
--- /dev/null
+++ b/comphelper/qa/string/test_string.cxx
@@ -0,0 +1,246 @@
+/* -*- 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 <iterator>
+
+#include <comphelper/string.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/i18n/CharType.hpp>
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <com/sun/star/i18n/XCollator.hpp>
+
+#include <cppunit/TestAssert.h>
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/plugin/TestPlugIn.h>
+#include <rtl/string.hxx>
+#include <rtl/ustring.hxx>
+
+namespace {
+
+class TestString: public CppUnit::TestFixture
+{
+public:
+ void testStripStart();
+ void testStripEnd();
+ void testStrip();
+ void testToken();
+ void testTokenCount();
+ void testDecimalStringToNumber();
+ void testIsdigitAsciiString();
+ void testReverseString();
+ void testReverseCodePoints();
+ void testSplit();
+ void testRemoveAny();
+
+ CPPUNIT_TEST_SUITE(TestString);
+ CPPUNIT_TEST(testStripStart);
+ CPPUNIT_TEST(testStripEnd);
+ CPPUNIT_TEST(testStrip);
+ CPPUNIT_TEST(testToken);
+ CPPUNIT_TEST(testTokenCount);
+ CPPUNIT_TEST(testDecimalStringToNumber);
+ CPPUNIT_TEST(testIsdigitAsciiString);
+ CPPUNIT_TEST(testReverseString);
+ CPPUNIT_TEST(testReverseCodePoints);
+ CPPUNIT_TEST(testSplit);
+ CPPUNIT_TEST(testRemoveAny);
+ CPPUNIT_TEST_SUITE_END();
+};
+
+void TestString::testDecimalStringToNumber()
+{
+ OUString s1("1234");
+ CPPUNIT_ASSERT_EQUAL(sal_uInt32(1234), comphelper::string::decimalStringToNumber(s1));
+ s1 += u"\u07C6";
+ CPPUNIT_ASSERT_EQUAL(sal_uInt32(12346), comphelper::string::decimalStringToNumber(s1));
+ // Codepoints on 2 16bits words
+ s1 = u"\U0001D7FE\U0001D7F7"_ustr; // MATHEMATICAL MONOSPACE DIGIT EIGHT and ONE
+ CPPUNIT_ASSERT_EQUAL(sal_uInt32(81), comphelper::string::decimalStringToNumber(s1));
+}
+
+void TestString::testIsdigitAsciiString()
+{
+ CPPUNIT_ASSERT_EQUAL(true, comphelper::string::isdigitAsciiString("1234"));
+
+ CPPUNIT_ASSERT_EQUAL(false, comphelper::string::isdigitAsciiString("1A34"));
+
+ CPPUNIT_ASSERT_EQUAL(true, comphelper::string::isdigitAsciiString(""));
+}
+
+void TestString::testStripStart()
+{
+ OString aIn("abc"_ostr);
+ OString aOut;
+
+ aOut = ::comphelper::string::stripStart(aIn, 'b');
+ CPPUNIT_ASSERT_EQUAL("abc"_ostr, aOut);
+
+ aOut = ::comphelper::string::stripStart(aIn, 'a');
+ CPPUNIT_ASSERT_EQUAL("bc"_ostr, aOut);
+
+ aIn = "aaa"_ostr;
+ aOut = ::comphelper::string::stripStart(aIn, 'a');
+ CPPUNIT_ASSERT(aOut.isEmpty());
+
+ aIn = "aba"_ostr;
+ aOut = ::comphelper::string::stripStart(aIn, 'a');
+ CPPUNIT_ASSERT_EQUAL("ba"_ostr, aOut);
+}
+
+void TestString::testStripEnd()
+{
+ OString aIn("abc"_ostr);
+ OString aOut;
+
+ aOut = ::comphelper::string::stripEnd(aIn, 'b');
+ CPPUNIT_ASSERT_EQUAL("abc"_ostr, aOut);
+
+ aOut = ::comphelper::string::stripEnd(aIn, 'c');
+ CPPUNIT_ASSERT_EQUAL("ab"_ostr, aOut);
+
+ aIn = "aaa"_ostr;
+ aOut = ::comphelper::string::stripEnd(aIn, 'a');
+ CPPUNIT_ASSERT(aOut.isEmpty());
+
+ aIn = "aba"_ostr;
+ aOut = ::comphelper::string::stripEnd(aIn, 'a');
+ CPPUNIT_ASSERT_EQUAL("ab"_ostr, aOut);
+}
+
+void TestString::testStrip()
+{
+ OString aIn("abc"_ostr);
+ OString aOut;
+
+ aOut = ::comphelper::string::strip(aIn, 'b');
+ CPPUNIT_ASSERT_EQUAL("abc"_ostr, aOut);
+
+ aOut = ::comphelper::string::strip(aIn, 'c');
+ CPPUNIT_ASSERT_EQUAL("ab"_ostr, aOut);
+
+ aIn = "aaa"_ostr;
+ aOut = ::comphelper::string::strip(aIn, 'a');
+ CPPUNIT_ASSERT(aOut.isEmpty());
+
+ aIn = "aba"_ostr;
+ aOut = ::comphelper::string::strip(aIn, 'a');
+ CPPUNIT_ASSERT_EQUAL("b"_ostr, aOut);
+}
+
+void TestString::testToken()
+{
+ OString aIn("10.11.12"_ostr);
+ OString aOut;
+
+ aOut = aIn.getToken(-1, '.');
+ CPPUNIT_ASSERT(aOut.isEmpty());
+
+ aOut = aIn.getToken(0, '.');
+ CPPUNIT_ASSERT_EQUAL("10"_ostr, aOut);
+
+ aOut = aIn.getToken(1, '.');
+ CPPUNIT_ASSERT_EQUAL("11"_ostr, aOut);
+
+ aOut = aIn.getToken(2, '.');
+ CPPUNIT_ASSERT_EQUAL("12"_ostr, aOut);
+
+ aOut = aIn.getToken(3, '.');
+ CPPUNIT_ASSERT(aOut.isEmpty());
+}
+
+void TestString::testTokenCount()
+{
+ OString aIn("10.11.12"_ostr);
+ sal_Int32 nOut;
+
+ nOut = ::comphelper::string::getTokenCount(aIn, '.');
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(3), nOut);
+
+ nOut = ::comphelper::string::getTokenCount(aIn, 'X');
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), nOut);
+
+ nOut = ::comphelper::string::getTokenCount("", 'X');
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0), nOut);
+}
+
+void TestString::testReverseString()
+{
+ CPPUNIT_ASSERT_EQUAL(OUString(), comphelper::string::reverseString(u""));
+ CPPUNIT_ASSERT_EQUAL(OUString("cba"), comphelper::string::reverseString(u"abc"));
+ static sal_Unicode const rev[] = {'w', 0xDFFF, 0xDBFF, 'v', 0xDC00, 0xD800, 'u'};
+ CPPUNIT_ASSERT_EQUAL(
+ OUString(rev, std::size(rev)),
+ comphelper::string::reverseString(u"u\U00010000v\U0010FFFFw"));
+ static sal_Unicode const malformed[] = {0xDC00, 0xD800};
+ CPPUNIT_ASSERT_EQUAL(
+ u"\U00010000"_ustr,
+ comphelper::string::reverseString(std::u16string_view(malformed, std::size(malformed))));
+}
+
+void TestString::testReverseCodePoints() {
+ CPPUNIT_ASSERT_EQUAL(OUString(), comphelper::string::reverseCodePoints(""));
+ CPPUNIT_ASSERT_EQUAL(OUString("cba"), comphelper::string::reverseCodePoints("abc"));
+ CPPUNIT_ASSERT_EQUAL(
+ u"w\U0010FFFFv\U00010000u"_ustr,
+ comphelper::string::reverseCodePoints(u"u\U00010000v\U0010FFFFw"_ustr));
+ static sal_Unicode const malformed[] = {0xDC00, 0xD800};
+ CPPUNIT_ASSERT_EQUAL(
+ u"\U00010000"_ustr,
+ comphelper::string::reverseCodePoints(OUString(malformed, std::size(malformed))));
+}
+
+void TestString::testSplit()
+{
+ std::vector<OUString> aRet = ::comphelper::string::split(u"CTRL+ALT+F1", '+');
+ CPPUNIT_ASSERT_EQUAL(size_t(3), aRet.size());
+ CPPUNIT_ASSERT_EQUAL(OUString("CTRL"), aRet[0]);
+ CPPUNIT_ASSERT_EQUAL(OUString("ALT"), aRet[1]);
+ CPPUNIT_ASSERT_EQUAL(OUString("F1"), aRet[2]);
+}
+
+void TestString::testRemoveAny()
+{
+ using namespace ::comphelper::string;
+ OUString in("abcAAAbbC");
+ sal_Unicode const test1 [] = { 'a', 0 };
+ CPPUNIT_ASSERT_EQUAL(OUString("bcAAAbbC"), removeAny(in, test1));
+ sal_Unicode const test2 [] = { 0 };
+ CPPUNIT_ASSERT_EQUAL(in, removeAny(in, test2));
+ sal_Unicode const test3 [] = { 'A', 0 };
+ CPPUNIT_ASSERT_EQUAL(OUString("abcbbC"), removeAny(in, test3));
+ sal_Unicode const test4 [] = { 'A', 'a', 0 };
+ CPPUNIT_ASSERT_EQUAL(OUString("bcbbC"), removeAny(in, test4));
+ sal_Unicode const test5 [] = { 'C', 0 };
+ CPPUNIT_ASSERT_EQUAL(OUString("abcAAAbb"), removeAny(in, test5));
+ sal_Unicode const test6 [] = { 'X', 0 };
+ CPPUNIT_ASSERT_EQUAL(in, removeAny(in, test6));
+ sal_Unicode const test7 [] = { 'A', 'B', 'C', 'a', 'b', 'c', 0 };
+ CPPUNIT_ASSERT_EQUAL(OUString(), removeAny(in, test7));
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(TestString);
+
+}
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/qa/unit/base64_test.cxx b/comphelper/qa/unit/base64_test.cxx
new file mode 100644
index 0000000000..dc637f63f7
--- /dev/null
+++ b/comphelper/qa/unit/base64_test.cxx
@@ -0,0 +1,112 @@
+/* -*- 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/types.h>
+#include <cppunit/TestAssert.h>
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+
+#include <rtl/ustrbuf.hxx>
+
+#include <com/sun/star/uno/Sequence.hxx>
+
+#include <comphelper/base64.hxx>
+
+using namespace css;
+
+namespace
+{
+class Base64Test : public CppUnit::TestFixture
+{
+public:
+ void testBase64Encode();
+ void testBase64Decode();
+ void testBase64EncodeForOStringBuffer();
+
+ CPPUNIT_TEST_SUITE(Base64Test);
+ CPPUNIT_TEST(testBase64Encode);
+ CPPUNIT_TEST(testBase64Decode);
+ CPPUNIT_TEST(testBase64EncodeForOStringBuffer);
+ CPPUNIT_TEST_SUITE_END();
+};
+
+void Base64Test::testBase64Encode()
+{
+ OUStringBuffer aBuffer(32);
+ uno::Sequence<sal_Int8> inputSequence;
+
+ inputSequence = { 0, 0, 0, 0, 0, 1, 2, 3 };
+ comphelper::Base64::encode(aBuffer, inputSequence);
+ CPPUNIT_ASSERT_EQUAL(OUString("AAAAAAABAgM="), aBuffer.toString());
+ aBuffer.setLength(0);
+
+ inputSequence = { 5, 2, 3, 0, 0, 1, 2, 3 };
+ comphelper::Base64::encode(aBuffer, inputSequence);
+ CPPUNIT_ASSERT_EQUAL(OUString("BQIDAAABAgM="), aBuffer.toString());
+ aBuffer.setLength(0);
+
+ inputSequence = { sal_Int8(sal_uInt8(200)), 31, 77, 111, 0, 1, 2, 3 };
+ comphelper::Base64::encode(aBuffer, inputSequence);
+ CPPUNIT_ASSERT_EQUAL(OUString("yB9NbwABAgM="), aBuffer.makeStringAndClear());
+}
+
+void Base64Test::testBase64Decode()
+{
+ uno::Sequence<sal_Int8> decodedSequence;
+
+ uno::Sequence<sal_Int8> expectedSequence = { 0, 0, 0, 0, 0, 1, 2, 3 };
+ comphelper::Base64::decode(decodedSequence, u"AAAAAAABAgM=");
+ CPPUNIT_ASSERT(std::equal(std::cbegin(expectedSequence), std::cend(expectedSequence),
+ std::cbegin(decodedSequence)));
+
+ expectedSequence = { 5, 2, 3, 0, 0, 1, 2, 3 };
+ comphelper::Base64::decode(decodedSequence, u"BQIDAAABAgM=");
+ CPPUNIT_ASSERT(std::equal(std::cbegin(expectedSequence), std::cend(expectedSequence),
+ std::cbegin(decodedSequence)));
+
+ expectedSequence = { sal_Int8(sal_uInt8(200)), 31, 77, 111, 0, 1, 2, 3 };
+ comphelper::Base64::decode(decodedSequence, u"yB9NbwABAgM=");
+ CPPUNIT_ASSERT(std::equal(std::cbegin(expectedSequence), std::cend(expectedSequence),
+ std::cbegin(decodedSequence)));
+}
+
+void Base64Test::testBase64EncodeForOStringBuffer()
+{
+ OStringBuffer aBuffer(32);
+ uno::Sequence<sal_Int8> inputSequence;
+
+ inputSequence = { 0, 0, 0, 0, 0, 1, 2, 3 };
+ comphelper::Base64::encode(aBuffer, inputSequence);
+ CPPUNIT_ASSERT_EQUAL("AAAAAAABAgM="_ostr, aBuffer.toString());
+ aBuffer.setLength(0);
+
+ inputSequence = { 5, 2, 3, 0, 0, 1, 2, 3 };
+ comphelper::Base64::encode(aBuffer, inputSequence);
+ CPPUNIT_ASSERT_EQUAL("BQIDAAABAgM="_ostr, aBuffer.toString());
+ aBuffer.setLength(0);
+
+ inputSequence = { sal_Int8(sal_uInt8(200)), 31, 77, 111, 0, 1, 2, 3 };
+ comphelper::Base64::encode(aBuffer, inputSequence);
+ CPPUNIT_ASSERT_EQUAL("yB9NbwABAgM="_ostr, aBuffer.makeStringAndClear());
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(Base64Test);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/qa/unit/parallelsorttest.cxx b/comphelper/qa/unit/parallelsorttest.cxx
new file mode 100644
index 0000000000..a3618244ab
--- /dev/null
+++ b/comphelper/qa/unit/parallelsorttest.cxx
@@ -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/.
+ */
+
+#include <comphelper/parallelsort.hxx>
+#include <comphelper/threadpool.hxx>
+#include <rtl/string.hxx>
+#include <cppunit/TestAssert.h>
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/plugin/TestPlugIn.h>
+
+#include <cstdlib>
+#include <vector>
+#include <algorithm>
+#include <random>
+
+class ParallelSortTest : public CppUnit::TestFixture
+{
+public:
+ void testSortTiny();
+ void testSortMedium();
+ void testSortBig();
+
+ virtual void setUp() override;
+ virtual void tearDown() override;
+
+ CPPUNIT_TEST_SUITE(ParallelSortTest);
+ CPPUNIT_TEST(testSortTiny);
+ CPPUNIT_TEST(testSortMedium);
+ CPPUNIT_TEST(testSortBig);
+ CPPUNIT_TEST_SUITE_END();
+
+private:
+ void sortTest(size_t nLen);
+ void fillRandomUptoN(std::vector<size_t>& rVector, size_t N);
+
+ comphelper::ThreadPool* pThreadPool;
+ size_t mnThreads;
+};
+
+void ParallelSortTest::setUp()
+{
+ pThreadPool = &comphelper::ThreadPool::getSharedOptimalPool();
+ mnThreads = pThreadPool->getWorkerCount();
+}
+
+void ParallelSortTest::tearDown()
+{
+ if (pThreadPool)
+ pThreadPool->joinThreadsIfIdle();
+}
+
+void ParallelSortTest::fillRandomUptoN(std::vector<size_t>& rVector, size_t N)
+{
+ rVector.resize(N);
+ for (size_t nIdx = 0; nIdx < N; ++nIdx)
+ rVector[nIdx] = nIdx;
+ std::shuffle(rVector.begin(), rVector.end(), std::default_random_engine(42));
+}
+
+void ParallelSortTest::sortTest(size_t nLen)
+{
+ std::vector<size_t> aVector(nLen);
+ fillRandomUptoN(aVector, nLen);
+ comphelper::parallelSort(aVector.begin(), aVector.end());
+ for (size_t nIdx = 0; nIdx < nLen; ++nIdx)
+ {
+ OString aMsg = "Wrong aVector[" + OString::number(nIdx) + "]";
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(aMsg.getStr(), nIdx, aVector[nIdx]);
+ }
+}
+
+void ParallelSortTest::testSortTiny()
+{
+ sortTest(5);
+ sortTest(15);
+ sortTest(16);
+ sortTest(17);
+}
+
+void ParallelSortTest::testSortMedium()
+{
+ sortTest(1025);
+ sortTest(1029);
+ sortTest(1024 * 2 + 1);
+ sortTest(1024 * 2 + 9);
+}
+
+void ParallelSortTest::testSortBig() { sortTest(1024 * 16 + 3); }
+
+CPPUNIT_TEST_SUITE_REGISTRATION(ParallelSortTest);
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/qa/unit/propertyvalue.cxx b/comphelper/qa/unit/propertyvalue.cxx
new file mode 100644
index 0000000000..4470b28f50
--- /dev/null
+++ b/comphelper/qa/unit/propertyvalue.cxx
@@ -0,0 +1,131 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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 <cppunit/TestAssert.h>
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <cppu/unotype.hxx>
+#include <o3tl/any.hxx>
+
+using namespace com::sun::star;
+
+namespace
+{
+class MakePropertyValueTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(MakePropertyValueTest);
+ CPPUNIT_TEST(testLvalue);
+ CPPUNIT_TEST(testRvalue);
+ CPPUNIT_TEST(testBitField);
+ CPPUNIT_TEST(testJson);
+ CPPUNIT_TEST_SUITE_END();
+
+ void testLvalue()
+ {
+ sal_Int32 const i = 123;
+ auto const v = comphelper::makePropertyValue("test", i);
+ CPPUNIT_ASSERT_EQUAL(cppu::UnoType<sal_Int32>::get(), v.Value.getValueType());
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(123), *o3tl::doAccess<sal_Int32>(v.Value));
+ }
+
+ void testRvalue()
+ {
+ auto const v = comphelper::makePropertyValue("test", sal_Int32(456));
+ CPPUNIT_ASSERT_EQUAL(cppu::UnoType<sal_Int32>::get(), v.Value.getValueType());
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(456), *o3tl::doAccess<sal_Int32>(v.Value));
+ }
+
+ void testBitField()
+ {
+ struct
+ {
+ bool b : 1;
+ } s = { false };
+ auto const v = comphelper::makePropertyValue("test", s.b);
+ CPPUNIT_ASSERT_EQUAL(cppu::UnoType<bool>::get(), v.Value.getValueType());
+ CPPUNIT_ASSERT_EQUAL(false, *o3tl::doAccess<bool>(v.Value));
+ }
+
+ void testJson()
+ {
+ std::vector<beans::PropertyValue> aRet = comphelper::JsonToPropertyValues(R"json(
+{
+ "FieldType": {
+ "type": "string",
+ "value": "vnd.oasis.opendocument.field.UNHANDLED"
+ },
+ "FieldCommandPrefix": {
+ "type": "string",
+ "value": "ADDIN ZOTERO_ITEM"
+ },
+ "Fields": {
+ "type": "[][]com.sun.star.beans.PropertyValue",
+ "value": [
+ {
+ "FieldType": {
+ "type": "string",
+ "value": "vnd.oasis.opendocument.field.UNHANDLED"
+ },
+ "FieldCommand": {
+ "type": "string",
+ "value": "ADDIN ZOTERO_ITEM new command 1"
+ },
+ "Fields": {
+ "type": "string",
+ "value": "new result 1"
+ }
+ },
+ {
+ "FieldType": {
+ "type": "string",
+ "value": "vnd.oasis.opendocument.field.UNHANDLED"
+ },
+ "FieldCommandPrefix": {
+ "type": "string",
+ "value": "ADDIN ZOTERO_ITEM new command 2"
+ },
+ "Fields": {
+ "type": "string",
+ "value": "new result 2"
+ }
+ }
+ ]
+ }
+}
+)json"_ostr);
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aRet.size());
+ beans::PropertyValue aFirst = aRet[0];
+ CPPUNIT_ASSERT_EQUAL(OUString("FieldType"), aFirst.Name);
+ CPPUNIT_ASSERT_EQUAL(OUString("vnd.oasis.opendocument.field.UNHANDLED"),
+ aFirst.Value.get<OUString>());
+ beans::PropertyValue aSecond = aRet[1];
+ CPPUNIT_ASSERT_EQUAL(OUString("FieldCommandPrefix"), aSecond.Name);
+ CPPUNIT_ASSERT_EQUAL(OUString("ADDIN ZOTERO_ITEM"), aSecond.Value.get<OUString>());
+ beans::PropertyValue aThird = aRet[2];
+ CPPUNIT_ASSERT_EQUAL(OUString("Fields"), aThird.Name);
+ uno::Sequence<uno::Sequence<beans::PropertyValue>> aSeqs;
+ aThird.Value >>= aSeqs;
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2), aSeqs.getLength());
+ uno::Sequence<beans::PropertyValue> aFirstSeq = aSeqs[0];
+ CPPUNIT_ASSERT_EQUAL(OUString("FieldType"), aFirstSeq[0].Name);
+ CPPUNIT_ASSERT_EQUAL(OUString("FieldCommand"), aFirstSeq[1].Name);
+ CPPUNIT_ASSERT_EQUAL(OUString("ADDIN ZOTERO_ITEM new command 1"),
+ aFirstSeq[1].Value.get<OUString>());
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(MakePropertyValueTest);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/comphelper/qa/unit/syntaxhighlighttest.cxx b/comphelper/qa/unit/syntaxhighlighttest.cxx
new file mode 100644
index 0000000000..eab382b85a
--- /dev/null
+++ b/comphelper/qa/unit/syntaxhighlighttest.cxx
@@ -0,0 +1,117 @@
+/* -*- 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 <comphelper/syntaxhighlight.hxx>
+#include <cppunit/TestAssert.h>
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/plugin/TestPlugIn.h>
+#include <rtl/ustring.hxx>
+
+#include <vector>
+
+class SyntaxHighlightTest : public CppUnit::TestFixture
+{
+public:
+ void testBasicString();
+ void testBasicComment();
+ void testBasicCommentNewline();
+ void testBasicEmptyComment();
+ void testBasicEmptyCommentNewline();
+ void testBasic();
+
+ CPPUNIT_TEST_SUITE(SyntaxHighlightTest);
+ CPPUNIT_TEST(testBasicString);
+ CPPUNIT_TEST(testBasicComment);
+ CPPUNIT_TEST(testBasicCommentNewline);
+ CPPUNIT_TEST(testBasicEmptyComment);
+ CPPUNIT_TEST(testBasicEmptyCommentNewline);
+ CPPUNIT_TEST(testBasic);
+ CPPUNIT_TEST_SUITE_END();
+};
+
+void SyntaxHighlightTest::testBasicString() {
+ std::vector<HighlightPortion> ps;
+ SyntaxHighlighter(HighlighterLanguage::Basic).getHighlightPortions(u"\"foo\"", ps);
+ CPPUNIT_ASSERT_EQUAL(
+ static_cast<std::vector<HighlightPortion>::size_type>(1), ps.size());
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(0), ps[0].nBegin);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(5), ps[0].nEnd);
+ CPPUNIT_ASSERT_EQUAL(TokenType::String, ps[0].tokenType);
+}
+
+void SyntaxHighlightTest::testBasicComment() {
+ std::vector<HighlightPortion> ps;
+ SyntaxHighlighter(HighlighterLanguage::Basic).getHighlightPortions(u"' foo", ps);
+ CPPUNIT_ASSERT_EQUAL(
+ static_cast<std::vector<HighlightPortion>::size_type>(1), ps.size());
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(0), ps[0].nBegin);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(5), ps[0].nEnd);
+ CPPUNIT_ASSERT_EQUAL(TokenType::Comment, ps[0].tokenType);
+}
+
+void SyntaxHighlightTest::testBasicCommentNewline() {
+ std::vector<HighlightPortion> ps;
+ SyntaxHighlighter(HighlighterLanguage::Basic).getHighlightPortions(u"' foo\n", ps);
+ CPPUNIT_ASSERT_EQUAL(
+ static_cast<std::vector<HighlightPortion>::size_type>(2), ps.size());
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(0), ps[0].nBegin);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(5), ps[0].nEnd);
+ CPPUNIT_ASSERT_EQUAL(TokenType::Comment, ps[0].tokenType);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(5), ps[1].nBegin);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(6), ps[1].nEnd);
+ CPPUNIT_ASSERT_EQUAL(TokenType::EOL, ps[1].tokenType);
+}
+
+void SyntaxHighlightTest::testBasicEmptyComment() {
+ std::vector<HighlightPortion> ps;
+ SyntaxHighlighter(HighlighterLanguage::Basic).getHighlightPortions(u"'", ps);
+ CPPUNIT_ASSERT_EQUAL(
+ static_cast<std::vector<HighlightPortion>::size_type>(1), ps.size());
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(0), ps[0].nBegin);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(1), ps[0].nEnd);
+ CPPUNIT_ASSERT_EQUAL(TokenType::Comment, ps[0].tokenType);
+}
+
+void SyntaxHighlightTest::testBasicEmptyCommentNewline() {
+ std::vector<HighlightPortion> ps;
+ SyntaxHighlighter(HighlighterLanguage::Basic).getHighlightPortions(u"'\n", ps);
+ CPPUNIT_ASSERT_EQUAL(
+ static_cast<std::vector<HighlightPortion>::size_type>(2), ps.size());
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(0), ps[0].nBegin);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(1), ps[0].nEnd);
+ CPPUNIT_ASSERT_EQUAL(TokenType::Comment, ps[0].tokenType);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(1), ps[1].nBegin);
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(2), ps[1].nEnd);
+ CPPUNIT_ASSERT_EQUAL(TokenType::EOL, ps[1].tokenType);
+}
+
+void SyntaxHighlightTest::testBasic()
+{
+ OUString aBasicString(" if Mid(sText,iRun,1 )<> \" \" then Mid( sText ,iRun, 1, Chr( 1 + Asc( Mid(sText,iRun,1 )) ) '");
+
+ std::vector<HighlightPortion> aPortions;
+ SyntaxHighlighter(HighlighterLanguage::Basic).getHighlightPortions(
+ aBasicString, aPortions );
+
+ sal_Int32 prevEnd = 0;
+ for (auto const& portion : aPortions)
+ {
+ CPPUNIT_ASSERT_EQUAL(prevEnd, portion.nBegin);
+ CPPUNIT_ASSERT(portion.nBegin < portion.nEnd);
+ prevEnd = portion.nEnd;
+ }
+ CPPUNIT_ASSERT_EQUAL(aBasicString.getLength(), prevEnd);
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(SyntaxHighlightTest);
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/qa/unit/test_guards.cxx b/comphelper/qa/unit/test_guards.cxx
new file mode 100644
index 0000000000..83034a2dcc
--- /dev/null
+++ b/comphelper/qa/unit/test_guards.cxx
@@ -0,0 +1,88 @@
+/* -*- 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 <comphelper/flagguard.hxx>
+#include <unotest/bootstrapfixturebase.hxx>
+
+CPPUNIT_TEST_FIXTURE(CppUnit::TestFixture, testScopeGuard)
+{
+ // Test that comphelper::ScopeGuard executes its parameter on destruction
+
+ // initial value "true", out-of-scope ScopeGuard function executes and changes the value to "false"
+ bool bFlag = true;
+ {
+ comphelper::ScopeGuard aGuard([&bFlag] { bFlag = false; });
+ CPPUNIT_ASSERT(bFlag);
+ }
+ CPPUNIT_ASSERT(!bFlag);
+}
+
+CPPUNIT_TEST_FIXTURE(CppUnit::TestFixture, testFlagGuard)
+{
+ // Test that comphelper::FlagGuard properly sets and resets the flag
+
+ // initial value "false", change to "true", out-of-scope change to "false"
+ bool bFlag = false;
+ {
+ comphelper::FlagGuard aGuard(bFlag);
+ CPPUNIT_ASSERT(bFlag);
+ }
+ // comphelper::FlagGuard must reset flag to false on destruction unconditionally
+ CPPUNIT_ASSERT(!bFlag);
+
+ // initial value "true", retain the value at "true", out-of-scope change to "false"
+ bFlag = true;
+ {
+ comphelper::FlagGuard aGuard(bFlag);
+ CPPUNIT_ASSERT(bFlag);
+ }
+ // comphelper::FlagGuard must reset flag to false on destruction unconditionally
+ CPPUNIT_ASSERT(!bFlag);
+}
+
+CPPUNIT_TEST_FIXTURE(CppUnit::TestFixture, testFlagRestorationGuard)
+{
+ // Test that comphelper::FlagRestorationGuard properly sets and resets the flag
+
+ // initial value "true", change to "false", out-of-scope change to "true"
+
+ bool bFlag = true;
+ {
+ comphelper::FlagRestorationGuard aGuard(bFlag, false);
+ CPPUNIT_ASSERT(!bFlag);
+ }
+ // comphelper::FlagRestorationGuard must reset flag to initial state on destruction
+ CPPUNIT_ASSERT(bFlag);
+}
+
+CPPUNIT_TEST_FIXTURE(CppUnit::TestFixture, testValueRestorationGuard)
+{
+ // Test that comphelper::ValueRestorationGuard properly sets and resets the (int) value
+
+ int value = 199;
+
+ // set value and restore after scope ends
+ {
+ CPPUNIT_ASSERT_EQUAL(199, value);
+ comphelper::ValueRestorationGuard aGuard(value, 100);
+ CPPUNIT_ASSERT_EQUAL(100, value);
+ }
+ CPPUNIT_ASSERT_EQUAL(199, value);
+
+ // set value, manually setto another value and restore after scope ends
+ {
+ CPPUNIT_ASSERT_EQUAL(199, value);
+ comphelper::ValueRestorationGuard aGuard(value, 100);
+ CPPUNIT_ASSERT_EQUAL(100, value);
+ value = 200;
+ }
+ CPPUNIT_ASSERT_EQUAL(199, value);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/qa/unit/test_hash.cxx b/comphelper/qa/unit/test_hash.cxx
new file mode 100644
index 0000000000..64815ee56d
--- /dev/null
+++ b/comphelper/qa/unit/test_hash.cxx
@@ -0,0 +1,130 @@
+/* -*- 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 <config_oox.h>
+#include <comphelper/hash.hxx>
+#include <comphelper/docpasswordhelper.hxx>
+
+#include <rtl/ustring.hxx>
+#include <iomanip>
+
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+
+#if USE_TLS_NSS
+#include <nss.h>
+#endif
+
+class TestHash : public CppUnit::TestFixture
+{
+public:
+ void testMD5();
+ void testSHA1();
+ void testSHA256();
+ void testSHA512();
+ void testSHA512_NoSaltNoSpin();
+ void testSHA512_saltspin();
+
+ virtual void tearDown()
+ {
+#if USE_TLS_NSS
+ NSS_Shutdown();
+#endif
+ }
+ CPPUNIT_TEST_SUITE(TestHash);
+ CPPUNIT_TEST(testMD5);
+ CPPUNIT_TEST(testSHA1);
+ CPPUNIT_TEST(testSHA256);
+ CPPUNIT_TEST(testSHA512);
+ CPPUNIT_TEST(testSHA512_NoSaltNoSpin);
+ CPPUNIT_TEST(testSHA512_saltspin);
+ CPPUNIT_TEST_SUITE_END();
+};
+
+namespace {
+
+std::string tostring(const std::vector<unsigned char>& a)
+{
+ std::stringstream aStrm;
+ for (auto& i:a)
+ {
+ aStrm << std::setw(2) << std::setfill('0') << std::hex << static_cast<int>(i);
+ }
+
+ return aStrm.str();
+}
+
+}
+
+void TestHash::testMD5()
+{
+ comphelper::Hash aHash(comphelper::HashType::MD5);
+ const char* const pInput = "";
+ aHash.update(reinterpret_cast<const unsigned char*>(pInput), 0);
+ std::vector<unsigned char> calculate_hash = aHash.finalize();
+ CPPUNIT_ASSERT_EQUAL(size_t(16), calculate_hash.size());
+ CPPUNIT_ASSERT_EQUAL(std::string("d41d8cd98f00b204e9800998ecf8427e"), tostring(calculate_hash));
+}
+
+void TestHash::testSHA1()
+{
+ comphelper::Hash aHash(comphelper::HashType::SHA1);
+ const char* const pInput = "";
+ aHash.update(reinterpret_cast<const unsigned char*>(pInput), 0);
+ std::vector<unsigned char> calculate_hash = aHash.finalize();
+ CPPUNIT_ASSERT_EQUAL(size_t(20), calculate_hash.size());
+ CPPUNIT_ASSERT_EQUAL(std::string("da39a3ee5e6b4b0d3255bfef95601890afd80709"), tostring(calculate_hash));
+}
+
+void TestHash::testSHA256()
+{
+ comphelper::Hash aHash(comphelper::HashType::SHA256);
+ const char* const pInput = "";
+ aHash.update(reinterpret_cast<const unsigned char*>(pInput), 0);
+ std::vector<unsigned char> calculate_hash = aHash.finalize();
+ CPPUNIT_ASSERT_EQUAL(size_t(32), calculate_hash.size());
+ CPPUNIT_ASSERT_EQUAL(std::string("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"), tostring(calculate_hash));
+}
+
+void TestHash::testSHA512()
+{
+ comphelper::Hash aHash(comphelper::HashType::SHA512);
+ const char* const pInput = "";
+ aHash.update(reinterpret_cast<const unsigned char*>(pInput), 0);
+ std::vector<unsigned char> calculate_hash = aHash.finalize();
+ CPPUNIT_ASSERT_EQUAL(size_t(64), calculate_hash.size());
+ std::string aStr("cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e");
+ CPPUNIT_ASSERT_EQUAL(aStr, tostring(calculate_hash));
+}
+
+// Must be identical to testSHA512()
+void TestHash::testSHA512_NoSaltNoSpin()
+{
+ const char* const pInput = "";
+ std::vector<unsigned char> calculate_hash =
+ comphelper::Hash::calculateHash( reinterpret_cast<const unsigned char*>(pInput), 0,
+ nullptr, 0, 0, comphelper::Hash::IterCount::NONE, comphelper::HashType::SHA512);
+ CPPUNIT_ASSERT_EQUAL(size_t(64), calculate_hash.size());
+ std::string aStr("cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e");
+ CPPUNIT_ASSERT_EQUAL(aStr, tostring(calculate_hash));
+}
+
+// Password, salt, hash and spin count taken from OOXML sheetProtection of
+// tdf#104250 https://bugs.documentfoundation.org/attachment.cgi?id=129104
+void TestHash::testSHA512_saltspin()
+{
+ const OUString aHash = comphelper::DocPasswordHelper::GetOoxHashAsBase64( "pwd", u"876MLoKTq42+/DLp415iZQ==", 100000,
+ comphelper::Hash::IterCount::APPEND, u"SHA-512");
+ CPPUNIT_ASSERT_EQUAL(OUString("5l3mgNHXpWiFaBPv5Yso1Xd/UifWvQWmlDnl/hsCYbFT2sJCzorjRmBCQ/3qeDu6Q/4+GIE8a1DsdaTwYh1q2g=="), aHash);
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(TestHash);
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/qa/unit/test_traceevent.cxx b/comphelper/qa/unit/test_traceevent.cxx
new file mode 100644
index 0000000000..34d10f519d
--- /dev/null
+++ b/comphelper/qa/unit/test_traceevent.cxx
@@ -0,0 +1,125 @@
+/* -*- 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 <comphelper/profilezone.hxx>
+#include <comphelper/traceevent.hxx>
+
+#include <rtl/ustring.hxx>
+
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+
+class TestTraceEvent : public CppUnit::TestFixture
+{
+public:
+ void test();
+
+ CPPUNIT_TEST_SUITE(TestTraceEvent);
+ CPPUNIT_TEST(test);
+ CPPUNIT_TEST_SUITE_END();
+};
+
+namespace
+{
+void trace_event_test()
+{
+ {
+ // When we start recording is off and this will not generate any 'X' event when we leave the scope
+ comphelper::ProfileZone aZone0("test0");
+
+ // This will not generate any 'b' and 'e' events either
+ auto pAsync1(std::make_shared<comphelper::AsyncEvent>("async1"));
+
+ {
+ // No 'X' by this either
+ comphelper::ProfileZone aZone1("block1");
+
+ // Now we turn on recording
+ comphelper::TraceEvent::startRecording();
+ }
+
+ // This will generate an 'i' event for instant1
+ comphelper::TraceEvent::addInstantEvent("instant1");
+
+ std::shared_ptr<comphelper::AsyncEvent> pAsync25;
+ {
+ comphelper::ProfileZone aZone2("block2");
+
+ // This does not generate any 'e' event as it was created when recording was off
+ // And the nested async2 object will thus not generate anything either
+ pAsync1.reset();
+
+ // This will generate 'b' event and an 'e' event when the pointer is reset or goes out of scope
+ pAsync25 = std::make_shared<comphelper::AsyncEvent>("async2.5");
+
+ // Leaving this scope will generate an 'X' event for block2
+ }
+
+ // This will generate a 'b' event for async3
+ std::map<OUString, OUString> aArgsAsync3({ { "foo", "bar" }, { "tem", "42" } });
+ auto pAsync3(std::make_shared<comphelper::AsyncEvent>("async3", aArgsAsync3));
+
+ {
+ comphelper::ProfileZone aZone3("block3");
+
+ // Leaving this scope will generate an 'X' event for block3
+ }
+
+ // This will generate an 'e' event for async2.5
+ pAsync25.reset();
+
+ comphelper::ProfileZone aZone4("test2");
+
+ // This will generate an 'i' event for instant2"
+ std::map<OUString, OUString> aArgsInstant2({ { "foo2", "bar2" }, { "tem2", "42" } });
+ comphelper::TraceEvent::addInstantEvent("instant2", aArgsInstant2);
+
+ // Leaving this scope will generate 'X' events for test2 and a
+ // 'e' event for async4in3, async7in3, and async3.
+ }
+
+ // This incorrect use of overlapping (not nested) ProfileZones
+ // will generate a SAL_WARN but should not crash
+ auto p1 = new comphelper::ProfileZone("error1");
+ auto p2 = new comphelper::ProfileZone("error2");
+ delete p1;
+ delete p2;
+}
+}
+
+void TestTraceEvent::test()
+{
+ trace_event_test();
+ auto aEvents = comphelper::TraceEvent::getEventVectorAndClear();
+ for (const auto& s : aEvents)
+ {
+ std::cerr << s << "\n";
+ }
+
+ CPPUNIT_ASSERT_EQUAL(9, static_cast<int>(aEvents.size()));
+
+ CPPUNIT_ASSERT(aEvents[0].startsWith("{\"name:\"instant1\",\"ph\":\"i\","));
+ CPPUNIT_ASSERT(aEvents[1].startsWith("{\"name\":\"async2.5\",\"ph\":\"S\",\"id\":1,"));
+ CPPUNIT_ASSERT(aEvents[2].startsWith("{\"name\":\"block2\",\"ph\":\"X\","));
+ CPPUNIT_ASSERT(aEvents[3].startsWith(
+ "{\"name\":\"async3\",\"ph\":\"S\",\"id\":2,\"args\":{\"foo\":\"bar\",\"tem\":\"42\"},"));
+ CPPUNIT_ASSERT(aEvents[4].startsWith("{\"name\":\"block3\",\"ph\":\"X\","));
+ CPPUNIT_ASSERT(aEvents[5].startsWith("{\"name\":\"async2.5\",\"ph\":\"F\",\"id\":1,"));
+ CPPUNIT_ASSERT(aEvents[6].startsWith(
+ "{\"name:\"instant2\",\"ph\":\"i\",\"args\":{\"foo2\":\"bar2\",\"tem2\":\"42\"},"));
+ CPPUNIT_ASSERT(aEvents[7].startsWith("{\"name\":\"test2\",\"ph\":\"X\""));
+ CPPUNIT_ASSERT(aEvents[8].startsWith(
+ "{\"name\":\"async3\",\"ph\":\"F\",\"id\":2,\"args\":{\"foo\":\"bar\",\"tem\":\"42\"},"));
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(TestTraceEvent);
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/qa/unit/threadpooltest.cxx b/comphelper/qa/unit/threadpooltest.cxx
new file mode 100644
index 0000000000..13eaf210a1
--- /dev/null
+++ b/comphelper/qa/unit/threadpooltest.cxx
@@ -0,0 +1,169 @@
+/* -*- 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 <comphelper/threadpool.hxx>
+#include <cppunit/TestAssert.h>
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/plugin/TestPlugIn.h>
+#include <tools/time.hxx>
+#include <osl/thread.hxx>
+
+#include <stdlib.h>
+#include <atomic>
+#include <cstddef>
+#include <thread>
+#include <mutex>
+
+class ThreadPoolTest : public CppUnit::TestFixture
+{
+public:
+ void testPreferredConcurrency();
+ void testWorkerUsage();
+ void testTasksInThreads();
+ void testNoThreads();
+ void testDedicatedPool();
+
+ CPPUNIT_TEST_SUITE(ThreadPoolTest);
+ CPPUNIT_TEST(testPreferredConcurrency);
+ CPPUNIT_TEST(testWorkerUsage);
+ CPPUNIT_TEST(testTasksInThreads);
+ CPPUNIT_TEST(testNoThreads);
+ CPPUNIT_TEST(testDedicatedPool);
+ CPPUNIT_TEST_SUITE_END();
+};
+
+void ThreadPoolTest::testPreferredConcurrency()
+{
+ // Check default.
+ auto nThreads = comphelper::ThreadPool::getPreferredConcurrency();
+ std::size_t nExpected = 4; // UTs are capped to 4.
+ CPPUNIT_ASSERT_MESSAGE("Expected no more than 4 threads", nExpected >= nThreads);
+
+#ifndef _WIN32
+ // The result should be cached, so this should change anything.
+ nThreads = std::thread::hardware_concurrency() * 2;
+ setenv("MAX_CONCURRENCY", std::to_string(nThreads).c_str(), true);
+ nThreads = comphelper::ThreadPool::getPreferredConcurrency();
+ CPPUNIT_ASSERT_MESSAGE("Expected no more than hardware threads",
+ nThreads <= std::thread::hardware_concurrency());
+
+ // Revert and check. Again, nothing should change.
+ unsetenv("MAX_CONCURRENCY");
+ nThreads = comphelper::ThreadPool::getPreferredConcurrency();
+ CPPUNIT_ASSERT_MESSAGE("Expected no more than 4 threads", nExpected >= nThreads);
+#endif
+}
+
+namespace
+{
+class UsageTask : public comphelper::ThreadTask
+{
+public:
+ UsageTask(const std::shared_ptr<comphelper::ThreadTaskTag>& pTag)
+ : ThreadTask(pTag)
+ {
+ }
+ virtual void doWork()
+ {
+ ++count;
+ mutex.lock();
+ mutex.unlock();
+ }
+ static inline std::atomic<int> count = 0;
+ static inline std::mutex mutex;
+};
+} // namespace
+
+void ThreadPoolTest::testWorkerUsage()
+{
+ // Create tasks for each available worker. Lock a shared mutex before that to make all
+ // tasks block on it. And check that all workers have started, i.e. that the full
+ // thread pool capacity is used.
+ comphelper::ThreadPool& rSharedPool = comphelper::ThreadPool::getSharedOptimalPool();
+ std::shared_ptr<comphelper::ThreadTaskTag> pTag = comphelper::ThreadPool::createThreadTaskTag();
+ UsageTask::mutex.lock();
+ for (int i = 0; i < rSharedPool.getWorkerCount(); ++i)
+ {
+ rSharedPool.pushTask(std::make_unique<UsageTask>(pTag));
+ osl::Thread::wait(std::chrono::milliseconds(10)); // give it a time to start
+ }
+ sal_uInt64 startTicks = tools::Time::GetSystemTicks();
+ while (UsageTask::count != rSharedPool.getWorkerCount())
+ {
+ // Wait at most 5 seconds, that should do even on slow systems.
+ CPPUNIT_ASSERT_MESSAGE("Thread pool does not use all worker threads.",
+ startTicks + 5000 > tools::Time::GetSystemTicks());
+ osl::Thread::wait(std::chrono::milliseconds(10));
+ }
+ UsageTask::mutex.unlock();
+ rSharedPool.waitUntilDone(pTag);
+}
+
+namespace
+{
+class CheckThreadTask : public comphelper::ThreadTask
+{
+ oslThreadIdentifier mThreadId;
+ bool mCheckEqual;
+
+public:
+ CheckThreadTask(oslThreadIdentifier threadId, bool checkEqual,
+ const std::shared_ptr<comphelper::ThreadTaskTag>& pTag)
+ : ThreadTask(pTag)
+ , mThreadId(threadId)
+ , mCheckEqual(checkEqual)
+ {
+ }
+ virtual void doWork()
+ {
+ CPPUNIT_ASSERT(mCheckEqual ? osl::Thread::getCurrentIdentifier() == mThreadId
+ : osl::Thread::getCurrentIdentifier() != mThreadId);
+ }
+};
+} // namespace
+
+void ThreadPoolTest::testTasksInThreads()
+{
+ // Check that all tasks are run in worker threads, not this thread.
+ comphelper::ThreadPool& pool = comphelper::ThreadPool::getSharedOptimalPool();
+ std::shared_ptr<comphelper::ThreadTaskTag> pTag = comphelper::ThreadPool::createThreadTaskTag();
+ for (int i = 0; i < 8; ++i)
+ pool.pushTask(
+ std::make_unique<CheckThreadTask>(osl::Thread::getCurrentIdentifier(), false, pTag));
+ pool.waitUntilDone(pTag);
+}
+
+void ThreadPoolTest::testNoThreads()
+{
+ // No worker threads, tasks will be run in this thread.
+ comphelper::ThreadPool pool(0);
+ std::shared_ptr<comphelper::ThreadTaskTag> pTag = comphelper::ThreadPool::createThreadTaskTag();
+ for (int i = 0; i < 8; ++i)
+ pool.pushTask(
+ std::make_unique<CheckThreadTask>(osl::Thread::getCurrentIdentifier(), true, pTag));
+ pool.waitUntilDone(pTag);
+}
+
+void ThreadPoolTest::testDedicatedPool()
+{
+ // Test that a separate thread pool works. The tasks themselves do not matter.
+ comphelper::ThreadPool pool(4);
+ std::shared_ptr<comphelper::ThreadTaskTag> pTag = comphelper::ThreadPool::createThreadTaskTag();
+ for (int i = 0; i < 8; ++i)
+ pool.pushTask(
+ std::make_unique<CheckThreadTask>(osl::Thread::getCurrentIdentifier(), false, pTag));
+ pool.waitUntilDone(pTag);
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(ThreadPoolTest);
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/qa/unit/types_test.cxx b/comphelper/qa/unit/types_test.cxx
new file mode 100644
index 0000000000..c69b071991
--- /dev/null
+++ b/comphelper/qa/unit/types_test.cxx
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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 <comphelper/types.hxx>
+#include <sal/types.h>
+
+#include <cppunit/TestAssert.h>
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+
+using namespace css;
+
+namespace
+{
+class TypesTest : public CppUnit::TestFixture
+{
+public:
+ void testGetINT64();
+ void testGetINT32();
+ void testGetINT16();
+ void testGetDouble();
+ void testGetFloat();
+ void testGetString();
+
+ CPPUNIT_TEST_SUITE(TypesTest);
+
+ CPPUNIT_TEST(testGetINT64);
+ CPPUNIT_TEST(testGetINT32);
+ CPPUNIT_TEST(testGetINT16);
+ CPPUNIT_TEST(testGetDouble);
+ CPPUNIT_TEST(testGetFloat);
+ CPPUNIT_TEST(testGetString);
+
+ CPPUNIT_TEST_SUITE_END();
+};
+
+void TypesTest::testGetINT64()
+{
+ CPPUNIT_ASSERT_EQUAL(sal_Int64(1337), ::comphelper::getINT64(uno::Any(sal_Int64(1337))));
+
+ uno::Any aValue;
+ CPPUNIT_ASSERT_EQUAL(sal_Int64(0), ::comphelper::getINT64(aValue));
+}
+
+void TypesTest::testGetINT32()
+{
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(1337), ::comphelper::getINT32(uno::Any(sal_Int32(1337))));
+
+ uno::Any aValue;
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(0), ::comphelper::getINT32(aValue));
+}
+
+void TypesTest::testGetINT16()
+{
+ CPPUNIT_ASSERT_EQUAL(sal_Int16(1337), ::comphelper::getINT16(uno::Any(sal_Int16(1337))));
+
+ uno::Any aValue;
+ CPPUNIT_ASSERT_EQUAL(sal_Int16(0), ::comphelper::getINT16(aValue));
+}
+
+void TypesTest::testGetDouble()
+{
+ CPPUNIT_ASSERT_EQUAL(1337.1337, ::comphelper::getDouble(uno::Any(1337.1337)));
+
+ uno::Any aValue;
+ CPPUNIT_ASSERT_EQUAL(0.0, ::comphelper::getDouble(aValue));
+}
+
+void TypesTest::testGetFloat()
+{
+ CPPUNIT_ASSERT_EQUAL(static_cast<float>(1337.0),
+ ::comphelper::getFloat(uno::Any(static_cast<float>(1337.0))));
+
+ uno::Any aValue;
+ CPPUNIT_ASSERT_EQUAL(static_cast<float>(0.0), ::comphelper::getFloat(aValue));
+}
+
+void TypesTest::testGetString()
+{
+ CPPUNIT_ASSERT_EQUAL(OUString("1337"), ::comphelper::getString(uno::Any(OUString("1337"))));
+
+ uno::Any aValue;
+ CPPUNIT_ASSERT_EQUAL(OUString(""), ::comphelper::getString(aValue));
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(TypesTest);
+
+} // namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/comphelper/qa/unit/variadictemplates.cxx b/comphelper/qa/unit/variadictemplates.cxx
new file mode 100644
index 0000000000..6b62204f48
--- /dev/null
+++ b/comphelper/qa/unit/variadictemplates.cxx
@@ -0,0 +1,179 @@
+/* -*- 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 <optional>
+#include <sal/types.h>
+#include <comphelper/unwrapargs.hxx>
+#include <cppunit/TestAssert.h>
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/plugin/TestPlugIn.h>
+
+#include <sstream>
+
+class VariadicTemplatesTest : public CppUnit::TestFixture
+{
+public:
+ void testUnwrapArgs();
+
+ CPPUNIT_TEST_SUITE(VariadicTemplatesTest);
+ CPPUNIT_TEST(testUnwrapArgs);
+ CPPUNIT_TEST_SUITE_END();
+};
+
+namespace {
+
+namespace detail {
+
+template <typename T>
+void extract(
+ ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Any> const& seq,
+ sal_Int32 nArg, T & v,
+ ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface>
+ const& xErrorContext )
+{
+ if (nArg >= seq.getLength()) {
+ throw ::com::sun::star::lang::IllegalArgumentException(
+ "No such argument available!",
+ xErrorContext, static_cast<sal_Int16>(nArg) );
+ }
+ if (! fromAny(seq[nArg], &v)) {
+ throw ::com::sun::star::lang::IllegalArgumentException(
+ "Cannot extract ANY { "
+ + seq[nArg].getValueType().getTypeName()
+ + " } to " + ::cppu::UnoType<T>::get().getTypeName(),
+ xErrorContext,
+ static_cast<sal_Int16>(nArg) );
+ }
+}
+
+template <typename T>
+void extract(
+ ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Any> const& seq,
+ sal_Int32 nArg, ::std::optional<T> & v,
+ ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface>
+ const& xErrorContext )
+{
+ if (nArg < seq.getLength()) {
+ T t;
+ extract( seq, nArg, t, xErrorContext );
+ v = t;
+ }
+}
+
+} // namespace detail
+
+template < typename T0, typename T1, typename T2, typename T3, typename T4 >
+void unwrapArgsBaseline(
+ ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Any > const& seq,
+ T0& v0, T1& v1, T2& v2, T3& v3, T4& v4,
+ ::com::sun::star::uno::Reference<
+ ::com::sun::star::uno::XInterface> const& xErrorContext =
+ ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface>() )
+{
+ ::detail::extract( seq, 0, v0, xErrorContext );
+ ::detail::extract( seq, 1, v1, xErrorContext );
+ ::detail::extract( seq, 2, v2, xErrorContext );
+ ::detail::extract( seq, 3, v3, xErrorContext );
+ ::detail::extract( seq, 4, v4, xErrorContext );
+}
+
+}
+
+void VariadicTemplatesTest::testUnwrapArgs() {
+ OUString tmp1 = "Test1";
+ sal_Int32 tmp2 = 42;
+ sal_uInt32 tmp3 = 42;
+ ::com::sun::star::uno::Any tmp6(
+ tmp1
+ );
+ ::com::sun::star::uno::Any tmp7(
+ tmp2
+ );
+ ::com::sun::star::uno::Any tmp8(
+ tmp3
+ );
+ ::com::sun::star::uno::Any tmp9(
+ OUString("Test2")
+ );
+ ::std::optional< ::com::sun::star::uno::Any > tmp10(
+ OUString("Test3")
+ );
+ ::std::optional< ::com::sun::star::uno::Any > tmp11(
+ tmp1
+ );
+
+ // test equality with the baseline and template specialization with
+ // std::optional< T >
+ try {
+ ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Any > seq1(
+ static_cast< sal_uInt32 >( 5 ) );
+ ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Any > seq2(
+ static_cast< sal_uInt32 >( 5 ) );
+
+ // tmp11 should be ignored as it is ::std::optional< T >
+ ::comphelper::unwrapArgs( seq1, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11 );
+ unwrapArgsBaseline( seq2, tmp6, tmp7, tmp8, tmp9, tmp10 );
+ ::com::sun::star::uno::Any* p1 = seq1.getArray();
+ ::com::sun::star::uno::Any* p2 = seq2.getArray();
+
+ for( sal_Int32 i = 0; i < seq1.getLength() && i < seq2.getLength(); ++i ) {
+ CPPUNIT_ASSERT_EQUAL_MESSAGE( "seq1 and seq2 are equal",
+ p1[i], p2[i] );
+ }
+ CPPUNIT_ASSERT_MESSAGE( "seq1 and seq2 are equal",
+ bool(seq1 == seq2) );
+ }
+ catch( ::com::sun::star::lang::IllegalArgumentException& err ) {
+ std::stringstream ss;
+ ss << "IllegalArgumentException when unwrapping arguments at: " <<
+ err.ArgumentPosition;
+ CPPUNIT_FAIL( ss.str() );
+ }
+
+ // test argument counting
+ try {
+ ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Any > seq(
+ static_cast< sal_uInt32 >( 4 ) );
+ ::comphelper::unwrapArgs( seq, tmp6, tmp7, tmp10, tmp11, tmp10, tmp6 );
+ }
+ catch( ::com::sun::star::lang::IllegalArgumentException& err ) {
+ CPPUNIT_ASSERT_EQUAL( static_cast< short >( 5 ), err.ArgumentPosition );
+ }
+
+ OUString test1( "Test2" );
+ OUString test2( "Test2" );
+ OUString test3( "Test3" );
+ OUString test4( "Test4" );
+ OUString test5( "Test5" );
+
+ try {
+ ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Any > seq(
+ static_cast< sal_uInt32 >( 4 ) );
+ ::comphelper::unwrapArgs( seq, test1, test2, test3, test4, test5 );
+ }
+ catch( ::com::sun::star::lang::IllegalArgumentException& err1 ) {
+ try {
+ ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Any > seq(
+ static_cast< sal_uInt32 >( 4 ) );
+ unwrapArgsBaseline( seq, test1, test2, test3, test4, test5 );
+ CPPUNIT_FAIL( "unwrapArgs failed while the baseline did not throw" );
+ }
+ catch( ::com::sun::star::lang::IllegalArgumentException& err2 ) {
+ CPPUNIT_ASSERT_EQUAL_MESSAGE( "err1.ArgumentPosition == err2.ArgumentPosition",
+ err1.ArgumentPosition, err2.ArgumentPosition );
+ }
+ }
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(VariadicTemplatesTest);
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/qa/weakbag/makefile.mk b/comphelper/qa/weakbag/makefile.mk
new file mode 100644
index 0000000000..495c68f506
--- /dev/null
+++ b/comphelper/qa/weakbag/makefile.mk
@@ -0,0 +1,44 @@
+#
+# 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 .
+#
+
+PRJ := ..$/..
+PRJNAME := comphelper
+TARGET := qa_weakbag
+
+ENABLE_EXCEPTIONS := TRUE
+
+.INCLUDE: settings.mk
+.INCLUDE : $(PRJ)$/version.mk
+
+CFLAGSCXX += $(CPPUNIT_CFLAGS)
+
+DLLPRE = # no leading "lib" on .so files
+
+INCPRE += $(MISC)$/$(TARGET)$/inc
+
+SHL1TARGET = $(TARGET)_weakbag
+SHL1OBJS = $(SLO)$/test_weakbag.obj $(SLO)$/test_weakbag_noadditional.obj
+SHL1STDLIBS = $(CPPUHELPERLIB) $(CPPULIB) $(CPPUNITLIB) $(SALLIB) $(COMPHELPERLIB)
+SHL1VERSIONMAP = ..$/version.map
+SHL1IMPLIB = i$(SHL1TARGET)
+DEF1NAME = $(SHL1TARGET)
+
+SLOFILES = $(SHL1OBJS)
+
+.INCLUDE: target.mk
+.INCLUDE: _cppunit.mk
diff --git a/comphelper/qa/weakbag/test_weakbag.cxx b/comphelper/qa/weakbag/test_weakbag.cxx
new file mode 100644
index 0000000000..b646ca7aef
--- /dev/null
+++ b/comphelper/qa/weakbag/test_weakbag.cxx
@@ -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 .
+ */
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/XInterface.hpp>
+#include <comphelper/weakbag.hxx>
+#include <cppuhelper/weak.hxx>
+
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/plugin/TestPlugIn.h>
+
+namespace
+{
+class Test : public CppUnit::TestFixture
+{
+public:
+ void test()
+ {
+ css::uno::Reference<css::uno::XInterface> ref1(new cppu::OWeakObject);
+ css::uno::Reference<css::uno::XInterface> ref2(new cppu::OWeakObject);
+ css::uno::Reference<css::uno::XInterface> ref3(new cppu::OWeakObject);
+ comphelper::WeakBag<css::uno::XInterface> bag;
+ bag.add(ref1);
+ bag.add(ref1);
+ bag.add(ref2);
+ bag.add(ref2);
+ ref1.clear();
+ bag.add(ref3);
+ ref3.clear();
+ CPPUNIT_ASSERT_MESSAGE("remove first ref2", bag.remove() == ref2);
+ CPPUNIT_ASSERT_MESSAGE("remove second ref2", bag.remove() == ref2);
+ CPPUNIT_ASSERT_MESSAGE("remove first null", !bag.remove().is());
+ CPPUNIT_ASSERT_MESSAGE("remove second null", !bag.remove().is());
+ }
+
+ CPPUNIT_TEST_SUITE(Test);
+ CPPUNIT_TEST(test);
+ CPPUNIT_TEST_SUITE_END();
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(Test);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/qa/weakbag/test_weakbag_noadditional.cxx b/comphelper/qa/weakbag/test_weakbag_noadditional.cxx
new file mode 100644
index 0000000000..d2d66a61ea
--- /dev/null
+++ b/comphelper/qa/weakbag/test_weakbag_noadditional.cxx
@@ -0,0 +1,25 @@
+/* -*- 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/types.h>
+#include <cppunit/plugin/TestPlugIn.h>
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/compare/AnyCompareFactory.cxx b/comphelper/source/compare/AnyCompareFactory.cxx
new file mode 100644
index 0000000000..40e5f0806a
--- /dev/null
+++ b/comphelper/source/compare/AnyCompareFactory.cxx
@@ -0,0 +1,136 @@
+/* -*- 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 <com/sun/star/ucb/XAnyCompareFactory.hpp>
+#include <com/sun/star/i18n/Collator.hpp>
+#include <com/sun/star/lang/Locale.hpp>
+#include <com/sun/star/uno/Sequence.h>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::ucb;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::i18n;
+
+namespace {
+
+class AnyCompare : public ::cppu::WeakImplHelper< XAnyCompare >
+{
+ Reference< XCollator > m_xCollator;
+
+public:
+ AnyCompare( Reference< XComponentContext > const & xContext, const Locale& rLocale )
+ : m_xCollator(Collator::create( xContext ))
+ {
+ m_xCollator->loadDefaultCollator( rLocale,
+ 0 ); //???
+ }
+
+ virtual sal_Int16 SAL_CALL compare( const Any& any1, const Any& any2 ) override;
+};
+
+class AnyCompareFactory : public cppu::WeakImplHelper< XAnyCompareFactory, XInitialization, XServiceInfo >
+{
+ Reference< XAnyCompare > m_xAnyCompare;
+ Reference< XComponentContext > m_xContext;
+ Locale m_Locale;
+
+public:
+ explicit AnyCompareFactory( Reference< XComponentContext > const & xContext ) : m_xContext( xContext )
+ {}
+
+ // XAnyCompareFactory
+ virtual Reference< XAnyCompare > SAL_CALL createAnyCompareByName ( const OUString& aPropertyName ) override;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const Sequence< Any >& aArguments ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+};
+
+}
+
+sal_Int16 SAL_CALL AnyCompare::compare( const Any& any1, const Any& any2 )
+{
+ sal_Int16 aResult = 0;
+
+ OUString aStr1;
+ OUString aStr2;
+
+ any1 >>= aStr1;
+ any2 >>= aStr2;
+
+ aResult = static_cast<sal_Int16>(m_xCollator->compareString(aStr1, aStr2));
+
+ return aResult;
+}
+
+Reference< XAnyCompare > SAL_CALL AnyCompareFactory::createAnyCompareByName( const OUString& aPropertyName )
+{
+ // for now only OUString properties compare is implemented
+ // so no check for the property name is done
+
+ if( aPropertyName == "Title" )
+ return m_xAnyCompare;
+
+ return Reference< XAnyCompare >();
+}
+
+void SAL_CALL AnyCompareFactory::initialize( const Sequence< Any >& aArguments )
+{
+ if( aArguments.hasElements() )
+ {
+ if( aArguments[0] >>= m_Locale )
+ {
+ m_xAnyCompare = new AnyCompare( m_xContext, m_Locale );
+ return;
+ }
+ }
+}
+
+OUString SAL_CALL AnyCompareFactory::getImplementationName( )
+{
+ return "AnyCompareFactory";
+}
+
+sal_Bool SAL_CALL AnyCompareFactory::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+Sequence< OUString > SAL_CALL AnyCompareFactory::getSupportedServiceNames( )
+{
+ return { "com.sun.star.ucb.AnyCompareFactory" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+AnyCompareFactory_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new AnyCompareFactory(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/container/IndexedPropertyValuesContainer.cxx b/comphelper/source/container/IndexedPropertyValuesContainer.cxx
new file mode 100644
index 0000000000..f5b7358d64
--- /dev/null
+++ b/comphelper/source/container/IndexedPropertyValuesContainer.cxx
@@ -0,0 +1,127 @@
+/* -*- 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 <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <comphelper/indexedpropertyvalues.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <o3tl/safeint.hxx>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+using namespace com::sun::star;
+
+
+namespace comphelper {
+
+
+IndexedPropertyValuesContainer::IndexedPropertyValuesContainer() noexcept
+{
+}
+
+// XIndexContainer
+void SAL_CALL IndexedPropertyValuesContainer::insertByIndex( sal_Int32 nIndex, const css::uno::Any& aElement )
+{
+ sal_Int32 nSize(maProperties.size());
+ if ((nSize < nIndex) || (nIndex < 0))
+ throw lang::IndexOutOfBoundsException();
+
+ uno::Sequence<beans::PropertyValue> aProps;
+ if (!(aElement >>= aProps))
+ throw lang::IllegalArgumentException("element is not beans::PropertyValue", static_cast<cppu::OWeakObject*>(this), 2);
+ if (nSize == nIndex)
+ maProperties.push_back(aProps);
+ else
+ maProperties.insert(maProperties.begin() + nIndex, aProps);
+}
+
+void SAL_CALL IndexedPropertyValuesContainer::removeByIndex( sal_Int32 nIndex )
+{
+ if ((nIndex < 0) || (o3tl::make_unsigned(nIndex) >= maProperties.size()))
+ throw lang::IndexOutOfBoundsException();
+
+ maProperties.erase(maProperties.begin() + nIndex);
+}
+
+// XIndexReplace
+void SAL_CALL IndexedPropertyValuesContainer::replaceByIndex( sal_Int32 nIndex, const css::uno::Any& aElement )
+{
+ sal_Int32 nSize(maProperties.size());
+ if ((nIndex >= nSize) || (nIndex < 0))
+ throw lang::IndexOutOfBoundsException();
+
+ uno::Sequence<beans::PropertyValue> aProps;
+ if (!(aElement >>= aProps))
+ throw lang::IllegalArgumentException("element is not beans::PropertyValue", static_cast<cppu::OWeakObject*>(this), 2);
+ maProperties[nIndex] = aProps;
+}
+
+// XIndexAccess
+sal_Int32 SAL_CALL IndexedPropertyValuesContainer::getCount( )
+{
+ return maProperties.size();
+}
+
+css::uno::Any SAL_CALL IndexedPropertyValuesContainer::getByIndex( sal_Int32 nIndex )
+{
+ sal_Int32 nSize(maProperties.size());
+ if ((nIndex >= nSize) || (nIndex < 0))
+ throw lang::IndexOutOfBoundsException();
+
+ return uno::Any( maProperties[nIndex] );
+}
+
+// XElementAccess
+css::uno::Type SAL_CALL IndexedPropertyValuesContainer::getElementType( )
+{
+ return cppu::UnoType<uno::Sequence<beans::PropertyValue>>::get();
+}
+
+sal_Bool SAL_CALL IndexedPropertyValuesContainer::hasElements( )
+{
+ return !maProperties.empty();
+}
+
+//XServiceInfo
+OUString SAL_CALL IndexedPropertyValuesContainer::getImplementationName( )
+{
+ return "IndexedPropertyValuesContainer";
+}
+
+sal_Bool SAL_CALL IndexedPropertyValuesContainer::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL IndexedPropertyValuesContainer::getSupportedServiceNames( )
+{
+ return { "com.sun.star.document.IndexedPropertyValues" };
+}
+
+} // namespace comphelper
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+IndexedPropertyValuesContainer_get_implementation(
+ css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new comphelper::IndexedPropertyValuesContainer());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/container/NamedPropertyValuesContainer.cxx b/comphelper/source/container/NamedPropertyValuesContainer.cxx
new file mode 100644
index 0000000000..a44837f117
--- /dev/null
+++ b/comphelper/source/container/NamedPropertyValuesContainer.cxx
@@ -0,0 +1,169 @@
+/* -*- 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 <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/uno/Sequence.h>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <map>
+
+
+namespace com::sun::star::uno { class XComponentContext; }
+using namespace com::sun::star;
+
+typedef std::map< OUString, uno::Sequence<beans::PropertyValue> > NamedPropertyValues;
+
+namespace {
+
+class NamedPropertyValuesContainer : public cppu::WeakImplHelper< container::XNameContainer, lang::XServiceInfo >
+{
+public:
+ NamedPropertyValuesContainer() noexcept;
+
+ // XNameContainer
+ virtual void SAL_CALL insertByName( const OUString& aName, const css::uno::Any& aElement ) override;
+ virtual void SAL_CALL removeByName( const OUString& Name ) override;
+
+ // XNameReplace
+ virtual void SAL_CALL replaceByName( const OUString& aName, const css::uno::Any& aElement ) override;
+
+ // XNameAccess
+ virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getElementNames( ) override;
+ virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override;
+
+ // XElementAccess
+ virtual css::uno::Type SAL_CALL getElementType( ) override;
+ virtual sal_Bool SAL_CALL hasElements( ) override;
+
+ //XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+private:
+ NamedPropertyValues maProperties;
+};
+
+}
+
+NamedPropertyValuesContainer::NamedPropertyValuesContainer() noexcept
+{
+}
+
+// XNameContainer
+void SAL_CALL NamedPropertyValuesContainer::insertByName( const OUString& aName, const uno::Any& aElement )
+{
+ if( maProperties.find( aName ) != maProperties.end() )
+ throw container::ElementExistException();
+
+ uno::Sequence<beans::PropertyValue> aProps;
+ if( !(aElement >>= aProps ) )
+ throw lang::IllegalArgumentException("element is not beans::PropertyValue", static_cast<cppu::OWeakObject*>(this), 2);
+
+ maProperties.emplace( aName, aProps );
+}
+
+void SAL_CALL NamedPropertyValuesContainer::removeByName( const OUString& Name )
+{
+ NamedPropertyValues::iterator aIter = maProperties.find( Name );
+ if( aIter == maProperties.end() )
+ throw container::NoSuchElementException();
+
+ maProperties.erase( aIter );
+}
+
+// XNameReplace
+void SAL_CALL NamedPropertyValuesContainer::replaceByName( const OUString& aName, const css::uno::Any& aElement )
+{
+ NamedPropertyValues::iterator aIter = maProperties.find( aName );
+ if( aIter == maProperties.end() )
+ throw container::NoSuchElementException();
+
+ uno::Sequence<beans::PropertyValue> aProps;
+ if( !(aElement >>= aProps) )
+ throw lang::IllegalArgumentException("element is not beans::PropertyValue", static_cast<cppu::OWeakObject*>(this), 2);
+
+ (*aIter).second = aProps;
+}
+
+// XNameAccess
+css::uno::Any SAL_CALL NamedPropertyValuesContainer::getByName( const OUString& aName )
+{
+ NamedPropertyValues::iterator aIter = maProperties.find( aName );
+ if( aIter == maProperties.end() )
+ throw container::NoSuchElementException();
+
+ uno::Any aElement;
+
+ aElement <<= (*aIter).second;
+
+ return aElement;
+}
+
+css::uno::Sequence< OUString > SAL_CALL NamedPropertyValuesContainer::getElementNames( )
+{
+ return comphelper::mapKeysToSequence(maProperties);
+}
+
+sal_Bool SAL_CALL NamedPropertyValuesContainer::hasByName( const OUString& aName )
+{
+ NamedPropertyValues::iterator aIter = maProperties.find( aName );
+ return aIter != maProperties.end();
+}
+
+// XElementAccess
+css::uno::Type SAL_CALL NamedPropertyValuesContainer::getElementType( )
+{
+ return cppu::UnoType<uno::Sequence<beans::PropertyValue>>::get();
+}
+
+sal_Bool SAL_CALL NamedPropertyValuesContainer::hasElements( )
+{
+ return !maProperties.empty();
+}
+
+//XServiceInfo
+OUString SAL_CALL NamedPropertyValuesContainer::getImplementationName( )
+{
+ return "NamedPropertyValuesContainer";
+}
+
+sal_Bool SAL_CALL NamedPropertyValuesContainer::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL NamedPropertyValuesContainer::getSupportedServiceNames( )
+{
+ return { "com.sun.star.document.NamedPropertyValues" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+NamedPropertyValuesContainer_get_implementation(
+ css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new NamedPropertyValuesContainer());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/container/container.cxx b/comphelper/source/container/container.cxx
new file mode 100644
index 0000000000..7b24327233
--- /dev/null
+++ b/comphelper/source/container/container.cxx
@@ -0,0 +1,140 @@
+/* -*- 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 <com/sun/star/uno/XInterface.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/container/XChild.hpp>
+#include <comphelper/container.hxx>
+#include <o3tl/any.hxx>
+#include <utility>
+#include <osl/diagnose.h>
+
+
+namespace comphelper
+{
+
+
+IndexAccessIterator::IndexAccessIterator(css::uno::Reference< css::uno::XInterface> xStartingPoint)
+ :m_xStartingPoint(std::move(xStartingPoint))
+{
+ OSL_ENSURE(m_xStartingPoint.is(), "IndexAccessIterator::IndexAccessIterator : no starting point !");
+}
+
+IndexAccessIterator::~IndexAccessIterator() {}
+
+
+css::uno::Reference< css::uno::XInterface> const & IndexAccessIterator::Next()
+{
+ bool bCheckingStartingPoint = !m_xCurrentObject.is();
+ // Is the current node the starting point?
+ bool bAlreadyCheckedCurrent = m_xCurrentObject.is();
+ // Have I already tested the current node through ShouldHandleElement?
+ if (!m_xCurrentObject.is())
+ m_xCurrentObject = m_xStartingPoint;
+
+ css::uno::Reference< css::uno::XInterface> xSearchLoop( m_xCurrentObject);
+ bool bHasMoreToSearch = true;
+ bool bFoundSomething = false;
+ while (!bFoundSomething && bHasMoreToSearch)
+ {
+ // Priming loop
+ if (!bAlreadyCheckedCurrent && ShouldHandleElement(xSearchLoop))
+ {
+ m_xCurrentObject = xSearchLoop;
+ bFoundSomething = true;
+ }
+ else
+ {
+ // First, check to see if there's a match below
+ css::uno::Reference< css::container::XIndexAccess> xContainerAccess(xSearchLoop, css::uno::UNO_QUERY);
+ if (xContainerAccess.is() && xContainerAccess->getCount() && ShouldStepInto(xContainerAccess))
+ {
+ css::uno::Any aElement(xContainerAccess->getByIndex(0));
+ xSearchLoop = *o3tl::doAccess<css::uno::Reference<css::uno::XInterface>>(aElement);
+ bCheckingStartingPoint = false;
+
+ m_arrChildIndizies.push_back(sal_Int32(0));
+ }
+ else
+ { // otherwise, look above and to the right, if possible
+ while (!m_arrChildIndizies.empty())
+ { // If the list isn't empty and there's nothing above
+ css::uno::Reference< css::container::XChild> xChild(xSearchLoop, css::uno::UNO_QUERY);
+ OSL_ENSURE(xChild.is(), "IndexAccessIterator::Next : a content has no appropriate interface !");
+
+ css::uno::Reference< css::uno::XInterface> xParent( xChild->getParent());
+ xContainerAccess.set(xParent, css::uno::UNO_QUERY);
+ OSL_ENSURE(xContainerAccess.is(), "IndexAccessIterator::Next : a content has an invalid parent !");
+
+ // Remove the index that SearchLoop had within this parent from my stack
+ sal_Int32 nOldSearchChildIndex = m_arrChildIndizies[m_arrChildIndizies.size() - 1];
+ m_arrChildIndizies.pop_back();
+
+ if (nOldSearchChildIndex < xContainerAccess->getCount() - 1)
+ { // Move to the right in this row
+ ++nOldSearchChildIndex;
+ // and check the next child
+ css::uno::Any aElement(xContainerAccess->getByIndex(nOldSearchChildIndex));
+ xSearchLoop = *o3tl::doAccess<css::uno::Reference<css::uno::XInterface>>(aElement);
+ bCheckingStartingPoint = false;
+ // and update its position in the list.
+ m_arrChildIndizies.push_back(nOldSearchChildIndex);
+
+ break;
+ }
+ // Finally, if there's nothing more to do in this row (to the right), we'll move on to the next row.
+ xSearchLoop = xParent;
+ bCheckingStartingPoint = false;
+ }
+
+ if (m_arrChildIndizies.empty() && !bCheckingStartingPoint)
+ { //This is the case if there is nothing to the right in the original search loop
+ bHasMoreToSearch = false;
+ }
+ }
+
+ if (bHasMoreToSearch)
+ { // If there is still a node in the tree which can be tested
+ if (ShouldHandleElement(xSearchLoop))
+ {
+ m_xCurrentObject = xSearchLoop;
+ bFoundSomething = true;
+ }
+ else
+ if (bCheckingStartingPoint)
+ bHasMoreToSearch = false;
+ bAlreadyCheckedCurrent = true;
+ }
+ }
+ }
+
+ if (!bFoundSomething)
+ {
+ OSL_ENSURE(m_arrChildIndizies.empty(), "IndexAccessIterator::Next : items left on stack ! how this ?");
+ Invalidate();
+ }
+
+ return m_xCurrentObject;
+}
+
+
+} // namespace comphelper
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/container/containermultiplexer.cxx b/comphelper/source/container/containermultiplexer.cxx
new file mode 100644
index 0000000000..c687e72793
--- /dev/null
+++ b/comphelper/source/container/containermultiplexer.cxx
@@ -0,0 +1,159 @@
+/* -*- 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 <comphelper/containermultiplexer.hxx>
+#include <com/sun/star/container/XContainer.hpp>
+#include <osl/diagnose.h>
+
+namespace comphelper
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::container;
+
+ OContainerListener::OContainerListener(::osl::Mutex& _rMutex)
+ :m_rMutex(_rMutex)
+ {
+ }
+
+
+ OContainerListener::~OContainerListener()
+ {
+ if (m_xAdapter.is())
+ {
+ m_xAdapter->dispose();
+ }
+ }
+
+
+ void OContainerListener::_elementInserted( const ContainerEvent& /*_rEvent*/ )
+ {
+ }
+
+
+ void OContainerListener::_elementRemoved( const ContainerEvent& )
+ {
+ }
+
+
+ void OContainerListener::_elementReplaced( const ContainerEvent& /*_rEvent*/ )
+ {
+ }
+
+
+ void OContainerListener::_disposing(const EventObject& )
+ {
+ }
+
+
+ void OContainerListener::setAdapter(OContainerListenerAdapter* pAdapter)
+ {
+ ::osl::MutexGuard aGuard(m_rMutex);
+ m_xAdapter = pAdapter;
+ }
+
+ OContainerListenerAdapter::OContainerListenerAdapter(OContainerListener* _pListener,
+ const Reference< XContainer >& _rxContainer)
+ :m_xContainer(_rxContainer)
+ ,m_pListener(_pListener)
+ {
+ if (m_pListener)
+ m_pListener->setAdapter(this);
+
+ osl_atomic_increment(&m_refCount);
+ try
+ {
+ m_xContainer->addContainerListener(this);
+ }
+ catch(const Exception&)
+ {
+ OSL_FAIL("Exception caught!");
+ }
+ osl_atomic_decrement(&m_refCount);
+ }
+
+
+ OContainerListenerAdapter::~OContainerListenerAdapter()
+ {
+ }
+
+
+ void OContainerListenerAdapter::dispose()
+ {
+ if (!m_xContainer.is())
+ return;
+
+ try
+ {
+ Reference< XContainerListener > xPreventDelete(this);
+ m_xContainer->removeContainerListener(xPreventDelete);
+ m_pListener->setAdapter(nullptr);
+ }
+ catch(const Exception&)
+ {
+ OSL_FAIL("Exception caught!");
+ }
+ m_xContainer = nullptr;
+ m_pListener = nullptr;
+ }
+
+
+ void SAL_CALL OContainerListenerAdapter::disposing( const EventObject& _rSource)
+ {
+ if (m_pListener)
+ {
+ // tell the listener
+ m_pListener->_disposing(_rSource);
+ // disconnect the listener
+ if ( m_pListener )
+ m_pListener->setAdapter(nullptr);
+ }
+
+ m_xContainer = nullptr;
+ m_pListener = nullptr;
+ }
+
+
+ void SAL_CALL OContainerListenerAdapter::elementInserted( const ContainerEvent& _rEvent )
+ {
+ if (m_pListener)
+ m_pListener->_elementInserted(_rEvent);
+ }
+
+
+ void SAL_CALL OContainerListenerAdapter::elementRemoved( const ContainerEvent& _rEvent )
+ {
+ if (m_pListener)
+ m_pListener->_elementRemoved(_rEvent);
+ }
+
+
+ void SAL_CALL OContainerListenerAdapter::elementReplaced( const ContainerEvent& _rEvent )
+ {
+ if (m_pListener)
+ m_pListener->_elementReplaced(_rEvent);
+ }
+
+
+} // namespace comphelper
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/container/embeddedobjectcontainer.cxx b/comphelper/source/container/embeddedobjectcontainer.cxx
new file mode 100644
index 0000000000..23915d3e13
--- /dev/null
+++ b/comphelper/source/container/embeddedobjectcontainer.cxx
@@ -0,0 +1,1512 @@
+/* -*- 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 <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/embed/EmbeddedObjectCreator.hpp>
+#include <com/sun/star/embed/WrongStateException.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <com/sun/star/embed/XEmbedPersist.hpp>
+#include <com/sun/star/embed/XLinkageSupport.hpp>
+#include <com/sun/star/embed/XTransactedObject.hpp>
+#include <com/sun/star/embed/XOptimizedStorage.hpp>
+#include <com/sun/star/embed/EntryInitModes.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/util/XCloseable.hpp>
+#include <com/sun/star/util/XModifiable.hpp>
+#include <com/sun/star/embed/EmbedStates.hpp>
+#include <com/sun/star/beans/XPropertySetInfo.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/embed/Aspects.hpp>
+#include <com/sun/star/embed/EmbedMisc.hpp>
+
+#include <comphelper/seqstream.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <comphelper/embeddedobjectcontainer.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <cppuhelper/weakref.hxx>
+#include <sal/log.hxx>
+
+#include <officecfg/Office/Common.hxx>
+
+#include <algorithm>
+#include <unordered_map>
+
+
+using namespace ::com::sun::star;
+
+namespace comphelper {
+
+typedef std::unordered_map<OUString, uno::Reference<embed::XEmbeddedObject>> EmbeddedObjectContainerNameMap;
+struct EmbedImpl
+{
+ // TODO/LATER: remove objects from temp. Container storage when object is disposed
+ EmbeddedObjectContainerNameMap maNameToObjectMap;
+ // to speed up lookup by Reference
+ std::unordered_map<uno::Reference<embed::XEmbeddedObject>, OUString> maObjectToNameMap;
+ uno::Reference < embed::XStorage > mxStorage;
+ EmbeddedObjectContainer* mpTempObjectContainer;
+ uno::Reference < embed::XStorage > mxImageStorage;
+ uno::WeakReference < uno::XInterface > m_xModel;
+
+ bool mbOwnsStorage : 1;
+ bool mbUserAllowsLinkUpdate : 1;
+
+ const uno::Reference < embed::XStorage >& GetReplacements();
+};
+
+const uno::Reference < embed::XStorage >& EmbedImpl::GetReplacements()
+{
+ if ( !mxImageStorage.is() )
+ {
+ try
+ {
+ mxImageStorage = mxStorage->openStorageElement(
+ "ObjectReplacements", embed::ElementModes::READWRITE );
+ }
+ catch (const uno::Exception&)
+ {
+ mxImageStorage = mxStorage->openStorageElement(
+ "ObjectReplacements", embed::ElementModes::READ );
+ }
+ }
+
+ if ( !mxImageStorage.is() )
+ throw io::IOException("No ObjectReplacements");
+
+ return mxImageStorage;
+}
+
+EmbeddedObjectContainer::EmbeddedObjectContainer()
+ : pImpl(new EmbedImpl)
+{
+ pImpl->mxStorage = ::comphelper::OStorageHelper::GetTemporaryStorage();
+ pImpl->mbOwnsStorage = true;
+ pImpl->mbUserAllowsLinkUpdate = true;
+ pImpl->mpTempObjectContainer = nullptr;
+}
+
+EmbeddedObjectContainer::EmbeddedObjectContainer( const uno::Reference < embed::XStorage >& rStor )
+ : pImpl(new EmbedImpl)
+{
+ pImpl->mxStorage = rStor;
+ pImpl->mbOwnsStorage = false;
+ pImpl->mbUserAllowsLinkUpdate = true;
+ pImpl->mpTempObjectContainer = nullptr;
+}
+
+EmbeddedObjectContainer::EmbeddedObjectContainer( const uno::Reference < embed::XStorage >& rStor, const uno::Reference < uno::XInterface >& xModel )
+ : pImpl(new EmbedImpl)
+{
+ pImpl->mxStorage = rStor;
+ pImpl->mbOwnsStorage = false;
+ pImpl->mbUserAllowsLinkUpdate = true;
+ pImpl->mpTempObjectContainer = nullptr;
+ pImpl->m_xModel = xModel;
+}
+
+void EmbeddedObjectContainer::SwitchPersistence( const uno::Reference < embed::XStorage >& rStor )
+{
+ ReleaseImageSubStorage();
+
+ if ( pImpl->mbOwnsStorage )
+ pImpl->mxStorage->dispose();
+
+ pImpl->mxStorage = rStor;
+ pImpl->mbOwnsStorage = false;
+}
+
+bool EmbeddedObjectContainer::CommitImageSubStorage()
+{
+ if ( !pImpl->mxImageStorage )
+ return true;
+
+ try
+ {
+ bool bReadOnlyMode = true;
+ uno::Reference < beans::XPropertySet > xSet(pImpl->mxImageStorage,uno::UNO_QUERY);
+ if ( xSet.is() )
+ {
+ // get the open mode from the parent storage
+ sal_Int32 nMode = 0;
+ uno::Any aAny = xSet->getPropertyValue("OpenMode");
+ if ( aAny >>= nMode )
+ bReadOnlyMode = !(nMode & embed::ElementModes::WRITE );
+ } // if ( xSet.is() )
+ if ( !bReadOnlyMode )
+ {
+ uno::Reference< embed::XTransactedObject > xTransact( pImpl->mxImageStorage, uno::UNO_QUERY_THROW );
+ xTransact->commit();
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+void EmbeddedObjectContainer::ReleaseImageSubStorage()
+{
+ CommitImageSubStorage();
+
+ if ( pImpl->mxImageStorage.is() )
+ {
+ try
+ {
+ pImpl->mxImageStorage->dispose();
+ pImpl->mxImageStorage.clear();
+ }
+ catch (const uno::Exception&)
+ {
+ SAL_WARN( "comphelper.container", "Problems releasing image substorage!" );
+ }
+ }
+}
+
+EmbeddedObjectContainer::~EmbeddedObjectContainer()
+{
+ ReleaseImageSubStorage();
+
+ if ( pImpl->mbOwnsStorage )
+ pImpl->mxStorage->dispose();
+
+ delete pImpl->mpTempObjectContainer;
+}
+
+void EmbeddedObjectContainer::CloseEmbeddedObjects()
+{
+ for( const auto& rObj : pImpl->maNameToObjectMap )
+ {
+ uno::Reference < util::XCloseable > const & xClose = rObj.second;
+ if( xClose.is() )
+ {
+ try
+ {
+ xClose->close( true );
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ }
+ }
+}
+
+OUString EmbeddedObjectContainer::CreateUniqueObjectName()
+{
+ OUString aStr;
+ sal_Int32 i=1;
+ do
+ {
+ aStr = "Object " + OUString::number( i++ );
+ }
+ while( HasEmbeddedObject( aStr ) );
+ // TODO/LATER: should we consider deleted objects?
+
+ return aStr;
+}
+
+uno::Sequence < OUString > EmbeddedObjectContainer::GetObjectNames() const
+{
+ return comphelper::mapKeysToSequence(pImpl->maNameToObjectMap);
+}
+
+bool EmbeddedObjectContainer::HasEmbeddedObjects() const
+{
+ return !pImpl->maNameToObjectMap.empty();
+}
+
+bool EmbeddedObjectContainer::HasEmbeddedObject( const OUString& rName )
+{
+ auto aIt = pImpl->maNameToObjectMap.find( rName );
+ if (aIt != pImpl->maNameToObjectMap.end())
+ return true;
+ if (!pImpl->mxStorage.is())
+ return false;
+ return pImpl->mxStorage->hasByName(rName);
+}
+
+bool EmbeddedObjectContainer::HasEmbeddedObject( const uno::Reference < embed::XEmbeddedObject >& xObj ) const
+{
+ return pImpl->maObjectToNameMap.find(xObj) != pImpl->maObjectToNameMap.end();
+}
+
+bool EmbeddedObjectContainer::HasInstantiatedEmbeddedObject( const OUString& rName )
+{
+ // allows to detect whether the object was already instantiated
+ // currently the filter instantiate it on loading, so this method allows
+ // to avoid objects pointing to the same persistence
+ auto aIt = pImpl->maNameToObjectMap.find( rName );
+ return ( aIt != pImpl->maNameToObjectMap.end() );
+}
+
+OUString EmbeddedObjectContainer::GetEmbeddedObjectName( const css::uno::Reference < css::embed::XEmbeddedObject >& xObj ) const
+{
+ auto it = pImpl->maObjectToNameMap.find(xObj);
+ if (it == pImpl->maObjectToNameMap.end())
+ {
+ SAL_WARN( "comphelper.container", "Unknown object!" );
+ return OUString();
+ }
+ return it->second;
+}
+
+uno::Reference< embed::XEmbeddedObject>
+EmbeddedObjectContainer::GetEmbeddedObject(
+ const OUString& rName, OUString const*const pBaseURL)
+{
+ SAL_WARN_IF( rName.isEmpty(), "comphelper.container", "Empty object name!");
+
+ uno::Reference < embed::XEmbeddedObject > xObj;
+ auto aIt = pImpl->maNameToObjectMap.find( rName );
+
+#if OSL_DEBUG_LEVEL > 1
+ uno::Reference < container::XNameAccess > xAccess( pImpl->mxStorage, uno::UNO_QUERY );
+ uno::Sequence< OUString> aSeq = xAccess->getElementNames();
+ const OUString* pIter = aSeq.getConstArray();
+ const OUString* pEnd = pIter + aSeq.getLength();
+ for(;pIter != pEnd;++pIter)
+ {
+ (void)*pIter;
+ }
+ OSL_ENSURE( aIt != pImpl->maNameToObjectMap.end() || xAccess->hasByName(rName), "Could not return object!" );
+#endif
+
+ // check if object was already created
+ if ( aIt != pImpl->maNameToObjectMap.end() )
+ xObj = (*aIt).second;
+ else
+ xObj = Get_Impl(rName, uno::Reference<embed::XEmbeddedObject>(), pBaseURL);
+
+ return xObj;
+}
+
+uno::Reference<embed::XEmbeddedObject> EmbeddedObjectContainer::Get_Impl(
+ const OUString& rName,
+ const uno::Reference<embed::XEmbeddedObject>& xCopy,
+ OUString const*const pBaseURL)
+{
+ uno::Reference < embed::XEmbeddedObject > xObj;
+ try
+ {
+ // create the object from the storage
+ uno::Reference < beans::XPropertySet > xSet( pImpl->mxStorage, uno::UNO_QUERY );
+ bool bReadOnlyMode = true;
+ if ( xSet.is() )
+ {
+ // get the open mode from the parent storage
+ sal_Int32 nMode = 0;
+ uno::Any aAny = xSet->getPropertyValue("OpenMode");
+ if ( aAny >>= nMode )
+ bReadOnlyMode = !(nMode & embed::ElementModes::WRITE );
+ }
+
+ // object was not added until now - should happen only by calling this method from "inside"
+ //TODO/LATER: it would be good to detect an error when an object should be created already, but isn't (not an "inside" call)
+ uno::Reference < embed::XEmbeddedObjectCreator > xFactory = embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() );
+ uno::Sequence< beans::PropertyValue > aObjDescr(1 + (xCopy.is() ? 1 : 0) + (pBaseURL ? 1 : 0));
+ auto itObjDescr = aObjDescr.getArray();
+ itObjDescr->Name = "Parent";
+ itObjDescr->Value <<= pImpl->m_xModel.get();
+ if (pBaseURL)
+ {
+ ++itObjDescr;
+ itObjDescr->Name = "DefaultParentBaseURL";
+ itObjDescr->Value <<= *pBaseURL;
+ }
+ if ( xCopy.is() )
+ {
+ ++itObjDescr;
+ itObjDescr->Name = "CloneFrom";
+ itObjDescr->Value <<= xCopy;
+ }
+
+ uno::Sequence< beans::PropertyValue > aMediaDescr{ comphelper::makePropertyValue(
+ "ReadOnly", bReadOnlyMode) };
+ xObj.set( xFactory->createInstanceInitFromEntry(
+ pImpl->mxStorage, rName,
+ aMediaDescr, aObjDescr ), uno::UNO_QUERY );
+
+ // insert object into my list
+ AddEmbeddedObject( xObj, rName );
+ }
+ catch (uno::Exception const& e)
+ {
+ SAL_WARN("comphelper.container", "EmbeddedObjectContainer::Get_Impl: exception caught: " << e);
+ }
+
+ return xObj;
+}
+
+uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::CreateEmbeddedObject( const uno::Sequence < sal_Int8 >& rClassId,
+ const uno::Sequence < beans::PropertyValue >& rArgs, OUString& rNewName, OUString const* pBaseURL )
+{
+ if ( rNewName.isEmpty() )
+ rNewName = CreateUniqueObjectName();
+
+ SAL_WARN_IF( HasEmbeddedObject(rNewName), "comphelper.container", "Object to create already exists!");
+
+ // create object from classid by inserting it into storage
+ uno::Reference < embed::XEmbeddedObject > xObj;
+ try
+ {
+ uno::Reference < embed::XEmbeddedObjectCreator > xFactory = embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() );
+
+ const size_t nExtraArgs = pBaseURL ? 2 : 1;
+ uno::Sequence< beans::PropertyValue > aObjDescr( rArgs.getLength() + nExtraArgs );
+ auto pObjDescr = aObjDescr.getArray();
+ pObjDescr[0].Name = "Parent";
+ pObjDescr[0].Value <<= pImpl->m_xModel.get();
+ if (pBaseURL)
+ {
+ pObjDescr[1].Name = "DefaultParentBaseURL";
+ pObjDescr[1].Value <<= *pBaseURL;
+ }
+ std::copy( rArgs.begin(), rArgs.end(), pObjDescr + nExtraArgs );
+ xObj.set( xFactory->createInstanceInitNew(
+ rClassId, OUString(), pImpl->mxStorage, rNewName,
+ aObjDescr ), uno::UNO_QUERY );
+
+ AddEmbeddedObject( xObj, rNewName );
+
+ OSL_ENSURE( !xObj.is() || xObj->getCurrentState() != embed::EmbedStates::LOADED,
+ "A freshly create object should be running always!" );
+ }
+ catch (uno::Exception const& e)
+ {
+ SAL_WARN("comphelper.container", "EmbeddedObjectContainer::CreateEmbeddedObject: exception caught: " << e);
+ }
+
+ return xObj;
+}
+
+uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::CreateEmbeddedObject( const uno::Sequence < sal_Int8 >& rClassId, OUString& rNewName, OUString const* pBaseURL )
+{
+ return CreateEmbeddedObject( rClassId, uno::Sequence < beans::PropertyValue >(), rNewName, pBaseURL );
+}
+
+void EmbeddedObjectContainer::AddEmbeddedObject( const css::uno::Reference < css::embed::XEmbeddedObject >& xObj, const OUString& rName )
+{
+#if OSL_DEBUG_LEVEL > 1
+ SAL_WARN_IF( rName.isEmpty(), "comphelper.container", "Added object doesn't have a name!");
+ uno::Reference < container::XNameAccess > xAccess( pImpl->mxStorage, uno::UNO_QUERY );
+ uno::Reference < embed::XEmbedPersist > xEmb( xObj, uno::UNO_QUERY );
+ uno::Reference < embed::XLinkageSupport > xLink( xEmb, uno::UNO_QUERY );
+ // if the object has a persistence and the object is not a link than it must have persistence entry in the storage
+ OSL_ENSURE( !( xEmb.is() && ( !xLink.is() || !xLink->isLink() ) ) || xAccess->hasByName(rName),
+ "Added element not in storage!" );
+#endif
+
+ // remember object - it needs to be in storage already
+ auto aIt = pImpl->maNameToObjectMap.find( rName );
+ OSL_ENSURE( aIt == pImpl->maNameToObjectMap.end(), "Element already inserted!" );
+ pImpl->maNameToObjectMap[ rName ] = xObj;
+ pImpl->maObjectToNameMap[ xObj ] = rName;
+ uno::Reference < container::XChild > xChild( xObj, uno::UNO_QUERY );
+ if ( xChild.is() && xChild->getParent() != pImpl->m_xModel.get() )
+ xChild->setParent( pImpl->m_xModel.get() );
+
+ // look for object in temporary container
+ if ( !pImpl->mpTempObjectContainer )
+ return;
+
+ auto& rObjectContainer = pImpl->mpTempObjectContainer->pImpl->maNameToObjectMap;
+ auto aIter = std::find_if(rObjectContainer.begin(), rObjectContainer.end(),
+ [&xObj](const EmbeddedObjectContainerNameMap::value_type& rEntry) { return rEntry.second == xObj; });
+ if (aIter == rObjectContainer.end())
+ return;
+
+ // copy replacement image from temporary container (if there is any)
+ OUString aTempName = aIter->first;
+ OUString aMediaType;
+ uno::Reference < io::XInputStream > xStream = pImpl->mpTempObjectContainer->GetGraphicStream( xObj, &aMediaType );
+ if ( xStream.is() )
+ {
+ InsertGraphicStream( xStream, rName, aMediaType );
+ xStream = nullptr;
+ pImpl->mpTempObjectContainer->RemoveGraphicStream( aTempName );
+ }
+
+ // remove object from storage of temporary container
+ uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
+ if ( xPersist.is() )
+ {
+ try
+ {
+ pImpl->mpTempObjectContainer->pImpl->mxStorage->removeElement( aTempName );
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ }
+
+ // temp. container needs to forget the object
+ pImpl->mpTempObjectContainer->pImpl->maObjectToNameMap.erase( aIter->second );
+ pImpl->mpTempObjectContainer->pImpl->maNameToObjectMap.erase( aIter );
+}
+
+bool EmbeddedObjectContainer::StoreEmbeddedObject(
+ const uno::Reference < embed::XEmbeddedObject >& xObj, OUString& rName, bool bCopy,
+ const OUString& rSrcShellID, const OUString& rDestShellID )
+{
+ uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
+ if ( rName.isEmpty() )
+ rName = CreateUniqueObjectName();
+
+#if OSL_DEBUG_LEVEL > 1
+ uno::Reference < container::XNameAccess > xAccess( pImpl->mxStorage, uno::UNO_QUERY );
+ OSL_ENSURE( !xPersist.is() || !xAccess->hasByName(rName), "Inserting element already present in storage!" );
+ OSL_ENSURE( xPersist.is() || xObj->getCurrentState() == embed::EmbedStates::RUNNING, "Non persistent object inserted!");
+#endif
+
+ // insert objects' storage into the container storage (if object has one)
+ try
+ {
+ if ( xPersist.is() )
+ {
+ uno::Sequence < beans::PropertyValue > aSeq;
+ if ( bCopy )
+ {
+ auto aObjArgs(::comphelper::InitPropertySequence({
+ { "SourceShellID", uno::Any(rSrcShellID) },
+ { "DestinationShellID", uno::Any(rDestShellID) }
+ }));
+ xPersist->storeToEntry(pImpl->mxStorage, rName, aSeq, aObjArgs);
+ }
+ else
+ {
+ //TODO/LATER: possible optimization, don't store immediately
+ //xPersist->setPersistentEntry( pImpl->mxStorage, rName, embed::EntryInitModes::ENTRY_NO_INIT, aSeq, aSeq );
+ xPersist->storeAsEntry( pImpl->mxStorage, rName, aSeq, aSeq );
+ xPersist->saveCompleted( true );
+ }
+ }
+ }
+ catch (uno::Exception const& e)
+ {
+ SAL_WARN("comphelper.container", "EmbeddedObjectContainer::StoreEmbeddedObject: exception caught: " << e);
+ // TODO/LATER: better error recovery should keep storage intact
+ return false;
+ }
+
+ return true;
+}
+
+bool EmbeddedObjectContainer::InsertEmbeddedObject( const uno::Reference < embed::XEmbeddedObject >& xObj, OUString& rName )
+{
+ // store it into the container storage
+ if (StoreEmbeddedObject(xObj, rName, false, OUString(), OUString()))
+ {
+ // remember object
+ AddEmbeddedObject( xObj, rName );
+ return true;
+ }
+ else
+ return false;
+}
+
+uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::InsertEmbeddedObject( const uno::Reference < io::XInputStream >& xStm, OUString& rNewName )
+{
+ if ( rNewName.isEmpty() )
+ rNewName = CreateUniqueObjectName();
+
+ // store it into the container storage
+ bool bIsStorage = false;
+ try
+ {
+ // first try storage persistence
+ uno::Reference < embed::XStorage > xStore = ::comphelper::OStorageHelper::GetStorageFromInputStream( xStm );
+
+ // storage was created from stream successfully
+ bIsStorage = true;
+
+ uno::Reference < embed::XStorage > xNewStore = pImpl->mxStorage->openStorageElement( rNewName, embed::ElementModes::READWRITE );
+ xStore->copyToStorage( xNewStore );
+ }
+ catch (const uno::Exception&)
+ {
+ if ( bIsStorage )
+ // it is storage persistence, but opening of new substorage or copying to it failed
+ return uno::Reference < embed::XEmbeddedObject >();
+
+ // stream didn't contain a storage, now try stream persistence
+ try
+ {
+ uno::Reference < io::XStream > xNewStream = pImpl->mxStorage->openStreamElement( rNewName, embed::ElementModes::READWRITE );
+ ::comphelper::OStorageHelper::CopyInputToOutput( xStm, xNewStream->getOutputStream() );
+
+ // No mediatype is provided so the default for OLE objects value is used
+ // it is correct so for now, but what if somebody introduces a new stream based embedded object?
+ // Probably introducing of such an object must be restricted ( a storage must be used! ).
+ uno::Reference< beans::XPropertySet > xProps( xNewStream, uno::UNO_QUERY_THROW );
+ xProps->setPropertyValue("MediaType",
+ uno::Any( OUString( "application/vnd.sun.star.oleobject" ) ) );
+ }
+ catch (uno::Exception const& e)
+ {
+ // complete disaster!
+ SAL_WARN("comphelper.container", "EmbeddedObjectContainer::InsertEmbeddedObject: exception caught: " << e);
+ return uno::Reference < embed::XEmbeddedObject >();
+ }
+ }
+
+ // stream was copied into the container storage in either way, now try to open something form it
+ uno::Reference < embed::XEmbeddedObject > xRet = GetEmbeddedObject( rNewName );
+ try
+ {
+ if ( !xRet.is() )
+ // no object could be created, so withdraw insertion
+ pImpl->mxStorage->removeElement( rNewName );
+ }
+ catch (const uno::Exception&)
+ {
+ }
+
+ return xRet;
+}
+
+uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::InsertEmbeddedObject( const css::uno::Sequence < css::beans::PropertyValue >& aMedium, OUString& rNewName, OUString const* pBaseURL )
+{
+ if ( rNewName.isEmpty() )
+ rNewName = CreateUniqueObjectName();
+
+ uno::Reference < embed::XEmbeddedObject > xObj;
+ try
+ {
+ uno::Reference < embed::XEmbeddedObjectCreator > xFactory = embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() );
+ uno::Sequence< beans::PropertyValue > aObjDescr(pBaseURL ? 2 : 1);
+ auto pObjDescr = aObjDescr.getArray();
+ pObjDescr[0].Name = "Parent";
+ pObjDescr[0].Value <<= pImpl->m_xModel.get();
+ if (pBaseURL)
+ {
+ pObjDescr[1].Name = "DefaultParentBaseURL";
+ pObjDescr[1].Value <<= *pBaseURL;
+ }
+ xObj.set( xFactory->createInstanceInitFromMediaDescriptor(
+ pImpl->mxStorage, rNewName, aMedium, aObjDescr ), uno::UNO_QUERY );
+ uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
+
+ OSL_ENSURE( !xObj.is() || xObj->getCurrentState() != embed::EmbedStates::LOADED,
+ "A freshly create object should be running always!" );
+
+ // possible optimization: store later!
+ if ( xPersist.is())
+ xPersist->storeOwn();
+
+ AddEmbeddedObject( xObj, rNewName );
+ }
+ catch (const uno::Exception&)
+ {
+ }
+
+ return xObj;
+}
+
+uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::InsertEmbeddedLink( const css::uno::Sequence < css::beans::PropertyValue >& aMedium, OUString& rNewName )
+{
+ if ( rNewName.isEmpty() )
+ rNewName = CreateUniqueObjectName();
+
+ uno::Reference < embed::XEmbeddedObject > xObj;
+ try
+ {
+ uno::Reference < embed::XEmbeddedObjectCreator > xFactory = embed::EmbeddedObjectCreator::create(::comphelper::getProcessComponentContext());
+ uno::Sequence< beans::PropertyValue > aObjDescr{ comphelper::makePropertyValue(
+ "Parent", pImpl->m_xModel.get()) };
+ xObj.set( xFactory->createInstanceLink( pImpl->mxStorage, rNewName, aMedium, aObjDescr ), uno::UNO_QUERY );
+
+ uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
+
+ OSL_ENSURE( !xObj.is() || xObj->getCurrentState() != embed::EmbedStates::LOADED,
+ "A freshly create object should be running always!" );
+
+ // possible optimization: store later!
+ if ( xPersist.is())
+ xPersist->storeOwn();
+
+ AddEmbeddedObject( xObj, rNewName );
+ }
+ catch (uno::Exception const& e)
+ {
+ SAL_WARN("comphelper.container", "EmbeddedObjectContainer::InsertEmbeddedLink: "
+ "exception caught: " << e);
+ }
+
+ return xObj;
+}
+
+bool EmbeddedObjectContainer::TryToCopyGraphReplacement( EmbeddedObjectContainer& rSrc,
+ const OUString& aOrigName,
+ const OUString& aTargetName )
+{
+ bool bResult = false;
+
+ if ( ( &rSrc != this || aOrigName != aTargetName ) && !aOrigName.isEmpty() && !aTargetName.isEmpty() )
+ {
+ OUString aMediaType;
+ uno::Reference < io::XInputStream > xGrStream = rSrc.GetGraphicStream( aOrigName, &aMediaType );
+ if ( xGrStream.is() )
+ bResult = InsertGraphicStream( xGrStream, aTargetName, aMediaType );
+ }
+
+ return bResult;
+}
+
+uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::CopyAndGetEmbeddedObject(
+ EmbeddedObjectContainer& rSrc, const uno::Reference <embed::XEmbeddedObject>& xObj, OUString& rName,
+ const OUString& rSrcShellID, const OUString& rDestShellID )
+{
+ uno::Reference< embed::XEmbeddedObject > xResult;
+
+ // TODO/LATER: For now only objects that implement XEmbedPersist have a replacement image, it might change in future
+ // do an incompatible change so that object name is provided in all the move and copy methods
+ OUString aOrigName;
+ try
+ {
+ uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY_THROW );
+ aOrigName = xPersist->getEntryName();
+ }
+ catch (const uno::Exception&)
+ {
+ }
+
+ if ( rName.isEmpty() )
+ rName = CreateUniqueObjectName();
+
+ // objects without persistence are not really stored by the method
+ if (xObj.is() && StoreEmbeddedObject(xObj, rName, true, rSrcShellID, rDestShellID))
+ {
+ SAL_INFO_IF(rDestShellID.isEmpty(), "comphelper.container",
+ "SfxObjectShell with no base URL?"); // every shell has a base URL, except the clipboard SwDocShell
+ xResult = Get_Impl(rName, xObj, &rDestShellID);
+ if ( !xResult.is() )
+ {
+ // this is a case when object has no real persistence
+ // in such cases a new object should be explicitly created and initialized with the data of the old one
+ try
+ {
+ uno::Reference< embed::XLinkageSupport > xOrigLinkage( xObj, uno::UNO_QUERY );
+ if ( xOrigLinkage.is() && xOrigLinkage->isLink() )
+ {
+ // this is an OOo link, it has no persistence
+ OUString aURL = xOrigLinkage->getLinkURL();
+ if ( aURL.isEmpty() )
+ throw uno::RuntimeException();
+
+ // create new linked object from the URL the link is based on
+ uno::Reference < embed::XEmbeddedObjectCreator > xCreator =
+ embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() );
+
+ uno::Sequence< beans::PropertyValue > aMediaDescr{ comphelper::makePropertyValue(
+ "URL", aURL) };
+ uno::Sequence< beans::PropertyValue > aObjDescr{ comphelper::makePropertyValue(
+ "Parent", pImpl->m_xModel.get()) };
+ xResult.set(xCreator->createInstanceLink(
+ pImpl->mxStorage,
+ rName,
+ aMediaDescr,
+ aObjDescr ),
+ uno::UNO_QUERY_THROW );
+ }
+ else
+ {
+ // the component is required for copying of this object
+ if ( xObj->getCurrentState() == embed::EmbedStates::LOADED )
+ xObj->changeState( embed::EmbedStates::RUNNING );
+
+ // this must be an object based on properties, otherwise we can not copy it currently
+ uno::Reference< beans::XPropertySet > xOrigProps( xObj->getComponent(), uno::UNO_QUERY_THROW );
+
+ // use object class ID to create a new one and transfer all the properties
+ uno::Reference < embed::XEmbeddedObjectCreator > xCreator =
+ embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() );
+
+ uno::Sequence< beans::PropertyValue > aObjDescr{ comphelper::makePropertyValue(
+ "Parent", pImpl->m_xModel.get()) };
+ xResult.set(xCreator->createInstanceInitNew(
+ xObj->getClassID(),
+ xObj->getClassName(),
+ pImpl->mxStorage,
+ rName,
+ aObjDescr ),
+ uno::UNO_QUERY_THROW );
+
+ if ( xResult->getCurrentState() == embed::EmbedStates::LOADED )
+ xResult->changeState( embed::EmbedStates::RUNNING );
+
+ uno::Reference< beans::XPropertySet > xTargetProps( xResult->getComponent(), uno::UNO_QUERY_THROW );
+
+ // copy all the properties from xOrigProps to xTargetProps
+ uno::Reference< beans::XPropertySetInfo > xOrigInfo = xOrigProps->getPropertySetInfo();
+ if ( !xOrigInfo.is() )
+ throw uno::RuntimeException();
+
+ const uno::Sequence< beans::Property > aPropertiesList = xOrigInfo->getProperties();
+ for ( const auto & p : aPropertiesList )
+ {
+ try
+ {
+ xTargetProps->setPropertyValue(
+ p.Name,
+ xOrigProps->getPropertyValue( p.Name ) );
+ }
+ catch (const beans::PropertyVetoException&)
+ {
+ // impossibility to copy readonly property is not treated as an error for now
+ // but the assertion is helpful to detect such scenarios and review them
+ SAL_WARN( "comphelper.container", "Could not copy readonly property!" );
+ }
+ }
+ }
+
+ if ( xResult.is() )
+ AddEmbeddedObject( xResult, rName );
+ }
+ catch (const uno::Exception&)
+ {
+ if ( xResult.is() )
+ {
+ try
+ {
+ xResult->close( true );
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ xResult.clear();
+ }
+ }
+ }
+ }
+
+ SAL_WARN_IF( !xResult.is(), "comphelper.container", "Can not copy embedded object that has no persistence!" );
+
+ if ( xResult.is() )
+ {
+ // the object is successfully copied, try to copy graphical replacement
+ if ( !aOrigName.isEmpty() )
+ TryToCopyGraphReplacement( rSrc, aOrigName, rName );
+
+ // the object might need the size to be set
+ try
+ {
+ if ( xResult->getStatus( embed::Aspects::MSOLE_CONTENT ) & embed::EmbedMisc::EMBED_NEEDSSIZEONLOAD )
+ xResult->setVisualAreaSize( embed::Aspects::MSOLE_CONTENT,
+ xObj->getVisualAreaSize( embed::Aspects::MSOLE_CONTENT ) );
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ }
+
+ return xResult;
+}
+
+// #i119941, bKeepToTempStorage: use to specify whether store the removed object to temporary storage+
+void EmbeddedObjectContainer::RemoveEmbeddedObject( const OUString& rName, bool bKeepToTempStorage )
+{
+ uno::Reference < embed::XEmbeddedObject > xObj = GetEmbeddedObject( rName );
+ if ( xObj.is() )
+ RemoveEmbeddedObject( xObj, bKeepToTempStorage );
+}
+
+bool EmbeddedObjectContainer::MoveEmbeddedObject( const OUString& rName, EmbeddedObjectContainer& rCnt )
+{
+ // find object entry
+ auto aIt2 = rCnt.pImpl->maNameToObjectMap.find( rName );
+ OSL_ENSURE( aIt2 == rCnt.pImpl->maNameToObjectMap.end(), "Object does already exist in target container!" );
+
+ if ( aIt2 != rCnt.pImpl->maNameToObjectMap.end() )
+ return false;
+
+ uno::Reference < embed::XEmbeddedObject > xObj;
+ auto aIt = pImpl->maNameToObjectMap.find( rName );
+ if ( aIt != pImpl->maNameToObjectMap.end() )
+ {
+ xObj = (*aIt).second;
+ try
+ {
+ if ( xObj.is() )
+ {
+ // move object
+ OUString aName( rName );
+ rCnt.InsertEmbeddedObject( xObj, aName );
+ pImpl->maObjectToNameMap.erase( aIt->second );
+ pImpl->maNameToObjectMap.erase( aIt );
+ uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
+ if ( xPersist.is() )
+ pImpl->mxStorage->removeElement( rName );
+ }
+ else
+ {
+ // copy storages; object *must* have persistence!
+ uno::Reference < embed::XStorage > xOld = pImpl->mxStorage->openStorageElement( rName, embed::ElementModes::READ );
+ uno::Reference < embed::XStorage > xNew = rCnt.pImpl->mxStorage->openStorageElement( rName, embed::ElementModes::READWRITE );
+ xOld->copyToStorage( xNew );
+ }
+
+ rCnt.TryToCopyGraphReplacement( *this, rName, rName );
+ // RemoveGraphicStream( rName );
+
+ return true;
+ }
+ catch (const uno::Exception&)
+ {
+ SAL_WARN( "comphelper.container", "Could not move object!");
+ return false;
+ }
+
+ }
+ else
+ SAL_WARN( "comphelper.container", "Unknown object!");
+ return false;
+}
+
+// #i119941, bKeepToTempStorage: use to specify whether store the removed object to temporary storage+
+bool EmbeddedObjectContainer::RemoveEmbeddedObject( const uno::Reference < embed::XEmbeddedObject >& xObj, bool bKeepToTempStorage )
+{
+ uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
+ OUString aName;
+ if ( xPersist.is() )
+ aName = xPersist->getEntryName();
+
+#if OSL_DEBUG_LEVEL > 1
+ uno::Reference < container::XNameAccess > xAccess( pImpl->mxStorage, uno::UNO_QUERY );
+ uno::Reference < embed::XLinkageSupport > xLink( xPersist, uno::UNO_QUERY );
+ sal_Bool bIsNotEmbedded = !xPersist.is() || ( xLink.is() && xLink->isLink() );
+
+ // if the object has a persistence and the object is not a link than it must have persistence entry in the storage
+ OSL_ENSURE( bIsNotEmbedded || xAccess->hasByName(aName), "Removing element not present in storage!" );
+#endif
+
+ // somebody still needs the object, so we must assign a temporary persistence
+ try
+ {
+ if ( xPersist.is() && bKeepToTempStorage ) // #i119941
+ {
+
+ if ( !pImpl->mpTempObjectContainer )
+ {
+ pImpl->mpTempObjectContainer = new EmbeddedObjectContainer();
+ try
+ {
+ // TODO/LATER: in future probably the temporary container will have two storages ( of two formats )
+ // the media type will be provided with object insertion
+ OUString aOrigStorMediaType;
+ uno::Reference< beans::XPropertySet > xStorProps( pImpl->mxStorage, uno::UNO_QUERY_THROW );
+ static constexpr OUString s_sMediaType(u"MediaType"_ustr);
+ xStorProps->getPropertyValue( s_sMediaType ) >>= aOrigStorMediaType;
+
+ SAL_WARN_IF( aOrigStorMediaType.isEmpty(), "comphelper.container", "No valuable media type in the storage!" );
+
+ uno::Reference< beans::XPropertySet > xTargetStorProps(
+ pImpl->mpTempObjectContainer->pImpl->mxStorage,
+ uno::UNO_QUERY_THROW );
+ xTargetStorProps->setPropertyValue( s_sMediaType,uno::Any( aOrigStorMediaType ) );
+ }
+ catch (const uno::Exception&)
+ {
+ SAL_WARN( "comphelper.container", "Can not set the new media type to a storage!" );
+ }
+ }
+
+ OUString aTempName, aMediaType;
+ /* Do not create a new name for a removed object, in the pImpl->mpTempObjectContainer,
+ because the original m_aEntryName of xObj will be overwritten by InsertEmbeddedObject(),
+ so uno::Reference < embed::XEmbeddedObject >& xObj will misbehave in
+ EmbeddedObjectContainer::StoreAsChildren and SfxObjectShell::SaveCompletedChildren
+ and will throw an exception because of objects with the same names! */
+ if( !pImpl->mpTempObjectContainer->HasEmbeddedObject(aName) )
+ aTempName = aName;
+
+ pImpl->mpTempObjectContainer->InsertEmbeddedObject( xObj, aTempName );
+
+ uno::Reference < io::XInputStream > xStream = GetGraphicStream( xObj, &aMediaType );
+ if ( xStream.is() )
+ pImpl->mpTempObjectContainer->InsertGraphicStream( xStream, aTempName, aMediaType );
+
+ // object is stored, so at least it can be set to loaded state
+ xObj->changeState( embed::EmbedStates::LOADED );
+ }
+ else
+ // objects without persistence need to stay in running state if they shall not be closed
+ xObj->changeState( embed::EmbedStates::RUNNING );
+ }
+ catch (const uno::Exception&)
+ {
+ return false;
+ }
+
+ auto aIter = std::find_if(pImpl->maNameToObjectMap.begin(), pImpl->maNameToObjectMap.end(),
+ [&xObj](const EmbeddedObjectContainerNameMap::value_type& rEntry) { return rEntry.second == xObj; });
+ if (aIter != pImpl->maNameToObjectMap.end())
+ {
+ pImpl->maObjectToNameMap.erase( aIter->second );
+ pImpl->maNameToObjectMap.erase( aIter );
+ uno::Reference < container::XChild > xChild( xObj, uno::UNO_QUERY );
+ if ( xChild.is() )
+ xChild->setParent( uno::Reference < uno::XInterface >() );
+ }
+ else
+ SAL_WARN( "comphelper.container", "Object not found for removal!" );
+
+ if ( !xPersist || !bKeepToTempStorage ) // #i119941#
+ return true;
+
+ // remove replacement image (if there is one)
+ RemoveGraphicStream( aName );
+
+ // now it's time to remove the storage from the container storage
+ try
+ {
+#if OSL_DEBUG_LEVEL > 1
+ // if the object has a persistence and the object is not a link than it must have persistence entry in storage
+ OSL_ENSURE( bIsNotEmbedded || pImpl->mxStorage->hasByName( aName ), "The object has no persistence entry in the storage!" );
+#endif
+ if ( xPersist.is() && pImpl->mxStorage->hasByName( aName ) )
+ pImpl->mxStorage->removeElement( aName );
+ }
+ catch (const uno::Exception&)
+ {
+ SAL_WARN( "comphelper.container", "Failed to remove object from storage!" );
+ return false;
+ }
+
+ return true;
+}
+
+void EmbeddedObjectContainer::CloseEmbeddedObject( const uno::Reference < embed::XEmbeddedObject >& xObj )
+{
+ // disconnect the object from the container and close it if possible
+
+ auto aIter = std::find_if(pImpl->maNameToObjectMap.begin(), pImpl->maNameToObjectMap.end(),
+ [&xObj](const EmbeddedObjectContainerNameMap::value_type& rEntry) { return rEntry.second == xObj; });
+ if (aIter == pImpl->maNameToObjectMap.end())
+ return;
+
+ pImpl->maObjectToNameMap.erase( aIter->second );
+ pImpl->maNameToObjectMap.erase( aIter );
+
+ try
+ {
+ xObj->close( true );
+ }
+ catch (const uno::Exception&)
+ {
+ // it is no problem if the object is already closed
+ // TODO/LATER: what if the object can not be closed?
+ }
+}
+
+uno::Reference < io::XInputStream > EmbeddedObjectContainer::GetGraphicStream( const OUString& aName, OUString* pMediaType )
+{
+ uno::Reference < io::XInputStream > xStream;
+
+ SAL_WARN_IF( aName.isEmpty(), "comphelper.container", "Retrieving graphic for unknown object!" );
+ if ( !aName.isEmpty() )
+ {
+ try
+ {
+ uno::Reference < embed::XStorage > xReplacements = pImpl->GetReplacements();
+ uno::Reference < io::XStream > xGraphicStream = xReplacements->openStreamElement( aName, embed::ElementModes::READ );
+ xStream = xGraphicStream->getInputStream();
+ if ( pMediaType )
+ {
+ uno::Reference < beans::XPropertySet > xSet( xStream, uno::UNO_QUERY );
+ if ( xSet.is() )
+ {
+ uno::Any aAny = xSet->getPropertyValue("MediaType");
+ aAny >>= *pMediaType;
+ }
+ }
+ }
+ catch (uno::Exception const& e)
+ {
+ SAL_INFO("comphelper.container",
+ "EmbeddedObjectContainer::GetGraphicStream(): " << e);
+ }
+ }
+
+ return xStream;
+}
+
+uno::Reference < io::XInputStream > EmbeddedObjectContainer::GetGraphicStream( const css::uno::Reference < css::embed::XEmbeddedObject >& xObj, OUString* pMediaType )
+{
+ // try to load it from the container storage
+ return GetGraphicStream( GetEmbeddedObjectName( xObj ), pMediaType );
+}
+
+bool EmbeddedObjectContainer::InsertGraphicStream( const css::uno::Reference < css::io::XInputStream >& rStream, const OUString& rObjectName, const OUString& rMediaType )
+{
+ try
+ {
+ uno::Reference < embed::XStorage > xReplacements = pImpl->GetReplacements();
+
+ // store it into the subfolder
+ uno::Reference < io::XOutputStream > xOutStream;
+ uno::Reference < io::XStream > xGraphicStream = xReplacements->openStreamElement( rObjectName,
+ embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE );
+ xOutStream = xGraphicStream->getOutputStream();
+ ::comphelper::OStorageHelper::CopyInputToOutput( rStream, xOutStream );
+ xOutStream->flush();
+
+ uno::Reference< beans::XPropertySet > xPropSet( xGraphicStream, uno::UNO_QUERY_THROW );
+
+ xPropSet->setPropertyValue("UseCommonStoragePasswordEncryption",
+ uno::Any( true ) );
+ xPropSet->setPropertyValue("MediaType", uno::Any(rMediaType) );
+
+ xPropSet->setPropertyValue("Compressed",
+ uno::Any( true ) );
+ }
+ catch (const uno::Exception&)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool EmbeddedObjectContainer::InsertGraphicStreamDirectly( const css::uno::Reference < css::io::XInputStream >& rStream, const OUString& rObjectName, const OUString& rMediaType )
+{
+ try
+ {
+ uno::Reference < embed::XStorage > xReplacement = pImpl->GetReplacements();
+ uno::Reference < embed::XOptimizedStorage > xOptRepl( xReplacement, uno::UNO_QUERY_THROW );
+
+ // store it into the subfolder
+ uno::Sequence< beans::PropertyValue > aProps{
+ comphelper::makePropertyValue("MediaType", rMediaType),
+ comphelper::makePropertyValue("UseCommonStoragePasswordEncryption", true),
+ comphelper::makePropertyValue("Compressed", true)
+ };
+
+ if ( xReplacement->hasByName( rObjectName ) )
+ xReplacement->removeElement( rObjectName );
+
+ xOptRepl->insertStreamElementDirect( rObjectName, rStream, aProps );
+ }
+ catch (const uno::Exception&)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+
+void EmbeddedObjectContainer::RemoveGraphicStream( const OUString& rObjectName )
+{
+ try
+ {
+ uno::Reference < embed::XStorage > xReplacements = pImpl->GetReplacements();
+ xReplacements->removeElement( rObjectName );
+ }
+ catch (const uno::Exception&)
+ {
+ }
+}
+namespace {
+ void InsertStreamIntoPicturesStorage_Impl( const uno::Reference< embed::XStorage >& xDocStor,
+ const uno::Reference< io::XInputStream >& xInStream,
+ const OUString& aStreamName )
+ {
+ OSL_ENSURE( !aStreamName.isEmpty() && xInStream.is() && xDocStor.is(), "Misuse of the method!" );
+
+ try
+ {
+ uno::Reference< embed::XStorage > xPictures = xDocStor->openStorageElement(
+ "Pictures",
+ embed::ElementModes::READWRITE );
+ uno::Reference< io::XStream > xObjReplStr = xPictures->openStreamElement(
+ aStreamName,
+ embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE );
+ uno::Reference< io::XOutputStream > xOutStream(
+ xObjReplStr->getInputStream(), uno::UNO_QUERY_THROW );
+
+ ::comphelper::OStorageHelper::CopyInputToOutput( xInStream, xOutStream );
+ xOutStream->closeOutput();
+
+ uno::Reference< embed::XTransactedObject > xTransact( xPictures, uno::UNO_QUERY );
+ if ( xTransact.is() )
+ xTransact->commit();
+ }
+ catch (const uno::Exception&)
+ {
+ SAL_WARN( "comphelper.container", "The images storage is not available!" );
+ }
+ }
+
+}
+
+bool EmbeddedObjectContainer::StoreAsChildren(bool _bOasisFormat,bool _bCreateEmbedded, bool _bAutoSaveEvent,
+ const uno::Reference < embed::XStorage >& _xStorage)
+{
+ bool bResult = false;
+ try
+ {
+ comphelper::EmbeddedObjectContainer aCnt( _xStorage );
+ const uno::Sequence < OUString > aNames = GetObjectNames();
+ const OUString* pIter = aNames.getConstArray();
+ const OUString* pEnd = pIter + aNames.getLength();
+ for(;pIter != pEnd;++pIter)
+ {
+ uno::Reference < embed::XEmbeddedObject > xObj = GetEmbeddedObject( *pIter );
+ SAL_WARN_IF( !xObj.is(), "comphelper.container", "An empty entry in the embedded objects list!" );
+ if ( xObj.is() )
+ {
+ bool bSwitchBackToLoaded = false;
+ uno::Reference< embed::XLinkageSupport > xLink( xObj, uno::UNO_QUERY );
+
+ uno::Reference < io::XInputStream > xStream;
+ OUString aMediaType;
+
+ sal_Int32 nCurState = xObj->getCurrentState();
+ if ( nCurState == embed::EmbedStates::LOADED || nCurState == embed::EmbedStates::RUNNING )
+ {
+ // means that the object is not active
+ // copy replacement image from old to new container
+ xStream = GetGraphicStream( xObj, &aMediaType );
+ }
+
+ if ( !xStream.is() && getUserAllowsLinkUpdate() )
+ {
+ // the image must be regenerated
+ // TODO/LATER: another aspect could be used
+ if ( xObj->getCurrentState() == embed::EmbedStates::LOADED )
+ bSwitchBackToLoaded = true;
+
+ xStream = GetGraphicReplacementStream(
+ embed::Aspects::MSOLE_CONTENT,
+ xObj,
+ &aMediaType );
+ }
+
+ if ( _bOasisFormat || (xLink.is() && xLink->isLink()) )
+ {
+ if ( xStream.is() )
+ {
+ if ( _bOasisFormat )
+ {
+ // if it is an embedded object or the optimized inserting fails the normal inserting should be done
+ if ( _bCreateEmbedded
+ || !aCnt.InsertGraphicStreamDirectly( xStream, *pIter, aMediaType ) )
+ aCnt.InsertGraphicStream( xStream, *pIter, aMediaType );
+ }
+ else
+ {
+ // it is a linked object exported into SO7 format
+ InsertStreamIntoPicturesStorage_Impl( _xStorage, xStream, *pIter );
+ }
+ }
+ }
+
+ uno::Reference< embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
+ if ( xPersist.is() )
+ {
+ uno::Sequence< beans::PropertyValue > aArgs( _bOasisFormat ? 3 : 4 );
+ auto pArgs = aArgs.getArray();
+ pArgs[0].Name = "StoreVisualReplacement";
+ pArgs[0].Value <<= !_bOasisFormat;
+
+ // if it is an embedded object or the optimized inserting fails the normal inserting should be done
+ pArgs[1].Name = "CanTryOptimization";
+ pArgs[1].Value <<= !_bCreateEmbedded;
+
+ pArgs[2].Name = "AutoSaveEvent";
+ pArgs[2].Value <<= _bAutoSaveEvent;
+
+ if ( !_bOasisFormat )
+ {
+ // if object has no cached replacement it will use this one
+ pArgs[3].Name = "VisualReplacement";
+ pArgs[3].Value <<= xStream;
+ }
+
+ try
+ {
+ xPersist->storeAsEntry( _xStorage, xPersist->getEntryName(), uno::Sequence< beans::PropertyValue >(), aArgs );
+ }
+ catch (const embed::WrongStateException&)
+ {
+ SAL_WARN("comphelper.container", "failed to store '" << *pIter << "'");
+ }
+ }
+
+ if ( bSwitchBackToLoaded )
+ // switch back to loaded state; that way we have a minimum cache confusion
+ xObj->changeState( embed::EmbedStates::LOADED );
+ }
+ }
+
+ bResult = aCnt.CommitImageSubStorage();
+
+ }
+ catch (const uno::Exception& e)
+ {
+ // TODO/LATER: error handling
+ bResult = false;
+ SAL_WARN("comphelper.container", "failed. Message: " << e);
+ }
+
+ // the old SO6 format does not store graphical replacements
+ if ( !_bOasisFormat && bResult )
+ {
+ try
+ {
+ // the substorage still can not be locked by the embedded object container
+ OUString aObjReplElement( "ObjectReplacements" );
+ if ( _xStorage->hasByName( aObjReplElement ) && _xStorage->isStorageElement( aObjReplElement ) )
+ _xStorage->removeElement( aObjReplElement );
+ }
+ catch (const uno::Exception&)
+ {
+ // TODO/LATER: error handling;
+ bResult = false;
+ }
+ }
+ return bResult;
+}
+
+bool EmbeddedObjectContainer::StoreChildren(bool _bOasisFormat,bool _bObjectsOnly)
+{
+ bool bResult = true;
+ const uno::Sequence < OUString > aNames = GetObjectNames();
+ const OUString* pIter = aNames.getConstArray();
+ const OUString* pEnd = pIter + aNames.getLength();
+ for(;pIter != pEnd;++pIter)
+ {
+ try
+ {
+ uno::Reference < embed::XEmbeddedObject > xObj = GetEmbeddedObject( *pIter );
+ SAL_WARN_IF( !xObj.is(), "comphelper.container", "An empty entry in the embedded objects list!" );
+ if ( xObj.is() )
+ {
+ sal_Int32 nCurState = xObj->getCurrentState();
+ if ( _bOasisFormat && nCurState != embed::EmbedStates::LOADED && nCurState != embed::EmbedStates::RUNNING )
+ {
+ // means that the object is active
+ // the image must be regenerated
+ OUString aMediaType;
+
+ // TODO/LATER: another aspect could be used
+ uno::Reference < io::XInputStream > xStream =
+ GetGraphicReplacementStream(
+ embed::Aspects::MSOLE_CONTENT,
+ xObj,
+ &aMediaType );
+ if ( xStream.is() )
+ {
+ if ( !InsertGraphicStreamDirectly( xStream, *pIter, aMediaType ) )
+ InsertGraphicStream( xStream, *pIter, aMediaType );
+ }
+ }
+
+ // TODO/LATER: currently the object by default does not cache replacement image
+ // that means that if somebody loads SO7 document and store its objects using
+ // this method the images might be lost.
+ // Currently this method is only used on storing to alien formats, that means
+ // that SO7 documents storing does not use it, and all other filters are
+ // based on OASIS format. But if it changes the method must be fixed. The fix
+ // must be done only on demand since it can affect performance.
+
+ uno::Reference< embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
+ if ( xPersist.is() )
+ {
+ try
+ {
+ //TODO/LATER: only storing if changed!
+ //xPersist->storeOwn(); //commented, i120168
+
+ // begin:all charts will be persisted as xml format on disk when saving, which is time consuming.
+ // '_bObjectsOnly' mean we are storing to alien formats.
+ // 'isStorageElement' mean current object is NOT a MS OLE format. (may also include in future), i120168
+ if (_bObjectsOnly && (nCurState == embed::EmbedStates::LOADED || nCurState == embed::EmbedStates::RUNNING)
+ && (pImpl->mxStorage->isStorageElement( *pIter ) ))
+ {
+ uno::Reference< util::XModifiable > xModifiable( xObj->getComponent(), uno::UNO_QUERY );
+ if ( xModifiable.is() && xModifiable->isModified())
+ {
+ xPersist->storeOwn();
+ }
+ else
+ {
+ //do nothing. Embedded model is not modified, no need to persist.
+ }
+ }
+ else //the embedded object is in active status, always store back it.
+ {
+ xPersist->storeOwn();
+ }
+ //end i120168
+ }
+ catch (const uno::Exception&)
+ {
+ // TODO/LATER: error handling
+ bResult = false;
+ break;
+ }
+ }
+
+ if ( !_bOasisFormat && !_bObjectsOnly )
+ {
+ // copy replacement images for linked objects
+ try
+ {
+ uno::Reference< embed::XLinkageSupport > xLink( xObj, uno::UNO_QUERY );
+ if ( xLink.is() && xLink->isLink() )
+ {
+ OUString aMediaType;
+ uno::Reference < io::XInputStream > xInStream = GetGraphicStream( xObj, &aMediaType );
+ if ( xInStream.is() )
+ InsertStreamIntoPicturesStorage_Impl( pImpl->mxStorage, xInStream, *pIter );
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ }
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ // TODO/LATER: error handling
+ }
+ }
+
+ if ( bResult && _bOasisFormat )
+ bResult = CommitImageSubStorage();
+
+ if ( bResult && !_bObjectsOnly )
+ {
+ try
+ {
+ ReleaseImageSubStorage();
+ OUString aObjReplElement( "ObjectReplacements" );
+ if ( !_bOasisFormat && pImpl->mxStorage->hasByName( aObjReplElement ) && pImpl->mxStorage->isStorageElement( aObjReplElement ) )
+ pImpl->mxStorage->removeElement( aObjReplElement );
+ }
+ catch (const uno::Exception&)
+ {
+ // TODO/LATER: error handling
+ bResult = false;
+ }
+ }
+ return bResult;
+}
+
+uno::Reference< io::XInputStream > EmbeddedObjectContainer::GetGraphicReplacementStream(
+ sal_Int64 nViewAspect,
+ const uno::Reference< embed::XEmbeddedObject >& xObj,
+ OUString* pMediaType )
+{
+ uno::Reference< io::XInputStream > xInStream;
+ if ( xObj.is() )
+ {
+ try
+ {
+ // retrieving of the visual representation can switch object to running state
+ embed::VisualRepresentation aRep = xObj->getPreferredVisualRepresentation( nViewAspect );
+ if ( pMediaType )
+ *pMediaType = aRep.Flavor.MimeType;
+
+ uno::Sequence < sal_Int8 > aSeq;
+ aRep.Data >>= aSeq;
+ xInStream = new ::comphelper::SequenceInputStream( aSeq );
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ }
+
+ return xInStream;
+}
+
+bool EmbeddedObjectContainer::SetPersistentEntries(const uno::Reference< embed::XStorage >& _xStorage,bool _bClearModifiedFlag)
+{
+ bool bError = false;
+ const uno::Sequence < OUString > aNames = GetObjectNames();
+ const OUString* pIter = aNames.getConstArray();
+ const OUString* pEnd = pIter + aNames.getLength();
+ for(;pIter != pEnd;++pIter)
+ {
+ uno::Reference < embed::XEmbeddedObject > xObj = GetEmbeddedObject( *pIter );
+ SAL_WARN_IF( !xObj.is(), "comphelper.container", "An empty entry in the embedded objects list!" );
+ if ( xObj.is() )
+ {
+ uno::Reference< embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
+ if ( xPersist.is() )
+ {
+ try
+ {
+ xPersist->setPersistentEntry( _xStorage,
+ *pIter,
+ embed::EntryInitModes::NO_INIT,
+ uno::Sequence< beans::PropertyValue >(),
+ uno::Sequence< beans::PropertyValue >() );
+
+ }
+ catch (const uno::Exception&)
+ {
+ // TODO/LATER: error handling
+ bError = true;
+ break;
+ }
+ }
+ if ( _bClearModifiedFlag )
+ {
+ // if this method is used as part of SaveCompleted the object must stay unmodified after execution
+ try
+ {
+ uno::Reference< util::XModifiable > xModif( xObj->getComponent(), uno::UNO_QUERY_THROW );
+ if ( xModif->isModified() )
+ xModif->setModified( false );
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ }
+ }
+ }
+ return bError;
+}
+
+bool EmbeddedObjectContainer::getUserAllowsLinkUpdate() const
+{
+ if (officecfg::Office::Common::Security::Scripting::DisableActiveContent::get())
+ return false;
+ return pImpl->mbUserAllowsLinkUpdate;
+}
+
+void EmbeddedObjectContainer::setUserAllowsLinkUpdate(bool bNew)
+{
+ if(pImpl->mbUserAllowsLinkUpdate != bNew)
+ {
+ pImpl->mbUserAllowsLinkUpdate = bNew;
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/container/enumerablemap.cxx b/comphelper/source/container/enumerablemap.cxx
new file mode 100644
index 0000000000..ae78223a20
--- /dev/null
+++ b/comphelper/source/container/enumerablemap.cxx
@@ -0,0 +1,713 @@
+/* -*- 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 <comphelper/anytostring.hxx>
+#include <comphelper/anycompare.hxx>
+#include <comphelper/componentbase.hxx>
+
+#include <com/sun/star/container/XEnumerableMap.hpp>
+#include <com/sun/star/lang/NoSupportException.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/ucb/AlreadyInitializedException.hpp>
+#include <com/sun/star/beans/IllegalTypeException.hpp>
+#include <com/sun/star/beans/Pair.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <typelib/typedescription.hxx>
+
+#include <cmath>
+#include <map>
+#include <memory>
+#include <optional>
+#include <utility>
+
+namespace comphelper
+{
+
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::uno::UNO_QUERY;
+ using ::com::sun::star::uno::RuntimeException;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::uno::Type;
+ using ::com::sun::star::container::XEnumerableMap;
+ using ::com::sun::star::lang::NoSupportException;
+ using ::com::sun::star::beans::IllegalTypeException;
+ using ::com::sun::star::container::NoSuchElementException;
+ using ::com::sun::star::lang::IllegalArgumentException;
+ using ::com::sun::star::lang::XInitialization;
+ using ::com::sun::star::ucb::AlreadyInitializedException;
+ using ::com::sun::star::beans::Pair;
+ using ::com::sun::star::uno::TypeClass;
+ using ::com::sun::star::uno::TypeClass_VOID;
+ using ::com::sun::star::uno::TypeClass_UNKNOWN;
+ using ::com::sun::star::uno::TypeClass_ANY;
+ using ::com::sun::star::uno::TypeClass_EXCEPTION;
+ using ::com::sun::star::uno::TypeClass_STRUCT;
+ using ::com::sun::star::uno::TypeClass_FLOAT;
+ using ::com::sun::star::uno::TypeClass_DOUBLE;
+ using ::com::sun::star::uno::TypeClass_INTERFACE;
+ using ::com::sun::star::lang::XServiceInfo;
+ using ::com::sun::star::uno::XComponentContext;
+ using ::com::sun::star::container::XEnumeration;
+ using ::com::sun::star::uno::TypeDescription;
+ using ::com::sun::star::lang::DisposedException;
+
+ namespace {
+
+ class MapEnumerator;
+
+ }
+
+ typedef std::map< Any, Any, LessPredicateAdapter > KeyedValues;
+
+ namespace {
+
+ struct MapData
+ {
+ Type m_aKeyType;
+ Type m_aValueType;
+ std::optional< KeyedValues > m_pValues;
+ std::shared_ptr< IKeyPredicateLess > m_pKeyCompare;
+ bool m_bMutable;
+ std::vector< MapEnumerator* > m_aModListeners;
+
+ MapData()
+ :m_bMutable( true )
+ {
+ }
+
+ MapData( const MapData& _source )
+ :m_aKeyType( _source.m_aKeyType )
+ ,m_aValueType( _source.m_aValueType )
+ ,m_pKeyCompare( _source.m_pKeyCompare )
+ ,m_bMutable( false )
+ {
+ m_pValues.emplace( *_source.m_pValues );
+ }
+ private:
+ MapData& operator=( const MapData& _source ) = delete;
+ };
+
+ }
+
+ static void lcl_registerMapModificationListener( MapData& _mapData, MapEnumerator& _listener )
+ {
+ #if OSL_DEBUG_LEVEL > 0
+ for ( const MapEnumerator* lookup : _mapData.m_aModListeners )
+ {
+ OSL_ENSURE( lookup != &_listener, "lcl_registerMapModificationListener: this listener is already registered!" );
+ }
+ #endif
+ _mapData.m_aModListeners.push_back( &_listener );
+ }
+
+
+ static void lcl_revokeMapModificationListener( MapData& _mapData, MapEnumerator& _listener )
+ {
+ auto lookup = std::find(_mapData.m_aModListeners.begin(), _mapData.m_aModListeners.end(), &_listener);
+ if (lookup != _mapData.m_aModListeners.end())
+ {
+ _mapData.m_aModListeners.erase( lookup );
+ return;
+ }
+ OSL_FAIL( "lcl_revokeMapModificationListener: the listener is not registered!" );
+ }
+
+
+ static void lcl_notifyMapDataListeners_nothrow( const MapData& _mapData );
+
+
+ // EnumerableMap
+
+ typedef ::cppu::WeakComponentImplHelper < XInitialization
+ , XEnumerableMap
+ , XServiceInfo
+ > Map_IFace;
+
+ namespace {
+
+ class EnumerableMap: public Map_IFace, public ComponentBase
+ {
+ public:
+ EnumerableMap();
+ protected:
+ virtual ~EnumerableMap() override;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const Sequence< Any >& aArguments ) override;
+
+ // XEnumerableMap
+ virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createKeyEnumeration( sal_Bool Isolated ) override;
+ virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createValueEnumeration( sal_Bool Isolated ) override;
+ virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createElementEnumeration( sal_Bool Isolated ) override;
+
+ // XMap
+ virtual Type SAL_CALL getKeyType() override;
+ virtual Type SAL_CALL getValueType() override;
+ virtual void SAL_CALL clear( ) override;
+ virtual sal_Bool SAL_CALL containsKey( const Any& _key ) override;
+ virtual sal_Bool SAL_CALL containsValue( const Any& _value ) override;
+ virtual Any SAL_CALL get( const Any& _key ) override;
+ virtual Any SAL_CALL put( const Any& _key, const Any& _value ) override;
+ virtual Any SAL_CALL remove( const Any& _key ) override;
+
+ // XElementAccess (base of XMap)
+ virtual Type SAL_CALL getElementType() override;
+ virtual sal_Bool SAL_CALL hasElements() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+ private:
+ void impl_initValues_throw( const Sequence< Pair< Any, Any > >& _initialValues );
+
+ /// throws an IllegalTypeException if the given value is not compatible with our ValueType
+ void impl_checkValue_throw( const Any& _value ) const;
+ void impl_checkKey_throw( const Any& _key ) const;
+ void impl_checkNaN_throw( const Any& _keyOrValue, const Type& _keyOrValueType ) const;
+ void impl_checkMutable_throw() const;
+
+ private:
+ ::osl::Mutex m_aMutex;
+ MapData m_aData;
+ };
+
+ enum EnumerationType
+ {
+ eKeys, eValues, eBoth
+ };
+
+ class MapEnumerator final
+ {
+ public:
+ MapEnumerator( ::cppu::OWeakObject& _rParent, MapData& _mapData, const EnumerationType _type )
+ :m_rParent( _rParent )
+ ,m_rMapData( _mapData )
+ ,m_eType( _type )
+ ,m_mapPos( _mapData.m_pValues->begin() )
+ ,m_disposed( false )
+ {
+ lcl_registerMapModificationListener( m_rMapData, *this );
+ }
+
+ ~MapEnumerator()
+ {
+ dispose();
+ }
+
+ void dispose()
+ {
+ if ( !m_disposed )
+ {
+ lcl_revokeMapModificationListener( m_rMapData, *this );
+ m_disposed = true;
+ }
+ }
+
+ // noncopyable
+ MapEnumerator(const MapEnumerator&) = delete;
+ const MapEnumerator& operator=(const MapEnumerator&) = delete;
+
+ // XEnumeration equivalents
+ bool hasMoreElements();
+ Any nextElement();
+
+ /// called when the map was modified
+ void mapModified();
+
+ private:
+ ::cppu::OWeakObject& m_rParent;
+ MapData& m_rMapData;
+ const EnumerationType m_eType;
+ KeyedValues::const_iterator m_mapPos;
+ bool m_disposed;
+ };
+
+ }
+
+ static void lcl_notifyMapDataListeners_nothrow( const MapData& _mapData )
+ {
+ for ( MapEnumerator* loop : _mapData.m_aModListeners )
+ {
+ loop->mapModified();
+ }
+ }
+
+ typedef ::cppu::WeakImplHelper < XEnumeration
+ > MapEnumeration_Base;
+
+ namespace {
+
+ class MapEnumeration :public ComponentBase
+ ,public MapEnumeration_Base
+ {
+ public:
+ MapEnumeration( ::cppu::OWeakObject& _parentMap, MapData& _mapData, ::cppu::OBroadcastHelper& _rBHelper,
+ const EnumerationType _type, const bool _isolated )
+ :ComponentBase( _rBHelper, ComponentBase::NoInitializationNeeded() )
+ ,m_xKeepMapAlive( _parentMap )
+ ,m_pMapDataCopy( _isolated ? new MapData( _mapData ) : nullptr )
+ ,m_aEnumerator( *this, _isolated ? *m_pMapDataCopy : _mapData, _type )
+ {
+ }
+
+ // XEnumeration
+ virtual sal_Bool SAL_CALL hasMoreElements( ) override;
+ virtual Any SAL_CALL nextElement( ) override;
+
+ protected:
+ virtual ~MapEnumeration() override
+ {
+ acquire();
+ {
+ ::osl::MutexGuard aGuard( getMutex() );
+ m_aEnumerator.dispose();
+ m_pMapDataCopy.reset();
+ }
+ }
+
+ private:
+ // since we share our mutex with the main map, we need to keep it alive as long as we live
+ Reference< XInterface > m_xKeepMapAlive;
+ std::unique_ptr< MapData > m_pMapDataCopy;
+ MapEnumerator m_aEnumerator;
+ };
+
+ }
+
+ EnumerableMap::EnumerableMap()
+ :Map_IFace( m_aMutex )
+ ,ComponentBase( Map_IFace::rBHelper )
+ {
+ }
+
+
+ EnumerableMap::~EnumerableMap()
+ {
+ if ( !impl_isDisposed() )
+ {
+ acquire();
+ dispose();
+ }
+ }
+
+
+ void SAL_CALL EnumerableMap::initialize( const Sequence< Any >& _arguments )
+ {
+ ComponentMethodGuard aGuard( *this, ComponentMethodGuard::MethodType::WithoutInit );
+ if ( impl_isInitialized_nothrow() )
+ throw AlreadyInitializedException();
+
+ sal_Int32 nArgumentCount = _arguments.getLength();
+ if ( ( nArgumentCount != 2 ) && ( nArgumentCount != 3 ) )
+ throw IllegalArgumentException("wrong number of args", static_cast<cppu::OWeakObject*>(this), 1);
+
+ Type aKeyType, aValueType;
+ if ( !( _arguments[0] >>= aKeyType ) )
+ throw IllegalArgumentException("com.sun.star.uno.Type expected.", *this, 1 );
+ if ( !( _arguments[1] >>= aValueType ) )
+ throw IllegalArgumentException("com.sun.star.uno.Type expected.", *this, 2 );
+
+ Sequence< Pair< Any, Any > > aInitialValues;
+ bool bMutable = true;
+ if ( nArgumentCount == 3 )
+ {
+ if ( !( _arguments[2] >>= aInitialValues ) )
+ throw IllegalArgumentException("[]com.sun.star.beans.Pair<any,any> expected.", *this, 2 );
+ bMutable = false;
+ }
+
+ // for the value, anything is allowed, except VOID
+ if ( ( aValueType.getTypeClass() == TypeClass_VOID ) || ( aValueType.getTypeClass() == TypeClass_UNKNOWN ) )
+ throw IllegalTypeException("Unsupported value type.", *this );
+
+ // create the comparator for the KeyType, and throw if the type is not supported
+ std::unique_ptr< IKeyPredicateLess > pComparator( getStandardLessPredicate( aKeyType, nullptr ) );
+ if (!pComparator)
+ throw IllegalTypeException("Unsupported key type.", *this );
+
+ // init members
+ m_aData.m_aKeyType = aKeyType;
+ m_aData.m_aValueType = aValueType;
+ m_aData.m_pKeyCompare = std::move(pComparator);
+ m_aData.m_pValues.emplace( *m_aData.m_pKeyCompare );
+ m_aData.m_bMutable = bMutable;
+
+ if ( aInitialValues.hasElements() )
+ impl_initValues_throw( aInitialValues );
+
+ setInitialized();
+ }
+
+
+ void EnumerableMap::impl_initValues_throw( const Sequence< Pair< Any, Any > >& _initialValues )
+ {
+ OSL_PRECOND( m_aData.m_pValues && m_aData.m_pValues->empty(), "EnumerableMap::impl_initValues_throw: illegal call!" );
+ if (!m_aData.m_pValues || !m_aData.m_pValues->empty())
+ throw RuntimeException();
+
+ const Pair< Any, Any >* mapping = _initialValues.getConstArray();
+ const Pair< Any, Any >* mappingEnd = mapping + _initialValues.getLength();
+ for ( ; mapping != mappingEnd; ++mapping )
+ {
+ impl_checkValue_throw( mapping->Second );
+ (*m_aData.m_pValues)[ mapping->First ] = mapping->Second;
+ }
+ }
+
+
+ void EnumerableMap::impl_checkValue_throw( const Any& _value ) const
+ {
+ if ( !_value.hasValue() )
+ // nothing to do, NULL values are always allowed, regardless of the ValueType
+ return;
+
+ TypeClass eAllowedTypeClass = m_aData.m_aValueType.getTypeClass();
+ bool bValid = false;
+
+ switch ( eAllowedTypeClass )
+ {
+ default:
+ bValid = ( _value.getValueTypeClass() == eAllowedTypeClass );
+ break;
+ case TypeClass_ANY:
+ bValid = true;
+ break;
+ case TypeClass_INTERFACE:
+ {
+ // special treatment: _value might contain the proper type, but the interface
+ // might actually be NULL. Which is still valid ...
+ if ( m_aData.m_aValueType.isAssignableFrom( _value.getValueType() ) )
+ // this also catches the special case where XFoo is our value type,
+ // and _value contains a NULL-reference to XFoo, or a derived type
+ bValid = true;
+ else
+ {
+ Reference< XInterface > xValue( _value, UNO_QUERY );
+ if ( xValue.is() )
+ // XInterface is not-NULL, but is X(ValueType) not-NULL, too?
+ xValue.set( xValue->queryInterface( m_aData.m_aValueType ), UNO_QUERY );
+ bValid = xValue.is();
+ }
+ }
+ break;
+ case TypeClass_EXCEPTION:
+ case TypeClass_STRUCT:
+ {
+ // values are accepted if and only if their type equals, or is derived from, our value type
+
+ if ( _value.getValueTypeClass() != eAllowedTypeClass )
+ bValid = false;
+ else
+ {
+ const TypeDescription aValueTypeDesc( _value.getValueType() );
+ const TypeDescription aRequiredTypeDesc( m_aData.m_aValueType );
+
+ const _typelib_CompoundTypeDescription* pValueCompoundTypeDesc =
+ reinterpret_cast< const _typelib_CompoundTypeDescription* >( aValueTypeDesc.get() );
+
+ while ( pValueCompoundTypeDesc )
+ {
+ if ( typelib_typedescription_equals( &pValueCompoundTypeDesc->aBase, aRequiredTypeDesc.get() ) )
+ break;
+ pValueCompoundTypeDesc = pValueCompoundTypeDesc->pBaseTypeDescription;
+ }
+ bValid = ( pValueCompoundTypeDesc != nullptr );
+ }
+ }
+ break;
+ }
+
+ if ( !bValid )
+ {
+ throw IllegalTypeException(
+ "Incompatible value type. Found '" + _value.getValueTypeName()
+ + "', where '" + m_aData.m_aValueType.getTypeName()
+ + "' (or compatible type) is expected.",
+ *const_cast< EnumerableMap* >( this ) );
+ }
+
+ impl_checkNaN_throw( _value, m_aData.m_aValueType );
+ }
+
+
+ void EnumerableMap::impl_checkNaN_throw( const Any& _keyOrValue, const Type& _keyOrValueType ) const
+ {
+ if ( ( _keyOrValueType.getTypeClass() == TypeClass_DOUBLE )
+ || ( _keyOrValueType.getTypeClass() == TypeClass_FLOAT )
+ )
+ {
+ double nValue(0);
+ if ( _keyOrValue >>= nValue )
+ if ( std::isnan( nValue ) )
+ throw IllegalArgumentException(
+ "NaN (not-a-number) not supported by this implementation.",
+ *const_cast< EnumerableMap* >( this ), 0 );
+ // (note that the case of _key not containing a float/double value is handled in the
+ // respective IKeyPredicateLess implementation, so there's no need to handle this here.)
+ }
+ }
+
+
+ void EnumerableMap::impl_checkKey_throw( const Any& _key ) const
+ {
+ if ( !_key.hasValue() )
+ throw IllegalArgumentException(
+ "NULL keys not supported by this implementation.",
+ *const_cast< EnumerableMap* >( this ), 0 );
+
+ impl_checkNaN_throw( _key, m_aData.m_aKeyType );
+ }
+
+
+ void EnumerableMap::impl_checkMutable_throw() const
+ {
+ if ( !m_aData.m_bMutable )
+ throw NoSupportException(
+ "The map is immutable.",
+ *const_cast< EnumerableMap* >( this ) );
+ }
+
+
+ Reference< XEnumeration > SAL_CALL EnumerableMap::createKeyEnumeration( sal_Bool Isolated )
+ {
+ ComponentMethodGuard aGuard( *this );
+ return new MapEnumeration( *this, m_aData, getBroadcastHelper(), eKeys, Isolated );
+ }
+
+
+ Reference< XEnumeration > SAL_CALL EnumerableMap::createValueEnumeration( sal_Bool Isolated )
+ {
+ ComponentMethodGuard aGuard( *this );
+ return new MapEnumeration( *this, m_aData, getBroadcastHelper(), eValues, Isolated );
+ }
+
+
+ Reference< XEnumeration > SAL_CALL EnumerableMap::createElementEnumeration( sal_Bool Isolated )
+ {
+ ComponentMethodGuard aGuard( *this );
+ return new MapEnumeration( *this, m_aData, getBroadcastHelper(), eBoth, Isolated );
+ }
+
+
+ Type SAL_CALL EnumerableMap::getKeyType()
+ {
+ ComponentMethodGuard aGuard( *this );
+ return m_aData.m_aKeyType;
+ }
+
+
+ Type SAL_CALL EnumerableMap::getValueType()
+ {
+ ComponentMethodGuard aGuard( *this );
+ return m_aData.m_aValueType;
+ }
+
+
+ void SAL_CALL EnumerableMap::clear( )
+ {
+ ComponentMethodGuard aGuard( *this );
+ impl_checkMutable_throw();
+
+ m_aData.m_pValues->clear();
+
+ lcl_notifyMapDataListeners_nothrow( m_aData );
+ }
+
+
+ sal_Bool SAL_CALL EnumerableMap::containsKey( const Any& _key )
+ {
+ ComponentMethodGuard aGuard( *this );
+ impl_checkKey_throw( _key );
+
+ KeyedValues::const_iterator pos = m_aData.m_pValues->find( _key );
+ return ( pos != m_aData.m_pValues->end() );
+ }
+
+
+ sal_Bool SAL_CALL EnumerableMap::containsValue( const Any& _value )
+ {
+ ComponentMethodGuard aGuard( *this );
+ impl_checkValue_throw( _value );
+ for (auto const& value : *m_aData.m_pValues)
+ {
+ if ( value.second == _value )
+ return true;
+ }
+ return false;
+ }
+
+
+ Any SAL_CALL EnumerableMap::get( const Any& _key )
+ {
+ ComponentMethodGuard aGuard( *this );
+ impl_checkKey_throw( _key );
+
+ KeyedValues::const_iterator pos = m_aData.m_pValues->find( _key );
+ if ( pos == m_aData.m_pValues->end() )
+ throw NoSuchElementException( anyToString( _key ), *this );
+
+ return pos->second;
+ }
+
+
+ Any SAL_CALL EnumerableMap::put( const Any& _key, const Any& _value )
+ {
+ ComponentMethodGuard aGuard( *this );
+ impl_checkMutable_throw();
+ impl_checkKey_throw( _key );
+ impl_checkValue_throw( _value );
+
+ Any previousValue;
+
+ KeyedValues::iterator pos = m_aData.m_pValues->find( _key );
+ if ( pos != m_aData.m_pValues->end() )
+ {
+ previousValue = pos->second;
+ pos->second = _value;
+ }
+ else
+ {
+ (*m_aData.m_pValues)[ _key ] = _value;
+ }
+
+ lcl_notifyMapDataListeners_nothrow( m_aData );
+
+ return previousValue;
+ }
+
+
+ Any SAL_CALL EnumerableMap::remove( const Any& _key )
+ {
+ ComponentMethodGuard aGuard( *this );
+ impl_checkMutable_throw();
+ impl_checkKey_throw( _key );
+
+ Any previousValue;
+
+ KeyedValues::iterator pos = m_aData.m_pValues->find( _key );
+ if ( pos != m_aData.m_pValues->end() )
+ {
+ previousValue = pos->second;
+ m_aData.m_pValues->erase( pos );
+ }
+
+ lcl_notifyMapDataListeners_nothrow( m_aData );
+
+ return previousValue;
+ }
+
+
+ Type SAL_CALL EnumerableMap::getElementType()
+ {
+ return ::cppu::UnoType< Pair< Any, Any > >::get();
+ }
+
+
+ sal_Bool SAL_CALL EnumerableMap::hasElements()
+ {
+ ComponentMethodGuard aGuard( *this );
+ return m_aData.m_pValues->empty();
+ }
+
+
+ OUString SAL_CALL EnumerableMap::getImplementationName( )
+ {
+ return "org.openoffice.comp.comphelper.EnumerableMap";
+ }
+
+ sal_Bool SAL_CALL EnumerableMap::supportsService( const OUString& _serviceName )
+ {
+ return cppu::supportsService(this, _serviceName);
+ }
+
+
+ Sequence< OUString > SAL_CALL EnumerableMap::getSupportedServiceNames( )
+ {
+ return { "com.sun.star.container.EnumerableMap" };
+ }
+
+ bool MapEnumerator::hasMoreElements()
+ {
+ if ( m_disposed )
+ throw DisposedException( OUString(), m_rParent );
+ return m_mapPos != m_rMapData.m_pValues->end();
+ }
+
+
+ Any MapEnumerator::nextElement()
+ {
+ if ( m_disposed )
+ throw DisposedException( OUString(), m_rParent );
+ if ( m_mapPos == m_rMapData.m_pValues->end() )
+ throw NoSuchElementException("No more elements.", m_rParent );
+
+ Any aNextElement;
+ switch ( m_eType )
+ {
+ case eKeys: aNextElement = m_mapPos->first; break;
+ case eValues: aNextElement = m_mapPos->second; break;
+ case eBoth: aNextElement <<= Pair< Any, Any >( m_mapPos->first, m_mapPos->second ); break;
+ }
+ ++m_mapPos;
+ return aNextElement;
+ }
+
+
+ void MapEnumerator::mapModified()
+ {
+ m_disposed = true;
+ }
+
+
+ sal_Bool SAL_CALL MapEnumeration::hasMoreElements( )
+ {
+ ComponentMethodGuard aGuard( *this );
+ return m_aEnumerator.hasMoreElements();
+ }
+
+
+ Any SAL_CALL MapEnumeration::nextElement( )
+ {
+ ComponentMethodGuard aGuard( *this );
+ return m_aEnumerator.nextElement();
+ }
+
+
+} // namespace comphelper
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+org_openoffice_comp_comphelper_EnumerableMap(
+ css::uno::XComponentContext*, css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new comphelper::EnumerableMap());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/container/enumhelper.cxx b/comphelper/source/container/enumhelper.cxx
new file mode 100644
index 0000000000..66ba151982
--- /dev/null
+++ b/comphelper/source/container/enumhelper.cxx
@@ -0,0 +1,280 @@
+/* -*- 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 <comphelper/enumhelper.hxx>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <utility>
+
+namespace comphelper
+{
+
+OEnumerationByName::OEnumerationByName(css::uno::Reference<css::container::XNameAccess> _xAccess)
+ :m_aNames(_xAccess->getElementNames())
+ ,m_xAccess(_xAccess)
+ ,m_nPos(0)
+ ,m_bListening(false)
+{
+ impl_startDisposeListening();
+}
+
+
+OEnumerationByName::OEnumerationByName(css::uno::Reference<css::container::XNameAccess> _xAccess,
+ std::vector<OUString> _aNames )
+ :m_aNames(std::move(_aNames))
+ ,m_xAccess(std::move(_xAccess))
+ ,m_nPos(0)
+ ,m_bListening(false)
+{
+ impl_startDisposeListening();
+}
+
+OEnumerationByName::~OEnumerationByName()
+{
+ std::lock_guard aLock(m_aLock);
+
+ impl_stopDisposeListening();
+}
+
+
+sal_Bool SAL_CALL OEnumerationByName::hasMoreElements( )
+{
+ std::lock_guard aLock(m_aLock);
+
+ if (m_xAccess.is() && getLength() > m_nPos)
+ return true;
+
+ if (m_xAccess.is())
+ {
+ impl_stopDisposeListening();
+ m_xAccess.clear();
+ }
+
+ return false;
+}
+
+
+css::uno::Any SAL_CALL OEnumerationByName::nextElement( )
+{
+ std::lock_guard aLock(m_aLock);
+
+ css::uno::Any aRes;
+ if (m_xAccess.is() && m_nPos < getLength())
+ aRes = m_xAccess->getByName(getElement(m_nPos++));
+
+ if (m_xAccess.is() && m_nPos >= getLength())
+ {
+ impl_stopDisposeListening();
+ m_xAccess.clear();
+ }
+
+ if (!aRes.hasValue()) //There are no more elements
+ throw css::container::NoSuchElementException();
+
+ return aRes;
+}
+
+void SAL_CALL OEnumerationByName::disposing(const css::lang::EventObject& aEvent)
+{
+ std::lock_guard aLock(m_aLock);
+
+ if (aEvent.Source == m_xAccess)
+ m_xAccess.clear();
+}
+
+
+void OEnumerationByName::impl_startDisposeListening()
+{
+ if (m_bListening)
+ return;
+
+ osl_atomic_increment(&m_refCount);
+ css::uno::Reference< css::lang::XComponent > xDisposable(m_xAccess, css::uno::UNO_QUERY);
+ if (xDisposable.is())
+ {
+ xDisposable->addEventListener(this);
+ m_bListening = true;
+ }
+ osl_atomic_decrement(&m_refCount);
+}
+
+
+void OEnumerationByName::impl_stopDisposeListening()
+{
+ if (!m_bListening)
+ return;
+
+ osl_atomic_increment(&m_refCount);
+ css::uno::Reference< css::lang::XComponent > xDisposable(m_xAccess, css::uno::UNO_QUERY);
+ if (xDisposable.is())
+ {
+ xDisposable->removeEventListener(this);
+ m_bListening = false;
+ }
+ osl_atomic_decrement(&m_refCount);
+}
+
+sal_Int32 OEnumerationByName::getLength() const
+{
+ if (m_aNames.index() == 0)
+ return std::get<css::uno::Sequence<OUString>>(m_aNames).getLength();
+ else
+ return std::get<std::vector<OUString>>(m_aNames).size();
+}
+
+const OUString& OEnumerationByName::getElement(sal_Int32 nIndex) const
+{
+ if (m_aNames.index() == 0)
+ return std::get<css::uno::Sequence<OUString>>(m_aNames).getConstArray()[nIndex];
+ else
+ return std::get<std::vector<OUString>>(m_aNames)[nIndex];
+}
+
+
+OEnumerationByIndex::OEnumerationByIndex(css::uno::Reference< css::container::XIndexAccess > _xAccess)
+ :m_xAccess(std::move(_xAccess))
+ ,m_nPos(0)
+ ,m_bListening(false)
+{
+ impl_startDisposeListening();
+}
+
+
+OEnumerationByIndex::~OEnumerationByIndex()
+{
+ std::lock_guard aLock(m_aLock);
+
+ impl_stopDisposeListening();
+}
+
+
+sal_Bool SAL_CALL OEnumerationByIndex::hasMoreElements( )
+{
+ std::lock_guard aLock(m_aLock);
+
+ if (m_xAccess.is() && m_xAccess->getCount() > m_nPos)
+ return true;
+
+ if (m_xAccess.is())
+ {
+ impl_stopDisposeListening();
+ m_xAccess.clear();
+ }
+
+ return false;
+}
+
+
+css::uno::Any SAL_CALL OEnumerationByIndex::nextElement( )
+{
+ std::lock_guard aLock(m_aLock);
+
+ css::uno::Any aRes;
+ if (m_xAccess.is() && m_nPos < m_xAccess->getCount())
+ aRes = m_xAccess->getByIndex(m_nPos++);
+
+ if (m_xAccess.is() && m_nPos >= m_xAccess->getCount())
+ {
+ impl_stopDisposeListening();
+ m_xAccess.clear();
+ }
+
+ if (!aRes.hasValue())
+ throw css::container::NoSuchElementException();
+ return aRes;
+}
+
+
+void SAL_CALL OEnumerationByIndex::disposing(const css::lang::EventObject& aEvent)
+{
+ std::lock_guard aLock(m_aLock);
+
+ if (aEvent.Source == m_xAccess)
+ m_xAccess.clear();
+}
+
+
+void OEnumerationByIndex::impl_startDisposeListening()
+{
+ if (m_bListening)
+ return;
+
+ osl_atomic_increment(&m_refCount);
+ css::uno::Reference< css::lang::XComponent > xDisposable(m_xAccess, css::uno::UNO_QUERY);
+ if (xDisposable.is())
+ {
+ xDisposable->addEventListener(this);
+ m_bListening = true;
+ }
+ osl_atomic_decrement(&m_refCount);
+}
+
+
+void OEnumerationByIndex::impl_stopDisposeListening()
+{
+ if (!m_bListening)
+ return;
+
+ osl_atomic_increment(&m_refCount);
+ css::uno::Reference< css::lang::XComponent > xDisposable(m_xAccess, css::uno::UNO_QUERY);
+ if (xDisposable.is())
+ {
+ xDisposable->removeEventListener(this);
+ m_bListening = false;
+ }
+ osl_atomic_decrement(&m_refCount);
+}
+
+OAnyEnumeration::OAnyEnumeration(const css::uno::Sequence< css::uno::Any >& lItems)
+ :m_nPos(0)
+ ,m_lItems(lItems)
+{
+}
+
+
+OAnyEnumeration::~OAnyEnumeration()
+{
+}
+
+
+sal_Bool SAL_CALL OAnyEnumeration::hasMoreElements( )
+{
+ std::lock_guard aLock(m_aLock);
+
+ return (m_lItems.getLength() > m_nPos);
+}
+
+
+css::uno::Any SAL_CALL OAnyEnumeration::nextElement( )
+{
+ if ( ! hasMoreElements())
+ throw css::container::NoSuchElementException();
+
+ std::lock_guard aLock(m_aLock);
+ sal_Int32 nPos = m_nPos;
+ ++m_nPos;
+ return m_lItems[nPos];
+}
+
+
+} // namespace comphelper
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/container/interfacecontainer2.cxx b/comphelper/source/container/interfacecontainer2.cxx
new file mode 100644
index 0000000000..df1c9e0e69
--- /dev/null
+++ b/comphelper/source/container/interfacecontainer2.cxx
@@ -0,0 +1,424 @@
+/* -*- 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 <comphelper/interfacecontainer2.hxx>
+#include <comphelper/multicontainer2.hxx>
+
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <osl/mutex.hxx>
+
+#include <memory>
+
+#include <com/sun/star/lang/XEventListener.hpp>
+
+
+using namespace osl;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+
+namespace comphelper
+{
+
+OInterfaceIteratorHelper2::OInterfaceIteratorHelper2( OInterfaceContainerHelper2 & rCont_ )
+ : rCont( rCont_ )
+{
+ MutexGuard aGuard( rCont.rMutex );
+ if( rCont.bInUse )
+ // worst case, two iterators at the same time
+ rCont.copyAndResetInUse();
+ bIsList = rCont_.bIsList;
+ aData = rCont_.aData;
+ if( bIsList )
+ {
+ rCont.bInUse = true;
+ nRemain = aData.pAsVector->size();
+ }
+ else if( aData.pAsInterface )
+ {
+ aData.pAsInterface->acquire();
+ nRemain = 1;
+ }
+ else
+ nRemain = 0;
+}
+
+OInterfaceIteratorHelper2::~OInterfaceIteratorHelper2()
+{
+ bool bShared;
+ {
+ MutexGuard aGuard( rCont.rMutex );
+ // bResetInUse protect the iterator against recursion
+ bShared = aData.pAsVector == rCont.aData.pAsVector && rCont.bIsList;
+ if( bShared )
+ {
+ OSL_ENSURE( rCont.bInUse, "OInterfaceContainerHelper2 must be in use" );
+ rCont.bInUse = false;
+ }
+ }
+
+ if( !bShared )
+ {
+ if( bIsList )
+ // Sequence owned by the iterator
+ delete aData.pAsVector;
+ else if( aData.pAsInterface )
+ // Interface is acquired by the iterator
+ aData.pAsInterface->release();
+ }
+}
+
+XInterface * OInterfaceIteratorHelper2::next()
+{
+ if( nRemain )
+ {
+ nRemain--;
+ if( bIsList )
+ return (*aData.pAsVector)[nRemain].get();
+ else if( aData.pAsInterface )
+ return aData.pAsInterface;
+ }
+ // exception
+ return nullptr;
+}
+
+void OInterfaceIteratorHelper2::remove()
+{
+ if( bIsList )
+ {
+ OSL_ASSERT( nRemain >= 0 &&
+ o3tl::make_unsigned(nRemain) < aData.pAsVector->size() );
+ rCont.removeInterface( (*aData.pAsVector)[nRemain] );
+ }
+ else
+ {
+ OSL_ASSERT( 0 == nRemain );
+ rCont.removeInterface( aData.pAsInterface );
+ }
+}
+
+OInterfaceContainerHelper2::OInterfaceContainerHelper2( Mutex & rMutex_ )
+ : rMutex( rMutex_ )
+ , bInUse( false )
+ , bIsList( false )
+{
+}
+
+OInterfaceContainerHelper2::~OInterfaceContainerHelper2()
+{
+ OSL_ENSURE( !bInUse, "~OInterfaceContainerHelper2 but is in use" );
+ if( bIsList )
+ delete aData.pAsVector;
+ else if( aData.pAsInterface )
+ aData.pAsInterface->release();
+}
+
+sal_Int32 OInterfaceContainerHelper2::getLength() const
+{
+ MutexGuard aGuard( rMutex );
+ if( bIsList )
+ return aData.pAsVector->size();
+ else if( aData.pAsInterface )
+ return 1;
+ return 0;
+}
+
+std::vector< Reference<XInterface> > OInterfaceContainerHelper2::getElements() const
+{
+ std::vector< Reference<XInterface> > rVec;
+ MutexGuard aGuard( rMutex );
+ if( bIsList )
+ rVec = *aData.pAsVector;
+ else if( aData.pAsInterface )
+ {
+ rVec.emplace_back( aData.pAsInterface );
+ }
+ return rVec;
+}
+
+void OInterfaceContainerHelper2::copyAndResetInUse()
+{
+ OSL_ENSURE( bInUse, "OInterfaceContainerHelper2 not in use" );
+ if( bInUse )
+ {
+ // this should be the worst case. If an iterator is active
+ // and a new Listener is added.
+ if( bIsList )
+ aData.pAsVector = new std::vector< Reference< XInterface > >( *aData.pAsVector );
+ else if( aData.pAsInterface )
+ aData.pAsInterface->acquire();
+
+ bInUse = false;
+ }
+}
+
+sal_Int32 OInterfaceContainerHelper2::addInterface( const Reference<XInterface> & rListener )
+{
+ OSL_ASSERT( rListener.is() );
+ if ( !rListener.is() )
+ return 0;
+
+ MutexGuard aGuard( rMutex );
+ if( bInUse )
+ copyAndResetInUse();
+
+ if( bIsList )
+ {
+ aData.pAsVector->push_back( rListener );
+ return aData.pAsVector->size();
+ }
+ else if( aData.pAsInterface )
+ {
+ std::vector< Reference< XInterface > > * pVec = new std::vector< Reference< XInterface > >( 2 );
+ (*pVec)[0] = aData.pAsInterface;
+ (*pVec)[1] = rListener;
+ aData.pAsInterface->release();
+ aData.pAsVector = pVec;
+ bIsList = true;
+ return 2;
+ }
+ else
+ {
+ aData.pAsInterface = rListener.get();
+ if( rListener.is() )
+ rListener->acquire();
+ return 1;
+ }
+}
+
+sal_Int32 OInterfaceContainerHelper2::removeInterface( const Reference<XInterface> & rListener )
+{
+ OSL_ASSERT( rListener.is() );
+ MutexGuard aGuard( rMutex );
+ if( bInUse )
+ copyAndResetInUse();
+
+ if( bIsList )
+ {
+ // It is not valid to compare the pointer directly, but it's faster.
+ auto it = std::find_if(aData.pAsVector->begin(), aData.pAsVector->end(),
+ [&rListener](const css::uno::Reference<css::uno::XInterface>& rItem) {
+ return rItem.get() == rListener.get(); });
+
+ // interface not found, use the correct compare method
+ if (it == aData.pAsVector->end())
+ it = std::find(aData.pAsVector->begin(), aData.pAsVector->end(), rListener);
+
+ if (it != aData.pAsVector->end())
+ aData.pAsVector->erase(it);
+
+ if( aData.pAsVector->size() == 1 )
+ {
+ XInterface * p = (*aData.pAsVector)[0].get();
+ p->acquire();
+ delete aData.pAsVector;
+ aData.pAsInterface = p;
+ bIsList = false;
+ return 1;
+ }
+ else
+ return aData.pAsVector->size();
+ }
+ else if( aData.pAsInterface && Reference<XInterface>( aData.pAsInterface ) == rListener )
+ {
+ aData.pAsInterface->release();
+ aData.pAsInterface = nullptr;
+ }
+ return aData.pAsInterface ? 1 : 0;
+}
+
+Reference<XInterface> OInterfaceContainerHelper2::getInterface( sal_Int32 nIndex ) const
+{
+ MutexGuard aGuard( rMutex );
+
+ if( bIsList )
+ return (*aData.pAsVector)[nIndex];
+ else if( aData.pAsInterface )
+ {
+ if (nIndex == 0)
+ return aData.pAsInterface;
+ }
+ throw std::out_of_range("index out of range");
+}
+
+void OInterfaceContainerHelper2::disposeAndClear( const EventObject & rEvt )
+{
+ ClearableMutexGuard aGuard( rMutex );
+ OInterfaceIteratorHelper2 aIt( *this );
+ // Release container, in case new entries come while disposing
+ OSL_ENSURE( !bIsList || bInUse, "OInterfaceContainerHelper2 not in use" );
+ if( !bIsList && aData.pAsInterface )
+ aData.pAsInterface->release();
+ // set the member to null, use the iterator to delete the values
+ aData.pAsInterface = nullptr;
+ bIsList = false;
+ bInUse = false;
+ aGuard.clear();
+ while( aIt.hasMoreElements() )
+ {
+ try
+ {
+ Reference<XEventListener > xLst( aIt.next(), UNO_QUERY );
+ if( xLst.is() )
+ xLst->disposing( rEvt );
+ }
+ catch ( RuntimeException & )
+ {
+ // be robust, if e.g. a remote bridge has disposed already.
+ // there is no way to delegate the error to the caller :o(.
+ }
+ }
+}
+
+
+void OInterfaceContainerHelper2::clear()
+{
+ MutexGuard aGuard( rMutex );
+ // Release container, in case new entries come while disposing
+ OSL_ENSURE( !bIsList || bInUse, "OInterfaceContainerHelper2 not in use" );
+ if (bInUse)
+ copyAndResetInUse();
+ if (bIsList)
+ delete aData.pAsVector;
+ else if (aData.pAsInterface)
+ aData.pAsInterface->release();
+ aData.pAsInterface = nullptr;
+ bIsList = false;
+}
+
+
+
+// specialized class for type
+
+OMultiTypeInterfaceContainerHelper2::OMultiTypeInterfaceContainerHelper2( Mutex & rMutex_ )
+ : rMutex( rMutex_ )
+{
+}
+
+OMultiTypeInterfaceContainerHelper2::~OMultiTypeInterfaceContainerHelper2()
+{
+}
+
+std::vector< css::uno::Type > OMultiTypeInterfaceContainerHelper2::getContainedTypes() const
+{
+ ::osl::MutexGuard aGuard( rMutex );
+ std::vector< Type > aInterfaceTypes;
+ aInterfaceTypes.reserve( m_aMap.size() );
+ for (const auto& rItem : m_aMap)
+ {
+ // are interfaces added to this container?
+ if( rItem.second->getLength() )
+ // yes, put the type in the array
+ aInterfaceTypes.push_back(rItem.first);
+ }
+ return aInterfaceTypes;
+}
+
+OMultiTypeInterfaceContainerHelper2::t_type2ptr::iterator OMultiTypeInterfaceContainerHelper2::findType(const Type & rKey )
+{
+ return std::find_if(m_aMap.begin(), m_aMap.end(),
+ [&rKey](const t_type2ptr::value_type& rItem) { return rItem.first == rKey; });
+}
+
+OMultiTypeInterfaceContainerHelper2::t_type2ptr::const_iterator OMultiTypeInterfaceContainerHelper2::findType(const Type & rKey ) const
+{
+ return std::find_if(m_aMap.begin(), m_aMap.end(),
+ [&rKey](const t_type2ptr::value_type& rItem) { return rItem.first == rKey; });
+}
+
+OInterfaceContainerHelper2 * OMultiTypeInterfaceContainerHelper2::getContainer( const Type & rKey ) const
+{
+ ::osl::MutexGuard aGuard( rMutex );
+
+ auto iter = findType( rKey );
+ if( iter != m_aMap.end() )
+ return (*iter).second.get();
+ return nullptr;
+}
+
+sal_Int32 OMultiTypeInterfaceContainerHelper2::addInterface(
+ const Type & rKey, const Reference< XInterface > & rListener )
+{
+ ::osl::MutexGuard aGuard( rMutex );
+ auto iter = findType( rKey );
+ if( iter == m_aMap.end() )
+ {
+ OInterfaceContainerHelper2 * pLC = new OInterfaceContainerHelper2( rMutex );
+ m_aMap.emplace_back(rKey, pLC);
+ return pLC->addInterface( rListener );
+ }
+ return (*iter).second->addInterface( rListener );
+}
+
+sal_Int32 OMultiTypeInterfaceContainerHelper2::removeInterface(
+ const Type & rKey, const Reference< XInterface > & rListener )
+{
+ ::osl::MutexGuard aGuard( rMutex );
+
+ // search container with id nUik
+ auto iter = findType( rKey );
+ // container found?
+ if( iter != m_aMap.end() )
+ return (*iter).second->removeInterface( rListener );
+
+ // no container with this id. Always return 0
+ return 0;
+}
+
+void OMultiTypeInterfaceContainerHelper2::disposeAndClear( const EventObject & rEvt )
+{
+ t_type2ptr::size_type nSize = 0;
+ std::unique_ptr<OInterfaceContainerHelper2 *[]> ppListenerContainers;
+ {
+ ::osl::MutexGuard aGuard( rMutex );
+ nSize = m_aMap.size();
+ if( nSize )
+ {
+ typedef OInterfaceContainerHelper2* ppp;
+ ppListenerContainers.reset(new ppp[nSize]);
+
+ t_type2ptr::size_type i = 0;
+ for (const auto& rItem : m_aMap)
+ {
+ ppListenerContainers[i++] = rItem.second.get();
+ }
+ }
+ }
+
+ // create a copy, because do not fire event in a guarded section
+ for( t_type2ptr::size_type i = 0; i < nSize; i++ )
+ {
+ if( ppListenerContainers[i] )
+ ppListenerContainers[i]->disposeAndClear( rEvt );
+ }
+}
+
+void OMultiTypeInterfaceContainerHelper2::clear()
+{
+ ::osl::MutexGuard aGuard( rMutex );
+
+ for (auto& rItem : m_aMap)
+ rItem.second->clear();
+}
+
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/container/namecontainer.cxx b/comphelper/source/container/namecontainer.cxx
new file mode 100644
index 0000000000..c13ee7486e
--- /dev/null
+++ b/comphelper/source/container/namecontainer.cxx
@@ -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 .
+ */
+
+#include <sal/config.h>
+
+#include <map>
+#include <mutex>
+
+#include <comphelper/namecontainer.hxx>
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <osl/mutex.hxx>
+#include <com/sun/star/container/XNameContainer.hpp>
+
+typedef std::map<OUString, css::uno::Any> SvGenericNameContainerMapImpl;
+
+namespace comphelper
+{
+ namespace {
+
+ /** this is the base helper class for NameContainer that's also declared in this header. */
+ class NameContainer : public ::cppu::WeakImplHelper< css::container::XNameContainer >
+ {
+ public:
+ explicit NameContainer( const css::uno::Type& aType );
+
+ // XNameContainer
+ virtual void SAL_CALL insertByName( const OUString& aName, const css::uno::Any& aElement ) override;
+ virtual void SAL_CALL removeByName( const OUString& Name ) override;
+
+ // XNameReplace
+ virtual void SAL_CALL replaceByName( const OUString& aName, const css::uno::Any& aElement ) override;
+
+ // XNameAccess
+ virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getElementNames( ) override;
+ virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override;
+
+ // XElementAccess
+ virtual sal_Bool SAL_CALL hasElements( ) override;
+ virtual css::uno::Type SAL_CALL getElementType( ) override;
+
+ private:
+ SvGenericNameContainerMapImpl maProperties;
+ const css::uno::Type maType;
+ std::mutex maMutex;
+ };
+
+ }
+}
+
+using namespace ::comphelper;
+using namespace ::osl;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::lang;
+
+
+NameContainer::NameContainer( const css::uno::Type& aType )
+: maType( aType )
+{
+}
+
+// XNameContainer
+void SAL_CALL NameContainer::insertByName( const OUString& aName, const Any& aElement )
+{
+ std::scoped_lock aGuard( maMutex );
+
+ if( maProperties.find( aName ) != maProperties.end() )
+ throw ElementExistException();
+
+ if( aElement.getValueType() != maType )
+ throw IllegalArgumentException("element is wrong type", static_cast<cppu::OWeakObject*>(this), 2);
+
+ maProperties.emplace(aName,aElement);
+}
+
+void SAL_CALL NameContainer::removeByName( const OUString& Name )
+{
+ std::scoped_lock aGuard( maMutex );
+
+ SvGenericNameContainerMapImpl::iterator aIter = maProperties.find( Name );
+ if( aIter == maProperties.end() )
+ throw NoSuchElementException();
+
+ maProperties.erase( aIter );
+}
+
+// XNameReplace
+
+void SAL_CALL NameContainer::replaceByName( const OUString& aName, const Any& aElement )
+{
+ std::scoped_lock aGuard( maMutex );
+
+ SvGenericNameContainerMapImpl::iterator aIter( maProperties.find( aName ) );
+ if( aIter == maProperties.end() )
+ throw NoSuchElementException();
+
+ if( aElement.getValueType() != maType )
+ throw IllegalArgumentException("element is wrong type", static_cast<cppu::OWeakObject*>(this), 2);
+
+ (*aIter).second = aElement;
+}
+
+// XNameAccess
+
+Any SAL_CALL NameContainer::getByName( const OUString& aName )
+{
+ std::scoped_lock aGuard( maMutex );
+
+ SvGenericNameContainerMapImpl::iterator aIter = maProperties.find( aName );
+ if( aIter == maProperties.end() )
+ throw NoSuchElementException();
+
+ return (*aIter).second;
+}
+
+Sequence< OUString > SAL_CALL NameContainer::getElementNames( )
+{
+ std::scoped_lock aGuard( maMutex );
+
+ return comphelper::mapKeysToSequence(maProperties);
+}
+
+sal_Bool SAL_CALL NameContainer::hasByName( const OUString& aName )
+{
+ std::scoped_lock aGuard( maMutex );
+
+ SvGenericNameContainerMapImpl::iterator aIter = maProperties.find( aName );
+ return aIter != maProperties.end();
+}
+
+sal_Bool SAL_CALL NameContainer::hasElements( )
+{
+ std::scoped_lock aGuard( maMutex );
+
+ return !maProperties.empty();
+}
+
+Type SAL_CALL NameContainer::getElementType()
+{
+ return maType;
+}
+
+Reference< XNameContainer > comphelper::NameContainer_createInstance( const Type& aType )
+{
+ return new NameContainer(aType);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/eventattachermgr/eventattachermgr.cxx b/comphelper/source/eventattachermgr/eventattachermgr.cxx
new file mode 100644
index 0000000000..50085c5ce6
--- /dev/null
+++ b/comphelper/source/eventattachermgr/eventattachermgr.cxx
@@ -0,0 +1,779 @@
+/* -*- 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 <o3tl/any.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <comphelper/eventattachermgr.hxx>
+#include <comphelper/sequence.hxx>
+#include <com/sun/star/beans/theIntrospection.hpp>
+#include <com/sun/star/io/XObjectInputStream.hpp>
+#include <com/sun/star/io/XPersistObject.hpp>
+#include <com/sun/star/io/XObjectOutputStream.hpp>
+#include <com/sun/star/io/XMarkableStream.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/reflection/theCoreReflection.hpp>
+#include <com/sun/star/reflection/XIdlClass.hpp>
+#include <com/sun/star/reflection/XIdlReflection.hpp>
+#include <com/sun/star/reflection/XIdlMethod.hpp>
+#include <com/sun/star/script/CannotConvertException.hpp>
+#include <com/sun/star/script/Converter.hpp>
+#include <com/sun/star/script/XEventAttacher2.hpp>
+#include <com/sun/star/script/XEventAttacherManager.hpp>
+#include <com/sun/star/script/XScriptListener.hpp>
+#include <cppuhelper/weak.hxx>
+#include <comphelper/interfacecontainer4.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <rtl/ref.hxx>
+
+#include <deque>
+#include <mutex>
+#include <algorithm>
+#include <utility>
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::io;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::script;
+using namespace com::sun::star::reflection;
+using namespace cppu;
+using namespace osl;
+
+
+namespace comphelper
+{
+
+namespace {
+
+struct AttachedObject_Impl
+{
+ Reference< XInterface > xTarget;
+ std::vector< Reference< XEventListener > > aAttachedListenerSeq;
+ Any aHelper;
+};
+
+struct AttacherIndex_Impl
+{
+ std::deque< ScriptEventDescriptor > aEventList;
+ std::deque< AttachedObject_Impl > aObjList;
+};
+
+
+class ImplEventAttacherManager
+ : public WeakImplHelper< XEventAttacherManager, XPersistObject >
+{
+ friend class AttacherAllListener_Impl;
+ std::deque< AttacherIndex_Impl > aIndex;
+ std::mutex m_aMutex;
+ // Container for the ScriptListener
+ OInterfaceContainerHelper4<XScriptListener> aScriptListeners;
+ // Instance of EventAttacher
+ Reference< XEventAttacher2 > xAttacher;
+ Reference< XComponentContext > mxContext;
+ Reference< XIdlReflection > mxCoreReflection;
+ Reference< XTypeConverter > xConverter;
+ sal_Int16 nVersion;
+public:
+ ImplEventAttacherManager( const Reference< XIntrospection > & rIntrospection,
+ const Reference< XComponentContext >& rContext );
+
+ // Methods of XEventAttacherManager
+ virtual void SAL_CALL registerScriptEvent(sal_Int32 Index, const ScriptEventDescriptor& ScriptEvent) override;
+ virtual void SAL_CALL registerScriptEvents(sal_Int32 Index, const Sequence< ScriptEventDescriptor >& ScriptEvents) override;
+ virtual void SAL_CALL revokeScriptEvent(sal_Int32 Index, const OUString& ListenerType, const OUString& EventMethod, const OUString& removeListenerParam) override;
+ virtual void SAL_CALL revokeScriptEvents(sal_Int32 Index) override;
+ virtual void SAL_CALL insertEntry(sal_Int32 Index) override;
+ virtual void SAL_CALL removeEntry(sal_Int32 Index) override;
+ virtual Sequence< ScriptEventDescriptor > SAL_CALL getScriptEvents(sal_Int32 Index) override;
+ virtual void SAL_CALL attach(sal_Int32 Index, const Reference< XInterface >& Object, const Any& Helper) override;
+ virtual void SAL_CALL detach(sal_Int32 nIndex, const Reference< XInterface >& xObject) override;
+ virtual void SAL_CALL addScriptListener(const Reference< XScriptListener >& aListener) override;
+ virtual void SAL_CALL removeScriptListener(const Reference< XScriptListener >& Listener) override;
+
+ // Methods of XPersistObject
+ virtual OUString SAL_CALL getServiceName() override;
+ virtual void SAL_CALL write(const Reference< XObjectOutputStream >& OutStream) override;
+ virtual void SAL_CALL read(const Reference< XObjectInputStream >& InStream) override;
+
+private:
+ void registerScriptEvent(std::unique_lock<std::mutex>&, sal_Int32 Index, const ScriptEventDescriptor& ScriptEvent);
+ void registerScriptEvents(std::unique_lock<std::mutex>&, sal_Int32 Index, const Sequence< ScriptEventDescriptor >& ScriptEvents);
+ void attach(std::unique_lock<std::mutex>&, sal_Int32 Index, const Reference< XInterface >& Object, const Any& Helper);
+ void detach(std::unique_lock<std::mutex>&, sal_Int32 nIndex, const Reference< XInterface >& xObject);
+ void insertEntry(std::unique_lock<std::mutex>&, sal_Int32 Index);
+
+ /// @throws Exception
+ Reference< XIdlReflection > getReflection(std::unique_lock<std::mutex>&);
+
+ /** checks if <arg>_nIndex</arg> is a valid index, throws an <type>IllegalArgumentException</type> if not
+ @param _nIndex
+ the index to check
+ @return
+ the iterator pointing to the position indicated by the index
+ */
+ std::deque<AttacherIndex_Impl>::iterator implCheckIndex( sal_Int32 _nIndex );
+};
+
+
+// Implementation of an EventAttacher-subclass 'AllListeners', which
+// only passes individual events of the general AllListeners.
+class AttacherAllListener_Impl : public WeakImplHelper< XAllListener >
+{
+ rtl::Reference<ImplEventAttacherManager> mxManager;
+ OUString const aScriptType;
+ OUString const aScriptCode;
+
+ /// @throws CannotConvertException
+ void convertToEventReturn( Any & rRet, const Type & rRetType );
+public:
+ AttacherAllListener_Impl( ImplEventAttacherManager* pManager_, OUString aScriptType_,
+ OUString aScriptCode_ );
+
+ // Methods of XAllListener
+ virtual void SAL_CALL firing(const AllEventObject& Event) override;
+ virtual Any SAL_CALL approveFiring(const AllEventObject& Event) override;
+
+ // Methods of XEventListener
+ virtual void SAL_CALL disposing(const EventObject& Source) override;
+};
+
+}
+
+AttacherAllListener_Impl::AttacherAllListener_Impl
+(
+ ImplEventAttacherManager* pManager_,
+ OUString aScriptType_,
+ OUString aScriptCode_
+)
+ : mxManager( pManager_ )
+ , aScriptType(std::move( aScriptType_ ))
+ , aScriptCode(std::move( aScriptCode_ ))
+{
+}
+
+
+// Methods of XAllListener
+void SAL_CALL AttacherAllListener_Impl::firing(const AllEventObject& Event)
+{
+ ScriptEvent aScriptEvent;
+ aScriptEvent.Source = static_cast<OWeakObject *>(mxManager.get()); // get correct XInterface
+ aScriptEvent.ListenerType = Event.ListenerType;
+ aScriptEvent.MethodName = Event.MethodName;
+ aScriptEvent.Arguments = Event.Arguments;
+ aScriptEvent.Helper = Event.Helper;
+ aScriptEvent.ScriptType = aScriptType;
+ aScriptEvent.ScriptCode = aScriptCode;
+
+ // Iterate over all listeners and pass events.
+ std::unique_lock l(mxManager->m_aMutex);
+ mxManager->aScriptListeners.notifyEach( l, &XScriptListener::firing, aScriptEvent );
+}
+
+
+// Convert to the standard event return
+void AttacherAllListener_Impl::convertToEventReturn( Any & rRet, const Type & rRetType )
+{
+ // no return value? Set to the specified values
+ if( rRet.getValueType().getTypeClass() == TypeClass_VOID )
+ {
+ switch( rRetType.getTypeClass() )
+ {
+ case TypeClass_INTERFACE:
+ {
+ rRet <<= Reference< XInterface >();
+ }
+ break;
+
+ case TypeClass_BOOLEAN:
+ rRet <<= true;
+ break;
+
+ case TypeClass_STRING:
+ rRet <<= OUString();
+ break;
+
+ case TypeClass_FLOAT: rRet <<= float(0); break;
+ case TypeClass_DOUBLE: rRet <<= 0.0; break;
+ case TypeClass_BYTE: rRet <<= sal_uInt8(0); break;
+ case TypeClass_SHORT: rRet <<= sal_Int16( 0 ); break;
+ case TypeClass_LONG: rRet <<= sal_Int32( 0 ); break;
+ case TypeClass_UNSIGNED_SHORT: rRet <<= sal_uInt16( 0 ); break;
+ case TypeClass_UNSIGNED_LONG: rRet <<= sal_uInt32( 0 ); break;
+
+ default:
+ OSL_ASSERT(false);
+ break;
+ }
+ }
+ else if( !rRet.getValueType().equals( rRetType ) )
+ {
+ if( !mxManager->xConverter.is() )
+ throw CannotConvertException();
+ rRet = mxManager->xConverter->convertTo( rRet, rRetType );
+ }
+}
+
+// Methods of XAllListener
+Any SAL_CALL AttacherAllListener_Impl::approveFiring( const AllEventObject& Event )
+{
+ ScriptEvent aScriptEvent;
+ aScriptEvent.Source = static_cast<OWeakObject *>(mxManager.get()); // get correct XInterface
+ aScriptEvent.ListenerType = Event.ListenerType;
+ aScriptEvent.MethodName = Event.MethodName;
+ aScriptEvent.Arguments = Event.Arguments;
+ aScriptEvent.Helper = Event.Helper;
+ aScriptEvent.ScriptType = aScriptType;
+ aScriptEvent.ScriptCode = aScriptCode;
+
+ Any aRet;
+ // Iterate over all listeners and pass events.
+ std::unique_lock l(mxManager->m_aMutex);
+ OInterfaceIteratorHelper4 aIt( l, mxManager->aScriptListeners );
+ while( aIt.hasMoreElements() )
+ {
+ // cannot hold lock over call to approveFiring, since it might recurse back into us
+ l.unlock();
+ aRet = aIt.next()->approveFiring( aScriptEvent );
+ l.lock();
+ try
+ {
+ Reference< XIdlClass > xListenerType = mxManager->getReflection(l)->
+ forName( Event.ListenerType.getTypeName() );
+ Reference< XIdlMethod > xMeth = xListenerType->getMethod( Event.MethodName );
+ if( xMeth.is() )
+ {
+ Reference< XIdlClass > xRetType = xMeth->getReturnType();
+ Type aRetType(xRetType->getTypeClass(), xRetType->getName());
+ convertToEventReturn( aRet, aRetType );
+ }
+
+ switch( aRet.getValueType().getTypeClass() )
+ {
+ case TypeClass_INTERFACE:
+ {
+ // Interface not null, return
+ Reference< XInterface > x;
+ aRet >>= x;
+ if( x.is() )
+ return aRet;
+ }
+ break;
+
+ case TypeClass_BOOLEAN:
+ // FALSE -> Return
+ if( !(*o3tl::forceAccess<bool>(aRet)) )
+ return aRet;
+ break;
+
+ case TypeClass_STRING:
+ // none empty string -> return
+ if( !o3tl::forceAccess<OUString>(aRet)->isEmpty() )
+ return aRet;
+ break;
+
+ // none zero number -> return
+ case TypeClass_FLOAT: if( *o3tl::forceAccess<float>(aRet) ) return aRet; break;
+ case TypeClass_DOUBLE: if( *o3tl::forceAccess<double>(aRet) ) return aRet; break;
+ case TypeClass_BYTE: if( *o3tl::forceAccess<sal_Int8>(aRet) ) return aRet; break;
+ case TypeClass_SHORT: if( *o3tl::forceAccess<sal_Int16>(aRet) ) return aRet; break;
+ case TypeClass_LONG: if( *o3tl::forceAccess<sal_Int32>(aRet) ) return aRet; break;
+ case TypeClass_UNSIGNED_SHORT: if( *o3tl::forceAccess<sal_uInt16>(aRet) ) return aRet; break;
+ case TypeClass_UNSIGNED_LONG: if( *o3tl::forceAccess<sal_uInt32>(aRet) ) return aRet; break;
+
+ default:
+ OSL_ASSERT(false);
+ break;
+ }
+ }
+ catch (const CannotConvertException&)
+ {
+ // silent ignore conversions errors from a script call
+ Reference< XIdlClass > xListenerType = mxManager->getReflection(l)->
+ forName( Event.ListenerType.getTypeName() );
+ Reference< XIdlMethod > xMeth = xListenerType->getMethod( Event.MethodName );
+ if( xMeth.is() )
+ {
+ Reference< XIdlClass > xRetType = xMeth->getReturnType();
+ Type aRetType(xRetType->getTypeClass(), xRetType->getName());
+ aRet.clear();
+ try
+ {
+ convertToEventReturn( aRet, aRetType );
+ }
+ catch (const CannotConvertException& e)
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException(
+ "wrapped CannotConvertException " + e.Message,
+ css::uno::Reference<css::uno::XInterface>(), anyEx);
+ }
+ }
+ }
+ }
+ return aRet;
+}
+
+// Methods of XEventListener
+void SAL_CALL AttacherAllListener_Impl::disposing(const EventObject& )
+{
+ // It is up to the container to release the object
+}
+
+// Constructor method for EventAttacherManager
+Reference< XEventAttacherManager > createEventAttacherManager( const Reference< XComponentContext > & rxContext )
+{
+ Reference< XIntrospection > xIntrospection = theIntrospection::get( rxContext );
+ return new ImplEventAttacherManager( xIntrospection, rxContext );
+}
+
+
+ImplEventAttacherManager::ImplEventAttacherManager( const Reference< XIntrospection > & rIntrospection,
+ const Reference< XComponentContext >& rContext )
+ : mxContext( rContext )
+ , nVersion(0)
+{
+ if ( rContext.is() )
+ {
+ Reference< XInterface > xIFace( rContext->getServiceManager()->createInstanceWithContext(
+ "com.sun.star.script.EventAttacher", rContext) );
+ if ( xIFace.is() )
+ {
+ xAttacher.set( xIFace, UNO_QUERY );
+ }
+ xConverter = Converter::create(rContext);
+ }
+
+ Reference< XInitialization > xInit( xAttacher, UNO_QUERY );
+ if( xInit.is() )
+ {
+ xInit->initialize({ Any(rIntrospection) });
+ }
+}
+
+Reference< XIdlReflection > ImplEventAttacherManager::getReflection(std::unique_lock<std::mutex>&)
+{
+ // Do we already have a service? If not, create one.
+ if( !mxCoreReflection.is() )
+ {
+ mxCoreReflection = theCoreReflection::get(mxContext);
+ }
+ return mxCoreReflection;
+}
+
+
+std::deque< AttacherIndex_Impl >::iterator ImplEventAttacherManager::implCheckIndex( sal_Int32 _nIndex )
+{
+ if ( (_nIndex < 0) || (o3tl::make_unsigned(_nIndex) >= aIndex.size()) )
+ throw IllegalArgumentException("wrong index", static_cast<cppu::OWeakObject*>(this), 1);
+
+ std::deque<AttacherIndex_Impl>::iterator aIt = aIndex.begin() + _nIndex;
+ return aIt;
+}
+
+// Methods of XEventAttacherManager
+void SAL_CALL ImplEventAttacherManager::registerScriptEvent
+(
+ sal_Int32 nIndex,
+ const ScriptEventDescriptor& ScriptEvent
+)
+{
+ std::unique_lock l(m_aMutex);
+ registerScriptEvent(l, nIndex, ScriptEvent);
+}
+
+void ImplEventAttacherManager::registerScriptEvent
+(
+ std::unique_lock<std::mutex>&,
+ sal_Int32 nIndex,
+ const ScriptEventDescriptor& ScriptEvent
+)
+{
+ // Examine the index and apply the array
+ std::deque<AttacherIndex_Impl>::iterator aIt = implCheckIndex( nIndex );
+
+ ScriptEventDescriptor aEvt = ScriptEvent;
+ sal_Int32 nLastDot = aEvt.ListenerType.lastIndexOf('.');
+ if (nLastDot != -1)
+ aEvt.ListenerType = aEvt.ListenerType.copy(nLastDot+1);
+ aIt->aEventList.push_back( aEvt );
+
+ // register new Event
+ for( auto& rObj : aIt->aObjList )
+ {
+ Reference< XAllListener > xAll =
+ new AttacherAllListener_Impl( this, ScriptEvent.ScriptType, ScriptEvent.ScriptCode );
+ try
+ {
+ rObj.aAttachedListenerSeq.push_back( xAttacher->attachSingleEventListener( rObj.xTarget, xAll,
+ rObj.aHelper, ScriptEvent.ListenerType,
+ ScriptEvent.AddListenerParam, ScriptEvent.EventMethod ) );
+ }
+ catch( Exception& )
+ {
+ }
+ }
+}
+
+
+void SAL_CALL ImplEventAttacherManager::registerScriptEvents
+(
+ sal_Int32 nIndex,
+ const Sequence< ScriptEventDescriptor >& ScriptEvents
+)
+{
+ std::unique_lock l(m_aMutex);
+ registerScriptEvents(l, nIndex, ScriptEvents);
+}
+
+void ImplEventAttacherManager::registerScriptEvents
+(
+ std::unique_lock<std::mutex>& l,
+ sal_Int32 nIndex,
+ const Sequence< ScriptEventDescriptor >& ScriptEvents
+)
+{
+ // Examine the index and apply the array
+ std::deque< AttachedObject_Impl > aList = implCheckIndex( nIndex )->aObjList;
+ for( const auto& rObj : aList )
+ detach( l, nIndex, rObj.xTarget );
+
+ const ScriptEventDescriptor* pArray = ScriptEvents.getConstArray();
+ sal_Int32 nLen = ScriptEvents.getLength();
+ for( sal_Int32 i = 0 ; i < nLen ; i++ )
+ registerScriptEvent( l, nIndex, pArray[ i ] );
+
+ for( const auto& rObj : aList )
+ attach( l, nIndex, rObj.xTarget, rObj.aHelper );
+}
+
+
+void SAL_CALL ImplEventAttacherManager::revokeScriptEvent
+(
+ sal_Int32 nIndex,
+ const OUString& ListenerType,
+ const OUString& EventMethod,
+ const OUString& ToRemoveListenerParam
+)
+{
+ std::unique_lock l(m_aMutex);
+
+ std::deque<AttacherIndex_Impl>::iterator aIt = implCheckIndex( nIndex );
+
+ std::deque< AttachedObject_Impl > aList = aIt->aObjList;
+ for( const auto& rObj : aList )
+ detach( l, nIndex, rObj.xTarget );
+
+ std::u16string_view aLstType = ListenerType;
+ size_t nLastDot = aLstType.rfind('.');
+ if (nLastDot != std::u16string_view::npos)
+ aLstType = aLstType.substr(nLastDot+1);
+
+ auto aEvtIt = std::find_if(aIt->aEventList.begin(), aIt->aEventList.end(),
+ [&aLstType, &EventMethod, &ToRemoveListenerParam](const ScriptEventDescriptor& rEvent) {
+ return aLstType == rEvent.ListenerType
+ && EventMethod == rEvent.EventMethod
+ && ToRemoveListenerParam == rEvent.AddListenerParam;
+ });
+ if (aEvtIt != aIt->aEventList.end())
+ aIt->aEventList.erase( aEvtIt );
+
+ for( const auto& rObj : aList )
+ attach( l, nIndex, rObj.xTarget, rObj.aHelper );
+}
+
+
+void SAL_CALL ImplEventAttacherManager::revokeScriptEvents(sal_Int32 nIndex )
+{
+ std::unique_lock l(m_aMutex);
+ std::deque<AttacherIndex_Impl>::iterator aIt = implCheckIndex( nIndex );
+
+ std::deque< AttachedObject_Impl > aList = aIt->aObjList;
+ for( const auto& rObj : aList )
+ detach( l, nIndex, rObj.xTarget );
+ aIt->aEventList.clear();
+ for( const auto& rObj : aList )
+ attach( l, nIndex, rObj.xTarget, rObj.aHelper );
+}
+
+
+void SAL_CALL ImplEventAttacherManager::insertEntry(sal_Int32 nIndex)
+{
+ std::unique_lock l(m_aMutex);
+ if( nIndex < 0 )
+ throw IllegalArgumentException("negative index", static_cast<cppu::OWeakObject*>(this), 1);
+
+ insertEntry(l, nIndex);
+}
+
+void ImplEventAttacherManager::insertEntry(std::unique_lock<std::mutex>&, sal_Int32 nIndex)
+{
+ if ( o3tl::make_unsigned(nIndex) >= aIndex.size() )
+ aIndex.resize(nIndex+1);
+
+ AttacherIndex_Impl aTmp;
+ aIndex.insert( aIndex.begin() + nIndex, aTmp );
+}
+
+void SAL_CALL ImplEventAttacherManager::removeEntry(sal_Int32 nIndex)
+{
+ std::unique_lock l(m_aMutex);
+ std::deque<AttacherIndex_Impl>::iterator aIt = implCheckIndex( nIndex );
+
+ std::deque< AttachedObject_Impl > aList = aIt->aObjList;
+ for( const auto& rObj : aList )
+ detach( l, nIndex, rObj.xTarget );
+
+ aIndex.erase( aIt );
+}
+
+
+Sequence< ScriptEventDescriptor > SAL_CALL ImplEventAttacherManager::getScriptEvents(sal_Int32 nIndex)
+{
+ std::unique_lock l(m_aMutex);
+ std::deque<AttacherIndex_Impl>::iterator aIt = implCheckIndex( nIndex );
+ return comphelper::containerToSequence(aIt->aEventList);
+}
+
+
+void SAL_CALL ImplEventAttacherManager::attach(sal_Int32 nIndex, const Reference< XInterface >& xObject, const Any & Helper)
+{
+ std::unique_lock l(m_aMutex);
+ if( nIndex < 0 || !xObject.is() )
+ throw IllegalArgumentException("negative index, or null object", static_cast<cppu::OWeakObject*>(this), -1);
+ attach(l, nIndex, xObject, Helper);
+}
+
+void ImplEventAttacherManager::attach(std::unique_lock<std::mutex>& l, sal_Int32 nIndex, const Reference< XInterface >& xObject, const Any & Helper)
+{
+ if( o3tl::make_unsigned(nIndex) >= aIndex.size() )
+ {
+ // read older files
+ if( nVersion != 1 )
+ throw IllegalArgumentException();
+ insertEntry( l, nIndex );
+ attach( l, nIndex, xObject, Helper );
+ return;
+ }
+
+ std::deque< AttacherIndex_Impl >::iterator aCurrentPosition = aIndex.begin() + nIndex;
+
+ AttachedObject_Impl aTmp;
+ aTmp.xTarget = xObject;
+ aTmp.aHelper = Helper;
+ aCurrentPosition->aObjList.push_back( aTmp );
+
+ AttachedObject_Impl & rCurObj = aCurrentPosition->aObjList.back();
+ rCurObj.aAttachedListenerSeq = std::vector< Reference< XEventListener > >( aCurrentPosition->aEventList.size() );
+
+ if (aCurrentPosition->aEventList.empty())
+ return;
+
+ Sequence<css::script::EventListener> aEvents(aCurrentPosition->aEventList.size());
+ css::script::EventListener* p = aEvents.getArray();
+ size_t i = 0;
+ for (const auto& rEvent : aCurrentPosition->aEventList)
+ {
+ css::script::EventListener aListener;
+ aListener.AllListener =
+ new AttacherAllListener_Impl(this, rEvent.ScriptType, rEvent.ScriptCode);
+ aListener.Helper = rCurObj.aHelper;
+ aListener.ListenerType = rEvent.ListenerType;
+ aListener.EventMethod = rEvent.EventMethod;
+ aListener.AddListenerParam = rEvent.AddListenerParam;
+ p[i++] = aListener;
+ }
+
+ try
+ {
+ rCurObj.aAttachedListenerSeq = comphelper::sequenceToContainer<std::vector<Reference< XEventListener >>>(
+ xAttacher->attachMultipleEventListeners(rCurObj.xTarget, aEvents));
+ }
+ catch (const Exception&)
+ {
+ // Fail gracefully.
+ }
+}
+
+
+void SAL_CALL ImplEventAttacherManager::detach(sal_Int32 nIndex, const Reference< XInterface >& xObject)
+{
+ std::unique_lock l(m_aMutex);
+ //return;
+ if( nIndex < 0 || o3tl::make_unsigned(nIndex) >= aIndex.size() || !xObject.is() )
+ throw IllegalArgumentException("bad index or null object", static_cast<cppu::OWeakObject*>(this), 1);
+ detach(l, nIndex, xObject);
+}
+
+void ImplEventAttacherManager::detach(std::unique_lock<std::mutex>&, sal_Int32 nIndex, const Reference< XInterface >& xObject)
+{
+ std::deque< AttacherIndex_Impl >::iterator aCurrentPosition = aIndex.begin() + nIndex;
+ auto aObjIt = std::find_if(aCurrentPosition->aObjList.begin(), aCurrentPosition->aObjList.end(),
+ [&xObject](const AttachedObject_Impl& rObj) { return rObj.xTarget == xObject; });
+ if (aObjIt == aCurrentPosition->aObjList.end())
+ return;
+
+ sal_Int32 i = 0;
+ for( const auto& rEvt : aCurrentPosition->aEventList )
+ {
+ if( aObjIt->aAttachedListenerSeq[i].is() )
+ {
+ try
+ {
+ xAttacher->removeListener( aObjIt->xTarget, rEvt.ListenerType,
+ rEvt.AddListenerParam, aObjIt->aAttachedListenerSeq[i] );
+ }
+ catch( Exception& )
+ {
+ }
+ }
+ ++i;
+ }
+ aCurrentPosition->aObjList.erase( aObjIt );
+}
+
+void SAL_CALL ImplEventAttacherManager::addScriptListener(const Reference< XScriptListener >& aListener)
+{
+ std::unique_lock l(m_aMutex);
+ aScriptListeners.addInterface( l, aListener );
+}
+
+void SAL_CALL ImplEventAttacherManager::removeScriptListener(const Reference< XScriptListener >& aListener)
+{
+ std::unique_lock l(m_aMutex);
+ aScriptListeners.removeInterface( l, aListener );
+}
+
+
+// Methods of XPersistObject
+OUString SAL_CALL ImplEventAttacherManager::getServiceName()
+{
+ return "com.sun.star.uno.script.EventAttacherManager";
+}
+
+void SAL_CALL ImplEventAttacherManager::write(const Reference< XObjectOutputStream >& OutStream)
+{
+ std::unique_lock l(m_aMutex);
+ // Don't run without XMarkableStream
+ Reference< XMarkableStream > xMarkStream( OutStream, UNO_QUERY );
+ if( !xMarkStream.is() )
+ return;
+
+ // Write out the version
+ OutStream->writeShort( 2 );
+
+ // Remember position for length
+ sal_Int32 nObjLenMark = xMarkStream->createMark();
+ OutStream->writeLong( 0 );
+
+ OutStream->writeLong( aIndex.size() );
+
+ // Write out sequences
+ for( const auto& rIx : aIndex )
+ {
+ OutStream->writeLong( rIx.aEventList.size() );
+ for( const auto& rDesc : rIx.aEventList )
+ {
+ OutStream->writeUTF( rDesc.ListenerType );
+ OutStream->writeUTF( rDesc.EventMethod );
+ OutStream->writeUTF( rDesc.AddListenerParam );
+ OutStream->writeUTF( rDesc.ScriptType );
+ OutStream->writeUTF( rDesc.ScriptCode );
+ }
+ }
+
+ // The length is now known
+ sal_Int32 nObjLen = xMarkStream->offsetToMark( nObjLenMark ) -4;
+ xMarkStream->jumpToMark( nObjLenMark );
+ OutStream->writeLong( nObjLen );
+ xMarkStream->jumpToFurthest();
+ xMarkStream->deleteMark( nObjLenMark );
+}
+
+void SAL_CALL ImplEventAttacherManager::read(const Reference< XObjectInputStream >& InStream)
+{
+ std::unique_lock l(m_aMutex);
+ // Don't run without XMarkableStream
+ Reference< XMarkableStream > xMarkStream( InStream, UNO_QUERY );
+ if( !xMarkStream.is() )
+ return;
+
+ // Read in the version
+ nVersion = InStream->readShort();
+
+ // At first there's the data according to version 1 --
+ // this part needs to be kept in later versions.
+ sal_Int32 nLen = InStream->readLong();
+
+ // Position for comparative purposes
+ sal_Int32 nObjLenMark = xMarkStream->createMark();
+
+ // Number of read sequences
+ sal_Int32 nItemCount = InStream->readLong();
+
+ for( sal_Int32 i = 0 ; i < nItemCount ; i++ )
+ {
+ insertEntry( l, i );
+ // Read the length of the sequence
+ sal_Int32 nSeqLen = InStream->readLong();
+
+ // Display the sequences and read the descriptions
+ Sequence< ScriptEventDescriptor > aSEDSeq( nSeqLen );
+ ScriptEventDescriptor* pArray = aSEDSeq.getArray();
+ for( sal_Int32 j = 0 ; j < nSeqLen ; j++ )
+ {
+ ScriptEventDescriptor& rDesc = pArray[ j ];
+ rDesc.ListenerType = InStream->readUTF();
+ rDesc.EventMethod = InStream->readUTF();
+ rDesc.AddListenerParam = InStream->readUTF();
+ rDesc.ScriptType = InStream->readUTF();
+ rDesc.ScriptCode = InStream->readUTF();
+ }
+ registerScriptEvents( l, i, aSEDSeq );
+ }
+
+ // Have we read the specified length?
+ sal_Int32 nRealLen = xMarkStream->offsetToMark( nObjLenMark );
+ if( nRealLen != nLen )
+ {
+ // Only if the StreamVersion is > 1 and the date still follows, can
+ // this be true. Otherwise, something is completely gone.
+ if( nRealLen > nLen || nVersion == 1 )
+ {
+ OSL_FAIL( "ImplEventAttacherManager::read(): Fatal Error, wrong object length" );
+ }
+ else
+ { // TODO: Examine if caching the dates would be useful
+ // But for now, it's easier to skip it.
+ sal_Int32 nSkipCount = nLen - nRealLen;
+ InStream->skipBytes( nSkipCount );
+ }
+ }
+ xMarkStream->jumpToFurthest();
+ xMarkStream->deleteMark( nObjLenMark );
+}
+
+} // namespace comphelper
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/AccessibleImplementationHelper.cxx b/comphelper/source/misc/AccessibleImplementationHelper.cxx
new file mode 100644
index 0000000000..a02f4380dd
--- /dev/null
+++ b/comphelper/source/misc/AccessibleImplementationHelper.cxx
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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 <comphelper/AccessibleImplementationHelper.hxx>
+
+#include <com/sun/star/awt/KeyStroke.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <rtl/ustrbuf.hxx>
+
+using namespace css::awt;
+using namespace css::uno;
+
+namespace comphelper
+{
+OUString GetkeyBindingStrByXkeyBinding(const Sequence<KeyStroke>& keySet)
+{
+ OUStringBuffer buf;
+ for (const auto& k : keySet)
+ {
+ buf.append("\n" + OUStringChar(k.KeyChar));
+ }
+ return buf.makeStringAndClear();
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/comphelper/source/misc/DirectoryHelper.cxx b/comphelper/source/misc/DirectoryHelper.cxx
new file mode 100644
index 0000000000..badfe9b62d
--- /dev/null
+++ b/comphelper/source/misc/DirectoryHelper.cxx
@@ -0,0 +1,213 @@
+/* -*- 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 <comphelper/DirectoryHelper.hxx>
+
+#include <sal/config.h>
+#include <osl/file.hxx>
+#include <rtl/uri.hxx>
+
+#include <memory>
+
+namespace comphelper
+{
+typedef std::shared_ptr<osl::File> FileSharedPtr;
+
+std::u16string_view DirectoryHelper::splitAtLastToken(std::u16string_view rSrc, sal_Unicode aToken,
+ OUString& rRight)
+{
+ const size_t nIndex(rSrc.rfind(aToken));
+ std::u16string_view aRetval;
+
+ if (std::u16string_view::npos == nIndex)
+ {
+ aRetval = rSrc;
+ rRight.clear();
+ }
+ else if (nIndex > 0)
+ {
+ aRetval = rSrc.substr(0, nIndex);
+
+ if (rSrc.size() > nIndex + 1)
+ {
+ rRight = rSrc.substr(nIndex + 1);
+ }
+ }
+
+ return aRetval;
+}
+
+bool DirectoryHelper::fileExists(const OUString& rBaseURL)
+{
+ if (!rBaseURL.isEmpty())
+ {
+ FileSharedPtr aBaseFile = std::make_shared<osl::File>(rBaseURL);
+
+ return (osl::File::E_None == aBaseFile->open(osl_File_OpenFlag_Read));
+ }
+
+ return false;
+}
+
+bool DirectoryHelper::dirExists(const OUString& rDirURL)
+{
+ if (!rDirURL.isEmpty())
+ {
+ osl::Directory aDirectory(rDirURL);
+
+ return (osl::FileBase::E_None == aDirectory.open());
+ }
+
+ return false;
+}
+
+void DirectoryHelper::scanDirsAndFiles(const OUString& rDirURL, std::set<OUString>& rDirs,
+ std::set<std::pair<OUString, OUString>>& rFiles)
+{
+ if (rDirURL.isEmpty())
+ return;
+
+ osl::Directory aDirectory(rDirURL);
+
+ if (osl::FileBase::E_None != aDirectory.open())
+ return;
+
+ auto lcl_encodeUriSegment = [](OUString const& rPath) {
+ return rtl::Uri::encode(rPath, rtl_UriCharClassUricNoSlash, rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8);
+ };
+
+ osl::DirectoryItem aDirectoryItem;
+
+ while (osl::FileBase::E_None == aDirectory.getNextItem(aDirectoryItem))
+ {
+ osl::FileStatus aFileStatus(osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileName);
+
+ if (osl::FileBase::E_None == aDirectoryItem.getFileStatus(aFileStatus))
+ {
+ if (aFileStatus.isDirectory())
+ {
+ const OUString aFileName(aFileStatus.getFileName());
+
+ if (!aFileName.isEmpty())
+ {
+ rDirs.insert(lcl_encodeUriSegment(aFileName));
+ }
+ }
+ else if (aFileStatus.isRegular())
+ {
+ OUString aFileName(aFileStatus.getFileName());
+ OUString aExtension;
+ aFileName = splitAtLastToken(aFileName, '.', aExtension);
+
+ if (!aFileName.isEmpty())
+ {
+ rFiles.insert(std::pair<OUString, OUString>(lcl_encodeUriSegment(aFileName),
+ lcl_encodeUriSegment(aExtension)));
+ }
+ }
+ }
+ }
+}
+
+bool DirectoryHelper::deleteDirRecursively(const OUString& rDirURL)
+{
+ std::set<OUString> aDirs;
+ std::set<std::pair<OUString, OUString>> aFiles;
+ bool bError(false);
+
+ scanDirsAndFiles(rDirURL, aDirs, aFiles);
+
+ for (const auto& dir : aDirs)
+ {
+ const OUString aNewDirURL(rDirURL + "/" + dir);
+
+ bError |= deleteDirRecursively(aNewDirURL);
+ }
+
+ for (const auto& file : aFiles)
+ {
+ OUString aNewFileURL(rDirURL + "/" + file.first);
+
+ if (!file.second.isEmpty())
+ {
+ aNewFileURL += "." + file.second;
+ }
+ bError |= (osl::FileBase::E_None != osl::File::remove(aNewFileURL));
+ }
+
+ bError |= (osl::FileBase::E_None != osl::Directory::remove(rDirURL));
+
+ return bError;
+}
+
+// both exist, move content
+bool DirectoryHelper::moveDirContent(const OUString& rSourceDirURL,
+ std::u16string_view rTargetDirURL,
+ const std::set<OUString>& rExcludeList)
+{
+ std::set<OUString> aDirs;
+ std::set<std::pair<OUString, OUString>> aFiles;
+ bool bError(false);
+
+ scanDirsAndFiles(rSourceDirURL, aDirs, aFiles);
+
+ for (const auto& dir : aDirs)
+ {
+ const bool bExcluded(!rExcludeList.empty() && rExcludeList.find(dir) != rExcludeList.end());
+
+ if (!bExcluded)
+ {
+ const OUString aNewSourceDirURL(rSourceDirURL + "/" + dir);
+
+ if (dirExists(aNewSourceDirURL))
+ {
+ const OUString aNewTargetDirURL(OUString::Concat(rTargetDirURL) + "/" + dir);
+
+ if (dirExists(aNewTargetDirURL))
+ {
+ deleteDirRecursively(aNewTargetDirURL);
+ }
+
+ bError |= (osl::FileBase::E_None
+ != osl::File::move(aNewSourceDirURL, aNewTargetDirURL));
+ }
+ }
+ }
+
+ for (const auto& file : aFiles)
+ {
+ OUString aSourceFileURL(rSourceDirURL + "/" + file.first);
+
+ if (!file.second.isEmpty())
+ {
+ aSourceFileURL += "." + file.second;
+ }
+
+ if (fileExists(aSourceFileURL))
+ {
+ OUString aTargetFileURL(OUString::Concat(rTargetDirURL) + "/" + file.first);
+
+ if (!file.second.isEmpty())
+ {
+ aTargetFileURL += "." + file.second;
+ }
+
+ if (fileExists(aTargetFileURL))
+ {
+ osl::File::remove(aTargetFileURL);
+ }
+
+ bError |= (osl::FileBase::E_None != osl::File::move(aSourceFileURL, aTargetFileURL));
+ }
+ }
+
+ return bError;
+}
+}
diff --git a/comphelper/source/misc/SelectionMultiplex.cxx b/comphelper/source/misc/SelectionMultiplex.cxx
new file mode 100644
index 0000000000..37e4e30037
--- /dev/null
+++ b/comphelper/source/misc/SelectionMultiplex.cxx
@@ -0,0 +1,114 @@
+/* -*- 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 <comphelper/SelectionMultiplex.hxx>
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+
+namespace comphelper
+{
+
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::view;
+
+OSelectionChangeListener::~OSelectionChangeListener()
+{
+}
+
+
+void OSelectionChangeListener::_disposing(const EventObject&)
+{
+ // nothing to do here
+}
+
+
+OSelectionChangeMultiplexer::OSelectionChangeMultiplexer(OSelectionChangeListener* _pListener, const Reference< XSelectionSupplier>& _rxSet)
+ :m_xSet(_rxSet)
+ ,m_pListener(_pListener)
+ ,m_nLockCount(0)
+{
+ osl_atomic_increment(&m_refCount);
+ {
+ Reference< XSelectionChangeListener> xPreventDelete(this);
+ m_xSet->addSelectionChangeListener(xPreventDelete);
+ }
+ osl_atomic_decrement(&m_refCount);
+}
+
+
+OSelectionChangeMultiplexer::~OSelectionChangeMultiplexer()
+{
+}
+
+
+void OSelectionChangeMultiplexer::lock()
+{
+ ++m_nLockCount;
+}
+
+
+void OSelectionChangeMultiplexer::unlock()
+{
+ --m_nLockCount;
+}
+
+
+// XEventListener
+
+void SAL_CALL OSelectionChangeMultiplexer::disposing( const EventObject& _rSource)
+{
+ if (m_pListener)
+ {
+ // tell the listener
+ if (!locked())
+ m_pListener->_disposing(_rSource);
+ }
+
+ m_pListener = nullptr;
+
+ m_xSet = nullptr;
+}
+
+// XSelectionChangeListener
+
+void SAL_CALL OSelectionChangeMultiplexer::selectionChanged( const EventObject& _rEvent )
+{
+ if (m_pListener && !locked())
+ m_pListener->_selectionChanged(_rEvent);
+}
+
+void OSelectionChangeMultiplexer::dispose()
+{
+ osl_atomic_increment(&m_refCount);
+ {
+ Reference< XSelectionChangeListener> xPreventDelete(this);
+ if(m_xSet.is())
+ {
+ m_xSet->removeSelectionChangeListener(xPreventDelete);
+ m_xSet.clear();
+ }
+ }
+ osl_atomic_decrement(&m_refCount);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/accessiblecomponenthelper.cxx b/comphelper/source/misc/accessiblecomponenthelper.cxx
new file mode 100644
index 0000000000..3922812b92
--- /dev/null
+++ b/comphelper/source/misc/accessiblecomponenthelper.cxx
@@ -0,0 +1,369 @@
+/* -*- 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 <comphelper/accessiblecomponenthelper.hxx>
+#include <comphelper/accessiblecontexthelper.hxx>
+#include <osl/diagnose.h>
+#include <com/sun/star/accessibility/IllegalAccessibleComponentStateException.hpp>
+#include <comphelper/accessibleeventnotifier.hxx>
+#include <comphelper/solarmutex.hxx>
+
+
+namespace comphelper
+{
+
+
+ using namespace ::com::sun::star;
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::accessibility;
+
+ OCommonAccessibleComponent::OCommonAccessibleComponent( )
+ :OCommonAccessibleComponent_Base( GetMutex() )
+ ,m_nClientId( 0 )
+ {
+ }
+
+
+ OCommonAccessibleComponent::~OCommonAccessibleComponent( )
+ {
+ // this ensures that the lock, which may be already destroyed as part of the derivee,
+ // is not used anymore
+
+ ensureDisposed();
+ }
+
+
+ void SAL_CALL OCommonAccessibleComponent::disposing()
+ {
+ // rhbz#1001768: de facto this class is locked by SolarMutex;
+ // do not lock m_Mutex because it may cause deadlock
+ osl::Guard<SolarMutex> aGuard(SolarMutex::get());
+
+ if ( m_nClientId )
+ {
+ AccessibleEventNotifier::revokeClientNotifyDisposing( m_nClientId, *this );
+ m_nClientId=0;
+ }
+ }
+
+
+ void SAL_CALL OCommonAccessibleComponent::addAccessibleEventListener( const Reference< XAccessibleEventListener >& _rxListener )
+ {
+ osl::Guard<SolarMutex> aGuard(SolarMutex::get());
+ // don't use the OContextEntryGuard - it will throw an exception if we're not alive
+ // anymore, while the most recent specification for XComponent states that we should
+ // silently ignore the call in such a situation
+ if ( !isAlive() )
+ {
+ if ( _rxListener.is() )
+ _rxListener->disposing( EventObject( *this ) );
+ return;
+ }
+
+ if ( _rxListener.is() )
+ {
+ if ( !m_nClientId )
+ m_nClientId = AccessibleEventNotifier::registerClient( );
+
+ AccessibleEventNotifier::addEventListener( m_nClientId, _rxListener );
+ }
+ }
+
+
+ void SAL_CALL OCommonAccessibleComponent::removeAccessibleEventListener( const Reference< XAccessibleEventListener >& _rxListener )
+ {
+ osl::Guard<SolarMutex> aGuard(SolarMutex::get());
+ // don't use the OContextEntryGuard - it will throw an exception if we're not alive
+ // anymore, while the most recent specification for XComponent states that we should
+ // silently ignore the call in such a situation
+ if ( !isAlive() )
+ return;
+
+ if ( !(_rxListener.is() && m_nClientId) )
+ return;
+
+ sal_Int32 nListenerCount = AccessibleEventNotifier::removeEventListener( m_nClientId, _rxListener );
+ if ( !nListenerCount )
+ {
+ // no listeners anymore
+ // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client),
+ // and at least to us not firing any events anymore, in case somebody calls
+ // NotifyAccessibleEvent, again
+ AccessibleEventNotifier::revokeClient( m_nClientId );
+ m_nClientId = 0;
+ }
+ }
+
+
+ void OCommonAccessibleComponent::NotifyAccessibleEvent( const sal_Int16 _nEventId,
+ const Any& _rOldValue, const Any& _rNewValue, sal_Int32 nIndexHint )
+ {
+ if ( !m_nClientId )
+ // if we don't have a client id for the notifier, then we don't have listeners, then
+ // we don't need to notify anything
+ return;
+
+ // build an event object
+ AccessibleEventObject aEvent(*this, _nEventId, _rNewValue, _rOldValue, nIndexHint);
+
+ // let the notifier handle this event
+ AccessibleEventNotifier::addEvent( m_nClientId, aEvent );
+ }
+
+
+ bool OCommonAccessibleComponent::isAlive() const
+ {
+ return !rBHelper.bDisposed && !rBHelper.bInDispose;
+ }
+
+
+ void OCommonAccessibleComponent::ensureAlive() const
+ {
+ if( !isAlive() )
+ throw DisposedException();
+ }
+
+
+ void OCommonAccessibleComponent::ensureDisposed( )
+ {
+ if ( !rBHelper.bDisposed )
+ {
+ OSL_ENSURE( 0 == m_refCount, "OCommonAccessibleComponent::ensureDisposed: this method _has_ to be called from without your dtor only!" );
+ acquire();
+ dispose();
+ }
+ }
+
+
+ void OCommonAccessibleComponent::lateInit( const Reference< XAccessible >& _rxAccessible )
+ {
+ m_aCreator = _rxAccessible;
+ }
+
+
+ Reference< XAccessible > OCommonAccessibleComponent::getAccessibleCreator( ) const
+ {
+ return m_aCreator;
+ }
+
+
+ OUString SAL_CALL OCommonAccessibleComponent::getAccessibleId( )
+ {
+ return OUString();
+ }
+
+
+ sal_Int64 SAL_CALL OCommonAccessibleComponent::getAccessibleIndexInParent( )
+ {
+ OExternalLockGuard aGuard( this );
+
+ // -1 for child not found/no parent (according to specification)
+ sal_Int64 nRet = -1;
+
+ try
+ {
+
+ Reference< XAccessibleContext > xParentContext( implGetParentContext() );
+
+ // iterate over parent's children and search for this object
+ if ( xParentContext.is() )
+ {
+ // our own XAccessible for comparing with the children of our parent
+ Reference< XAccessible > xCreator( m_aCreator);
+
+ OSL_ENSURE( xCreator.is(), "OCommonAccessibleComponent::getAccessibleIndexInParent: invalid creator!" );
+ // two ideas why this could be NULL:
+ // * nobody called our late ctor (init), so we never had a creator at all -> bad
+ // * the creator is already dead. In this case, we should have been disposed, and
+ // never survived the above OContextEntryGuard.
+ // in all other situations the creator should be non-NULL
+
+ if ( xCreator.is() )
+ {
+ sal_Int64 nChildCount = xParentContext->getAccessibleChildCount();
+ for ( sal_Int64 nChild = 0; ( nChild < nChildCount ) && ( -1 == nRet ); ++nChild )
+ {
+ Reference< XAccessible > xChild( xParentContext->getAccessibleChild( nChild ) );
+ if ( xChild.get() == xCreator.get() )
+ nRet = nChild;
+ }
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ OSL_FAIL( "OCommonAccessibleComponent::getAccessibleIndexInParent: caught an exception!" );
+ }
+
+ return nRet;
+ }
+
+
+ Locale SAL_CALL OCommonAccessibleComponent::getLocale( )
+ {
+ // simply ask the parent
+ Reference< XAccessible > xParent = getAccessibleParent();
+ Reference< XAccessibleContext > xParentContext;
+ if ( xParent.is() )
+ xParentContext = xParent->getAccessibleContext();
+
+ if ( !xParentContext.is() )
+ throw IllegalAccessibleComponentStateException( OUString(), *this );
+
+ return xParentContext->getLocale();
+ }
+
+
+ Reference< XAccessibleContext > OCommonAccessibleComponent::implGetParentContext()
+ {
+ Reference< XAccessible > xParent = getAccessibleParent();
+ Reference< XAccessibleContext > xParentContext;
+ if ( xParent.is() )
+ xParentContext = xParent->getAccessibleContext();
+ return xParentContext;
+ }
+
+
+ bool OCommonAccessibleComponent::containsPoint( const awt::Point& _rPoint )
+ {
+ OExternalLockGuard aGuard( this );
+ awt::Rectangle aBounds( implGetBounds() );
+ return ( _rPoint.X >= 0 )
+ && ( _rPoint.Y >= 0 )
+ && ( _rPoint.X < aBounds.Width )
+ && ( _rPoint.Y < aBounds.Height );
+ }
+
+
+ awt::Point OCommonAccessibleComponent::getLocation( )
+ {
+ OExternalLockGuard aGuard( this );
+ awt::Rectangle aBounds( implGetBounds() );
+ return awt::Point( aBounds.X, aBounds.Y );
+ }
+
+
+ awt::Point OCommonAccessibleComponent::getLocationOnScreen( )
+ {
+ OExternalLockGuard aGuard( this );
+
+ awt::Point aScreenLoc( 0, 0 );
+
+ Reference< XAccessibleComponent > xParentComponent( implGetParentContext(), UNO_QUERY );
+ OSL_ENSURE( xParentComponent.is(), "OCommonAccessibleComponent::getLocationOnScreen: no parent component!" );
+ if ( xParentComponent.is() )
+ {
+ awt::Point aParentScreenLoc( xParentComponent->getLocationOnScreen() );
+ awt::Point aOwnRelativeLoc( getLocation() );
+ aScreenLoc.X = aParentScreenLoc.X + aOwnRelativeLoc.X;
+ aScreenLoc.Y = aParentScreenLoc.Y + aOwnRelativeLoc.Y;
+ }
+
+ return aScreenLoc;
+ }
+
+
+ awt::Size OCommonAccessibleComponent::getSize( )
+ {
+ OExternalLockGuard aGuard( this );
+ awt::Rectangle aBounds( implGetBounds() );
+ return awt::Size( aBounds.Width, aBounds.Height );
+ }
+
+
+ awt::Rectangle OCommonAccessibleComponent::getBounds( )
+ {
+ OExternalLockGuard aGuard( this );
+ return implGetBounds();
+ }
+
+ OAccessibleComponentHelper::OAccessibleComponentHelper( )
+ {
+ }
+
+
+ sal_Bool SAL_CALL OAccessibleComponentHelper::containsPoint( const awt::Point& _rPoint )
+ {
+ return OCommonAccessibleComponent::containsPoint( _rPoint );
+ }
+
+
+ awt::Point SAL_CALL OAccessibleComponentHelper::getLocation( )
+ {
+ return OCommonAccessibleComponent::getLocation( );
+ }
+
+
+ awt::Point SAL_CALL OAccessibleComponentHelper::getLocationOnScreen( )
+ {
+ return OCommonAccessibleComponent::getLocationOnScreen( );
+ }
+
+
+ awt::Size SAL_CALL OAccessibleComponentHelper::getSize( )
+ {
+ return OCommonAccessibleComponent::getSize( );
+ }
+
+
+ awt::Rectangle SAL_CALL OAccessibleComponentHelper::getBounds( )
+ {
+ return OCommonAccessibleComponent::getBounds( );
+ }
+
+ OAccessibleExtendedComponentHelper::OAccessibleExtendedComponentHelper( )
+ {
+ }
+
+
+ sal_Bool SAL_CALL OAccessibleExtendedComponentHelper::containsPoint( const awt::Point& _rPoint )
+ {
+ return OCommonAccessibleComponent::containsPoint( _rPoint );
+ }
+
+
+ awt::Point SAL_CALL OAccessibleExtendedComponentHelper::getLocation( )
+ {
+ return OCommonAccessibleComponent::getLocation( );
+ }
+
+
+ awt::Point SAL_CALL OAccessibleExtendedComponentHelper::getLocationOnScreen( )
+ {
+ return OCommonAccessibleComponent::getLocationOnScreen( );
+ }
+
+
+ awt::Size SAL_CALL OAccessibleExtendedComponentHelper::getSize( )
+ {
+ return OCommonAccessibleComponent::getSize( );
+ }
+
+
+ awt::Rectangle SAL_CALL OAccessibleExtendedComponentHelper::getBounds( )
+ {
+ return OCommonAccessibleComponent::getBounds( );
+ }
+
+
+} // namespace comphelper
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/accessibleeventnotifier.cxx b/comphelper/source/misc/accessibleeventnotifier.cxx
new file mode 100644
index 0000000000..9c3b55126b
--- /dev/null
+++ b/comphelper/source/misc/accessibleeventnotifier.cxx
@@ -0,0 +1,273 @@
+/* -*- 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 <comphelper/accessibleeventnotifier.hxx>
+#include <com/sun/star/accessibility/XAccessibleEventListener.hpp>
+#include <comphelper/interfacecontainer4.hxx>
+
+#include <limits>
+#include <map>
+#include <memory>
+#include <unordered_map>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::accessibility;
+using namespace ::comphelper;
+
+namespace {
+
+typedef std::pair< AccessibleEventNotifier::TClientId,
+ AccessibleEventObject > ClientEvent;
+
+typedef ::comphelper::OInterfaceContainerHelper4<XAccessibleEventListener> ListenerContainer;
+typedef std::unordered_map< AccessibleEventNotifier::TClientId, ListenerContainer > ClientMap;
+
+/// key is the end of the interval, value is the start of the interval
+typedef std::map<AccessibleEventNotifier::TClientId,
+ AccessibleEventNotifier::TClientId> IntervalMap;
+
+std::mutex& GetLocalMutex()
+{
+ static std::mutex MUTEX;
+ return MUTEX;
+}
+
+ClientMap gaClients;
+
+IntervalMap& GetFreeIntervals()
+{
+ static IntervalMap MAP =
+ []()
+ {
+ IntervalMap map;
+ map.insert(std::make_pair(
+ std::numeric_limits<AccessibleEventNotifier::TClientId>::max(), 1));
+ return map;
+ }();
+ return MAP;
+}
+
+void releaseId(AccessibleEventNotifier::TClientId const nId)
+{
+ IntervalMap & rFreeIntervals(GetFreeIntervals());
+ IntervalMap::iterator const upper(rFreeIntervals.upper_bound(nId));
+ assert(upper != rFreeIntervals.end());
+ assert(nId < upper->second); // second is start of the interval!
+ if (nId + 1 == upper->second)
+ {
+ --upper->second; // add nId to existing interval
+ }
+ else
+ {
+ IntervalMap::iterator const lower(rFreeIntervals.lower_bound(nId));
+ if (lower != rFreeIntervals.end() && lower->first == nId - 1)
+ {
+ // add nId by replacing lower with new merged entry
+ rFreeIntervals.insert(std::make_pair(nId, lower->second));
+ rFreeIntervals.erase(lower);
+ }
+ else // otherwise just add new 1-element interval
+ {
+ rFreeIntervals.insert(std::make_pair(nId, nId));
+ }
+ }
+ // currently it's not checked whether intervals can be merged now
+ // hopefully that won't be a problem in practice
+}
+
+/// generates a new client id
+AccessibleEventNotifier::TClientId generateId()
+{
+ IntervalMap & rFreeIntervals(GetFreeIntervals());
+ assert(!rFreeIntervals.empty());
+ IntervalMap::iterator const iter(rFreeIntervals.begin());
+ AccessibleEventNotifier::TClientId const nFirst = iter->first;
+ AccessibleEventNotifier::TClientId const nFreeId = iter->second;
+ assert(nFreeId <= nFirst);
+ if (nFreeId != nFirst)
+ {
+ ++iter->second; // remove nFreeId from interval
+ }
+ else
+ {
+ rFreeIntervals.erase(iter); // remove 1-element interval
+ }
+
+ assert(gaClients.end() == gaClients.find(nFreeId));
+
+ return nFreeId;
+}
+
+/** looks up a client in our client map, asserts if it cannot find it or
+ no event thread is present
+
+ @precond
+ to be called with our mutex locked
+
+ @param nClient
+ the id of the client to lookup
+ @param rPos
+ out-parameter for the position of the client in the client map
+
+ @return
+ <TRUE/> if and only if the client could be found and
+ <arg>rPos</arg> has been filled with its position
+*/
+bool implLookupClient(
+ const AccessibleEventNotifier::TClientId nClient,
+ ClientMap::iterator& rPos )
+{
+ // look up this client
+ ClientMap &rClients = gaClients;
+ rPos = rClients.find( nClient );
+ assert( rClients.end() != rPos &&
+ "AccessibleEventNotifier::implLookupClient: invalid client id "
+ "(did you register your client?)!" );
+
+ return ( rClients.end() != rPos );
+}
+
+} // anonymous namespace
+
+namespace comphelper {
+
+AccessibleEventNotifier::TClientId AccessibleEventNotifier::registerClient()
+{
+ std::scoped_lock aGuard( GetLocalMutex() );
+
+ // generate a new client id
+ TClientId nNewClientId = generateId( );
+
+ // add the client
+ gaClients.emplace( nNewClientId, ListenerContainer{} );
+
+ // outta here
+ return nNewClientId;
+}
+
+void AccessibleEventNotifier::revokeClient( const TClientId _nClient )
+{
+ std::scoped_lock aGuard( GetLocalMutex() );
+
+ ClientMap::iterator aClientPos;
+ if ( !implLookupClient( _nClient, aClientPos ) )
+ // already asserted in implLookupClient
+ return;
+
+ // remove it from the clients map
+ gaClients.erase( aClientPos );
+ releaseId(_nClient);
+}
+
+void AccessibleEventNotifier::revokeClientNotifyDisposing(
+ const TClientId _nClient, const Reference< XInterface >& _rxEventSource )
+{
+ std::unique_lock aGuard( GetLocalMutex() );
+
+ ClientMap::iterator aClientPos;
+ if (!implLookupClient(_nClient, aClientPos))
+ // already asserted in implLookupClient
+ return;
+
+ // notify the listeners
+ ListenerContainer aListeners(std::move(aClientPos->second));
+
+ // we do not need the entry in the clients map anymore
+ // (do this before actually notifying, because some client
+ // implementations have re-entrance problems and call into
+ // revokeClient while we are notifying from here)
+ gaClients.erase(aClientPos);
+ releaseId(_nClient);
+
+ // notify the "disposing" event for this client
+ EventObject aDisposalEvent;
+ aDisposalEvent.Source = _rxEventSource;
+
+ // now really do the notification
+ aListeners.disposeAndClear( aGuard, aDisposalEvent );
+}
+
+sal_Int32 AccessibleEventNotifier::addEventListener(
+ const TClientId _nClient, const Reference< XAccessibleEventListener >& _rxListener )
+{
+ std::unique_lock aGuard( GetLocalMutex() );
+
+ ClientMap::iterator aClientPos;
+ if ( !implLookupClient( _nClient, aClientPos ) )
+ // already asserted in implLookupClient
+ return 0;
+
+ if ( _rxListener.is() )
+ aClientPos->second.addInterface( aGuard, _rxListener );
+
+ return aClientPos->second.getLength(aGuard);
+}
+
+sal_Int32 AccessibleEventNotifier::removeEventListener(
+ const TClientId _nClient, const Reference< XAccessibleEventListener >& _rxListener )
+{
+ std::unique_lock aGuard( GetLocalMutex() );
+
+ ClientMap::iterator aClientPos;
+ if ( !implLookupClient( _nClient, aClientPos ) )
+ // already asserted in implLookupClient
+ return 0;
+
+ if ( _rxListener.is() )
+ aClientPos->second.removeInterface( aGuard, _rxListener );
+
+ return aClientPos->second.getLength(aGuard);
+}
+
+void AccessibleEventNotifier::addEvent( const TClientId _nClient, const AccessibleEventObject& _rEvent )
+{
+ std::unique_lock aGuard( GetLocalMutex() );
+
+ ClientMap::iterator aClientPos;
+ if ( !implLookupClient( _nClient, aClientPos ) )
+ // already asserted in implLookupClient
+ return;
+
+ // since we're synchronous, again, we want to notify immediately
+ OInterfaceIteratorHelper4 aIt(aGuard, aClientPos->second);
+ // no need to hold lock here, and we don't want to hold lock while calling listeners
+ aGuard.unlock();
+ while (aIt.hasMoreElements())
+ {
+ try
+ {
+ aIt.next()->notifyEvent(_rEvent);
+ }
+ catch (Exception&)
+ {
+ // no assertion, because a broken access remote bridge or something like this
+ // can cause this exception
+ }
+ }
+}
+
+void AccessibleEventNotifier::shutdown()
+{
+ gaClients.clear();
+}
+
+} // namespace comphelper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/accessiblekeybindinghelper.cxx b/comphelper/source/misc/accessiblekeybindinghelper.cxx
new file mode 100644
index 0000000000..d1db69b98f
--- /dev/null
+++ b/comphelper/source/misc/accessiblekeybindinghelper.cxx
@@ -0,0 +1,97 @@
+/* -*- 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/lang/IndexOutOfBoundsException.hpp>
+#include <comphelper/accessiblekeybindinghelper.hxx>
+#include <o3tl/safeint.hxx>
+
+
+namespace comphelper
+{
+
+
+ using namespace ::com::sun::star; // MT 04/2003: was ::drafts::com::sun::star - otherwise too many changes
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::accessibility;
+
+
+ // OAccessibleKeyBindingHelper
+
+
+ OAccessibleKeyBindingHelper::OAccessibleKeyBindingHelper()
+ {
+ }
+
+
+ OAccessibleKeyBindingHelper::OAccessibleKeyBindingHelper( const OAccessibleKeyBindingHelper& rHelper )
+ : cppu::WeakImplHelper<XAccessibleKeyBinding>( rHelper )
+ , m_aKeyBindings( rHelper.m_aKeyBindings )
+ {
+ }
+
+
+ OAccessibleKeyBindingHelper::~OAccessibleKeyBindingHelper()
+ {
+ }
+
+
+ void OAccessibleKeyBindingHelper::AddKeyBinding( const Sequence< awt::KeyStroke >& rKeyBinding )
+ {
+ std::scoped_lock aGuard( m_aMutex );
+
+ m_aKeyBindings.push_back( rKeyBinding );
+ }
+
+
+ void OAccessibleKeyBindingHelper::AddKeyBinding( const awt::KeyStroke& rKeyStroke )
+ {
+ std::scoped_lock aGuard( m_aMutex );
+ m_aKeyBindings.push_back( { rKeyStroke } );
+ }
+
+
+ // XAccessibleKeyBinding
+
+
+ sal_Int32 OAccessibleKeyBindingHelper::getAccessibleKeyBindingCount()
+ {
+ std::scoped_lock aGuard( m_aMutex );
+
+ return m_aKeyBindings.size();
+ }
+
+
+ Sequence< awt::KeyStroke > OAccessibleKeyBindingHelper::getAccessibleKeyBinding( sal_Int32 nIndex )
+ {
+ std::scoped_lock aGuard( m_aMutex );
+
+ if ( nIndex < 0 || o3tl::make_unsigned(nIndex) >= m_aKeyBindings.size() )
+ throw IndexOutOfBoundsException();
+
+ return m_aKeyBindings[nIndex];
+ }
+
+
+} // namespace comphelper
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/accessibleselectionhelper.cxx b/comphelper/source/misc/accessibleselectionhelper.cxx
new file mode 100644
index 0000000000..67ce5aadd1
--- /dev/null
+++ b/comphelper/source/misc/accessibleselectionhelper.cxx
@@ -0,0 +1,168 @@
+/* -*- 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 <comphelper/accessiblecontexthelper.hxx>
+#include <comphelper/accessibleselectionhelper.hxx>
+
+
+namespace comphelper
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::awt;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::accessibility;
+
+ OCommonAccessibleSelection::OCommonAccessibleSelection( )
+ {
+ }
+
+ OCommonAccessibleSelection::~OCommonAccessibleSelection() {}
+
+
+ void OCommonAccessibleSelection::selectAccessibleChild( sal_Int64 nChildIndex )
+ {
+ implSelect( nChildIndex, true );
+ }
+
+
+ bool OCommonAccessibleSelection::isAccessibleChildSelected( sal_Int64 nChildIndex )
+ {
+ return implIsSelected( nChildIndex );
+ }
+
+
+ void OCommonAccessibleSelection::clearAccessibleSelection( )
+ {
+ implSelect( ACCESSIBLE_SELECTION_CHILD_ALL, false );
+ }
+
+
+ void OCommonAccessibleSelection::selectAllAccessibleChildren( )
+ {
+ implSelect( ACCESSIBLE_SELECTION_CHILD_ALL, true );
+ }
+
+
+ sal_Int64 OCommonAccessibleSelection::getSelectedAccessibleChildCount( )
+ {
+ sal_Int64 nRet = 0;
+ Reference< XAccessibleContext > xParentContext( implGetAccessibleContext() );
+
+ OSL_ENSURE( xParentContext.is(), "OCommonAccessibleSelection::getSelectedAccessibleChildCount: no parent context!" );
+
+ if( xParentContext.is() )
+ {
+ for( sal_Int64 i = 0, nChildCount = xParentContext->getAccessibleChildCount(); i < nChildCount; i++ )
+ if( implIsSelected( i ) )
+ ++nRet;
+ }
+
+ return nRet;
+ }
+
+
+ Reference< XAccessible > OCommonAccessibleSelection::getSelectedAccessibleChild( sal_Int64 nSelectedChildIndex )
+ {
+ Reference< XAccessible > xRet;
+ Reference< XAccessibleContext > xParentContext( implGetAccessibleContext() );
+
+ OSL_ENSURE( xParentContext.is(), "OCommonAccessibleSelection::getSelectedAccessibleChildCount: no parent context!" );
+
+ if( xParentContext.is() )
+ {
+ for( sal_Int64 i = 0, nChildCount = xParentContext->getAccessibleChildCount(), nPos = 0; ( i < nChildCount ) && !xRet.is(); i++ )
+ if( implIsSelected( i ) && ( nPos++ == nSelectedChildIndex ) )
+ xRet = xParentContext->getAccessibleChild( i );
+ }
+
+ return xRet;
+ }
+
+
+ void OCommonAccessibleSelection::deselectAccessibleChild( sal_Int64 nSelectedChildIndex )
+ {
+ implSelect( nSelectedChildIndex, false );
+ }
+
+ OAccessibleSelectionHelper::OAccessibleSelectionHelper()
+ {
+ }
+
+
+ Reference< XAccessibleContext > OAccessibleSelectionHelper::implGetAccessibleContext()
+ {
+ return this;
+ }
+
+
+ void SAL_CALL OAccessibleSelectionHelper::selectAccessibleChild( sal_Int64 nChildIndex )
+ {
+ OExternalLockGuard aGuard( this );
+ OCommonAccessibleSelection::selectAccessibleChild( nChildIndex );
+ }
+
+
+ sal_Bool SAL_CALL OAccessibleSelectionHelper::isAccessibleChildSelected( sal_Int64 nChildIndex )
+ {
+ OExternalLockGuard aGuard( this );
+ return OCommonAccessibleSelection::isAccessibleChildSelected( nChildIndex );
+ }
+
+
+ void SAL_CALL OAccessibleSelectionHelper::clearAccessibleSelection( )
+ {
+ OExternalLockGuard aGuard( this );
+ OCommonAccessibleSelection::clearAccessibleSelection();
+ }
+
+
+ void SAL_CALL OAccessibleSelectionHelper::selectAllAccessibleChildren( )
+ {
+ OExternalLockGuard aGuard( this );
+ OCommonAccessibleSelection::selectAllAccessibleChildren();
+ }
+
+
+ sal_Int64 SAL_CALL OAccessibleSelectionHelper::getSelectedAccessibleChildCount( )
+ {
+ OExternalLockGuard aGuard( this );
+ return OCommonAccessibleSelection::getSelectedAccessibleChildCount();
+ }
+
+
+ Reference< XAccessible > SAL_CALL OAccessibleSelectionHelper::getSelectedAccessibleChild( sal_Int64 nSelectedChildIndex )
+ {
+ OExternalLockGuard aGuard( this );
+ return OCommonAccessibleSelection::getSelectedAccessibleChild( nSelectedChildIndex );
+ }
+
+
+ void SAL_CALL OAccessibleSelectionHelper::deselectAccessibleChild( sal_Int64 nSelectedChildIndex )
+ {
+ OExternalLockGuard aGuard( this );
+ OCommonAccessibleSelection::deselectAccessibleChild( nSelectedChildIndex );
+ }
+
+
+} // namespace comphelper
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/accessibletexthelper.cxx b/comphelper/source/misc/accessibletexthelper.cxx
new file mode 100644
index 0000000000..06752ba88d
--- /dev/null
+++ b/comphelper/source/misc/accessibletexthelper.cxx
@@ -0,0 +1,798 @@
+/* -*- 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 <comphelper/accessiblecontexthelper.hxx>
+#include <comphelper/accessibletexthelper.hxx>
+#include <com/sun/star/accessibility/AccessibleTextType.hpp>
+#include <com/sun/star/i18n/BreakIterator.hpp>
+#include <com/sun/star/i18n/CharacterIteratorMode.hpp>
+#include <com/sun/star/i18n/CharacterClassification.hpp>
+#include <com/sun/star/i18n/WordType.hpp>
+#include <com/sun/star/i18n/KCharacterType.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/accessibility/TextSegment.hpp>
+
+#include <algorithm>
+
+
+namespace comphelper
+{
+
+
+ using namespace ::com::sun::star;
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::accessibility;
+
+
+ // OCommonAccessibleText
+
+
+ OCommonAccessibleText::OCommonAccessibleText()
+ {
+ }
+
+
+ OCommonAccessibleText::~OCommonAccessibleText()
+ {
+ }
+
+
+ Reference < i18n::XBreakIterator > const & OCommonAccessibleText::implGetBreakIterator()
+ {
+ if ( !m_xBreakIter.is() )
+ {
+ Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ m_xBreakIter = i18n::BreakIterator::create(xContext);
+ }
+
+ return m_xBreakIter;
+ }
+
+
+ Reference < i18n::XCharacterClassification > const & OCommonAccessibleText::implGetCharacterClassification()
+ {
+ if ( !m_xCharClass.is() )
+ {
+ m_xCharClass = i18n::CharacterClassification::create( ::comphelper::getProcessComponentContext() );
+ }
+
+ return m_xCharClass;
+ }
+
+
+ bool OCommonAccessibleText::implIsValidBoundary( i18n::Boundary const & rBoundary, sal_Int32 nLength )
+ {
+ return ( rBoundary.startPos >= 0 ) && ( rBoundary.startPos < nLength ) && ( rBoundary.endPos >= 0 ) && ( rBoundary.endPos <= nLength );
+ }
+
+
+ bool OCommonAccessibleText::implIsValidIndex( sal_Int32 nIndex, sal_Int32 nLength )
+ {
+ return ( nIndex >= 0 ) && ( nIndex < nLength );
+ }
+
+
+ bool OCommonAccessibleText::implIsValidRange( sal_Int32 nStartIndex, sal_Int32 nEndIndex, sal_Int32 nLength )
+ {
+ return ( nStartIndex >= 0 ) && ( nStartIndex <= nLength ) && ( nEndIndex >= 0 ) && ( nEndIndex <= nLength );
+ }
+
+
+ void OCommonAccessibleText::implGetGlyphBoundary( const OUString& rText, i18n::Boundary& rBoundary, sal_Int32 nIndex )
+ {
+ if ( implIsValidIndex( nIndex, rText.getLength() ) )
+ {
+ Reference < i18n::XBreakIterator > xBreakIter = implGetBreakIterator();
+ if ( xBreakIter.is() )
+ {
+ sal_Int32 nCount = 1;
+ sal_Int32 nDone;
+ sal_Int32 nStartIndex = xBreakIter->previousCharacters( rText, nIndex, implGetLocale(), i18n::CharacterIteratorMode::SKIPCELL, nCount, nDone );
+ if ( nDone != 0 )
+ nStartIndex = xBreakIter->nextCharacters( rText, nStartIndex, implGetLocale(), i18n::CharacterIteratorMode::SKIPCELL, nCount, nDone );
+ sal_Int32 nEndIndex = xBreakIter->nextCharacters( rText, nStartIndex, implGetLocale(), i18n::CharacterIteratorMode::SKIPCELL, nCount, nDone );
+ if ( nDone != 0 )
+ {
+ rBoundary.startPos = nStartIndex;
+ rBoundary.endPos = nEndIndex;
+ }
+ }
+ }
+ else
+ {
+ rBoundary.startPos = nIndex;
+ rBoundary.endPos = nIndex;
+ }
+ }
+
+
+ bool OCommonAccessibleText::implGetWordBoundary( const OUString& rText, i18n::Boundary& rBoundary, sal_Int32 nIndex )
+ {
+ bool bWord = false;
+
+ if ( implIsValidIndex( nIndex, rText.getLength() ) )
+ {
+ Reference < i18n::XBreakIterator > xBreakIter = implGetBreakIterator();
+ if ( xBreakIter.is() )
+ {
+ rBoundary = xBreakIter->getWordBoundary( rText, nIndex, implGetLocale(), i18n::WordType::ANY_WORD, true );
+
+ // it's a word, if the first character is an alpha-numeric character
+ Reference< i18n::XCharacterClassification > xCharClass = implGetCharacterClassification();
+ if ( xCharClass.is() )
+ {
+ sal_Int32 nType = xCharClass->getCharacterType( rText, rBoundary.startPos, implGetLocale() );
+ if ( ( nType & ( i18n::KCharacterType::LETTER | i18n::KCharacterType::DIGIT ) ) != 0 )
+ bWord = true;
+ }
+ }
+ }
+ else
+ {
+ rBoundary.startPos = nIndex;
+ rBoundary.endPos = nIndex;
+ }
+
+ return bWord;
+ }
+
+
+ void OCommonAccessibleText::implGetSentenceBoundary( const OUString& rText, i18n::Boundary& rBoundary, sal_Int32 nIndex )
+ {
+ if ( implIsValidIndex( nIndex, rText.getLength() ) )
+ {
+ Locale aLocale = implGetLocale();
+ Reference < i18n::XBreakIterator > xBreakIter = implGetBreakIterator();
+ if ( xBreakIter.is() )
+ {
+ rBoundary.endPos = xBreakIter->endOfSentence( rText, nIndex, aLocale );
+ rBoundary.startPos = xBreakIter->beginOfSentence( rText, rBoundary.endPos, aLocale );
+ }
+ }
+ else
+ {
+ rBoundary.startPos = nIndex;
+ rBoundary.endPos = nIndex;
+ }
+ }
+
+
+ void OCommonAccessibleText::implGetParagraphBoundary( const OUString& rText, i18n::Boundary& rBoundary, sal_Int32 nIndex )
+ {
+ if ( implIsValidIndex( nIndex, rText.getLength() ) )
+ {
+ rBoundary.startPos = 0;
+ rBoundary.endPos = rText.getLength();
+
+ sal_Int32 nFound = rText.lastIndexOf( '\n', nIndex );
+ if ( nFound != -1 )
+ rBoundary.startPos = nFound + 1;
+
+ nFound = rText.indexOf( '\n', nIndex );
+ if ( nFound != -1 )
+ rBoundary.endPos = nFound + 1;
+ }
+ else
+ {
+ rBoundary.startPos = nIndex;
+ rBoundary.endPos = nIndex;
+ }
+ }
+
+
+ void OCommonAccessibleText::implGetLineBoundary( const OUString& rText, i18n::Boundary& rBoundary, sal_Int32 nIndex )
+ {
+ sal_Int32 nLength = rText.getLength();
+
+ if ( implIsValidIndex( nIndex, nLength ) || nIndex == nLength )
+ {
+ rBoundary.startPos = 0;
+ rBoundary.endPos = nLength;
+ }
+ else
+ {
+ rBoundary.startPos = nIndex;
+ rBoundary.endPos = nIndex;
+ }
+ }
+
+
+ sal_Unicode OCommonAccessibleText::implGetCharacter( std::u16string_view rText, sal_Int32 nIndex )
+ {
+ if ( !implIsValidIndex( nIndex, rText.size() ) )
+ throw IndexOutOfBoundsException();
+
+ return rText[nIndex];
+ }
+
+ OUString OCommonAccessibleText::getSelectedText()
+ {
+ OUString sText;
+ sal_Int32 nStartIndex;
+ sal_Int32 nEndIndex;
+
+ implGetSelection( nStartIndex, nEndIndex );
+
+ try
+ {
+ sText = implGetTextRange( implGetText(), nStartIndex, nEndIndex );
+ }
+ catch ( IndexOutOfBoundsException& )
+ {
+ }
+
+ return sText;
+ }
+
+
+ sal_Int32 OCommonAccessibleText::getSelectionStart()
+ {
+ sal_Int32 nStartIndex;
+ sal_Int32 nEndIndex;
+
+ implGetSelection( nStartIndex, nEndIndex );
+
+ return nStartIndex;
+ }
+
+
+ sal_Int32 OCommonAccessibleText::getSelectionEnd()
+ {
+ sal_Int32 nStartIndex;
+ sal_Int32 nEndIndex;
+
+ implGetSelection( nStartIndex, nEndIndex );
+
+ return nEndIndex;
+ }
+
+
+ OUString OCommonAccessibleText::implGetTextRange( std::u16string_view rText, sal_Int32 nStartIndex, sal_Int32 nEndIndex )
+ {
+
+ if ( !implIsValidRange( nStartIndex, nEndIndex, rText.size() ) )
+ throw IndexOutOfBoundsException();
+
+ sal_Int32 nMinIndex = std::min( nStartIndex, nEndIndex );
+ sal_Int32 nMaxIndex = std::max( nStartIndex, nEndIndex );
+
+ return OUString(rText.substr( nMinIndex, nMaxIndex - nMinIndex ));
+ }
+
+ TextSegment OCommonAccessibleText::getTextAtIndex( sal_Int32 nIndex, sal_Int16 aTextType )
+ {
+ OUString sText( implGetText() );
+ sal_Int32 nLength = sText.getLength();
+
+ if ( !implIsValidIndex( nIndex, nLength ) && nIndex != nLength )
+ throw IndexOutOfBoundsException();
+
+ i18n::Boundary aBoundary;
+ TextSegment aResult;
+ aResult.SegmentStart = -1;
+ aResult.SegmentEnd = -1;
+
+ switch ( aTextType )
+ {
+ case AccessibleTextType::CHARACTER:
+ {
+ if ( implIsValidIndex( nIndex, nLength ) )
+ {
+ auto nIndexEnd = nIndex;
+ sText.iterateCodePoints(&nIndexEnd);
+
+ aResult.SegmentText = sText.copy( nIndex, nIndexEnd - nIndex );
+ aResult.SegmentStart = nIndex;
+ aResult.SegmentEnd = nIndexEnd;
+ }
+ }
+ break;
+ case AccessibleTextType::GLYPH:
+ {
+ // get glyph at index
+ implGetGlyphBoundary( sText, aBoundary, nIndex );
+ if ( implIsValidBoundary( aBoundary, nLength ) )
+ {
+ aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
+ aResult.SegmentStart = aBoundary.startPos;
+ aResult.SegmentEnd = aBoundary.endPos;
+ }
+ }
+ break;
+ case AccessibleTextType::WORD:
+ {
+ // get word at index
+ bool bWord = implGetWordBoundary( sText, aBoundary, nIndex );
+ if ( bWord && implIsValidBoundary( aBoundary, nLength ) )
+ {
+ aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
+ aResult.SegmentStart = aBoundary.startPos;
+ aResult.SegmentEnd = aBoundary.endPos;
+ }
+ }
+ break;
+ case AccessibleTextType::SENTENCE:
+ {
+ // get sentence at index
+ implGetSentenceBoundary( sText, aBoundary, nIndex );
+ if ( implIsValidBoundary( aBoundary, nLength ) )
+ {
+ aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
+ aResult.SegmentStart = aBoundary.startPos;
+ aResult.SegmentEnd = aBoundary.endPos;
+ }
+ }
+ break;
+ case AccessibleTextType::PARAGRAPH:
+ {
+ // get paragraph at index
+ implGetParagraphBoundary( sText, aBoundary, nIndex );
+ if ( implIsValidBoundary( aBoundary, nLength ) )
+ {
+ aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
+ aResult.SegmentStart = aBoundary.startPos;
+ aResult.SegmentEnd = aBoundary.endPos;
+ }
+ }
+ break;
+ case AccessibleTextType::LINE:
+ {
+ // get line at index
+ implGetLineBoundary( sText, aBoundary, nIndex );
+ if ( implIsValidBoundary( aBoundary, nLength ) )
+ {
+ aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
+ aResult.SegmentStart = aBoundary.startPos;
+ aResult.SegmentEnd = aBoundary.endPos;
+ }
+ }
+ break;
+ case AccessibleTextType::ATTRIBUTE_RUN:
+ {
+ // TODO: implGetAttributeRunBoundary() (incompatible!)
+
+ aResult.SegmentText = sText;
+ aResult.SegmentStart = 0;
+ aResult.SegmentEnd = nLength;
+ }
+ break;
+ default:
+ {
+ // unknown text type
+ }
+ }
+
+ return aResult;
+ }
+
+
+ TextSegment OCommonAccessibleText::getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 aTextType )
+ {
+ OUString sText( implGetText() );
+ sal_Int32 nLength = sText.getLength();
+
+ if ( !implIsValidIndex( nIndex, nLength ) && nIndex != nLength )
+ throw IndexOutOfBoundsException();
+
+ i18n::Boundary aBoundary;
+ TextSegment aResult;
+ aResult.SegmentStart = -1;
+ aResult.SegmentEnd = -1;
+
+ switch ( aTextType )
+ {
+ case AccessibleTextType::CHARACTER:
+ {
+ if ( implIsValidIndex( nIndex - 1, nLength ) )
+ {
+ sText.iterateCodePoints(&nIndex, -1);
+ auto nIndexEnd = nIndex;
+ sText.iterateCodePoints(&nIndexEnd);
+ aResult.SegmentText = sText.copy(nIndex, nIndexEnd - nIndex);
+ aResult.SegmentStart = nIndex;
+ aResult.SegmentEnd = nIndexEnd;
+ }
+ }
+ break;
+ case AccessibleTextType::GLYPH:
+ {
+ // get glyph at index
+ implGetGlyphBoundary( sText, aBoundary, nIndex );
+ // get previous glyph
+ if ( aBoundary.startPos > 0 )
+ {
+ implGetGlyphBoundary( sText, aBoundary, aBoundary.startPos - 1 );
+ if ( implIsValidBoundary( aBoundary, nLength ) )
+ {
+ aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
+ aResult.SegmentStart = aBoundary.startPos;
+ aResult.SegmentEnd = aBoundary.endPos;
+ }
+ }
+ }
+ break;
+ case AccessibleTextType::WORD:
+ {
+ // get word at index
+ implGetWordBoundary( sText, aBoundary, nIndex );
+ // get previous word
+ bool bWord = false;
+ while ( !bWord && aBoundary.startPos > 0 )
+ bWord = implGetWordBoundary( sText, aBoundary, aBoundary.startPos - 1 );
+ if ( bWord && implIsValidBoundary( aBoundary, nLength ) )
+ {
+ aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
+ aResult.SegmentStart = aBoundary.startPos;
+ aResult.SegmentEnd = aBoundary.endPos;
+ }
+ }
+ break;
+ case AccessibleTextType::SENTENCE:
+ {
+ // get sentence at index
+ implGetSentenceBoundary( sText, aBoundary, nIndex );
+ // get previous sentence
+ if ( aBoundary.startPos > 0 )
+ {
+ implGetSentenceBoundary( sText, aBoundary, aBoundary.startPos - 1 );
+ if ( implIsValidBoundary( aBoundary, nLength ) )
+ {
+ aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
+ aResult.SegmentStart = aBoundary.startPos;
+ aResult.SegmentEnd = aBoundary.endPos;
+ }
+ }
+ }
+ break;
+ case AccessibleTextType::PARAGRAPH:
+ {
+ // get paragraph at index
+ implGetParagraphBoundary( sText, aBoundary, nIndex );
+ // get previous paragraph
+ if ( aBoundary.startPos > 0 )
+ {
+ implGetParagraphBoundary( sText, aBoundary, aBoundary.startPos - 1 );
+ if ( implIsValidBoundary( aBoundary, nLength ) )
+ {
+ aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
+ aResult.SegmentStart = aBoundary.startPos;
+ aResult.SegmentEnd = aBoundary.endPos;
+ }
+ }
+ }
+ break;
+ case AccessibleTextType::LINE:
+ {
+ // get line at index
+ implGetLineBoundary( sText, aBoundary, nIndex );
+ // get previous line
+ if ( aBoundary.startPos > 0 )
+ {
+ implGetLineBoundary( sText, aBoundary, aBoundary.startPos - 1 );
+ if ( implIsValidBoundary( aBoundary, nLength ) )
+ {
+ aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
+ aResult.SegmentStart = aBoundary.startPos;
+ aResult.SegmentEnd = aBoundary.endPos;
+ }
+ }
+ }
+ break;
+ case AccessibleTextType::ATTRIBUTE_RUN:
+ {
+ // TODO: implGetAttributeRunBoundary() (incompatible!)
+ }
+ break;
+ default:
+ {
+ // unknown text type
+ }
+ }
+
+ return aResult;
+ }
+
+
+ TextSegment OCommonAccessibleText::getTextBehindIndex( sal_Int32 nIndex, sal_Int16 aTextType )
+ {
+ OUString sText( implGetText() );
+ sal_Int32 nLength = sText.getLength();
+
+ if ( !implIsValidIndex( nIndex, nLength ) && nIndex != nLength )
+ throw IndexOutOfBoundsException();
+
+ i18n::Boundary aBoundary;
+ TextSegment aResult;
+ aResult.SegmentStart = -1;
+ aResult.SegmentEnd = -1;
+
+ switch ( aTextType )
+ {
+ case AccessibleTextType::CHARACTER:
+ {
+ if ( implIsValidIndex( nIndex + 1, nLength ) )
+ {
+ sText.iterateCodePoints(&nIndex);
+ auto nIndexEnd = nIndex;
+ sText.iterateCodePoints(&nIndexEnd);
+ aResult.SegmentText = sText.copy(nIndex, nIndexEnd - nIndex);
+ aResult.SegmentStart = nIndex;
+ aResult.SegmentEnd = nIndexEnd;
+ }
+ }
+ break;
+ case AccessibleTextType::GLYPH:
+ {
+ // get glyph at index
+ implGetGlyphBoundary( sText, aBoundary, nIndex );
+ // get next glyph
+ if ( aBoundary.endPos < nLength )
+ {
+ implGetGlyphBoundary( sText, aBoundary, aBoundary.endPos );
+ if ( implIsValidBoundary( aBoundary, nLength ) )
+ {
+ aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
+ aResult.SegmentStart = aBoundary.startPos;
+ aResult.SegmentEnd = aBoundary.endPos;
+ }
+ }
+ }
+ break;
+ case AccessibleTextType::WORD:
+ {
+ // get word at index
+ implGetWordBoundary( sText, aBoundary, nIndex );
+ // get next word
+ bool bWord = false;
+ while ( !bWord && aBoundary.endPos < nLength )
+ bWord = implGetWordBoundary( sText, aBoundary, aBoundary.endPos );
+ if ( bWord && implIsValidBoundary( aBoundary, nLength ) )
+ {
+ aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
+ aResult.SegmentStart = aBoundary.startPos;
+ aResult.SegmentEnd = aBoundary.endPos;
+ }
+ }
+ break;
+ case AccessibleTextType::SENTENCE:
+ {
+ // get sentence at index
+ implGetSentenceBoundary( sText, aBoundary, nIndex );
+ // get next sentence
+ sal_Int32 nEnd = aBoundary.endPos;
+ sal_Int32 nI = aBoundary.endPos;
+ bool bFound = false;
+ while ( !bFound && ++nI < nLength )
+ {
+ implGetSentenceBoundary( sText, aBoundary, nI );
+ bFound = ( aBoundary.endPos > nEnd );
+ }
+ if ( bFound && implIsValidBoundary( aBoundary, nLength ) )
+ {
+ aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
+ aResult.SegmentStart = aBoundary.startPos;
+ aResult.SegmentEnd = aBoundary.endPos;
+ }
+ }
+ break;
+ case AccessibleTextType::PARAGRAPH:
+ {
+ // get paragraph at index
+ implGetParagraphBoundary( sText, aBoundary, nIndex );
+ // get next paragraph
+ if ( aBoundary.endPos < nLength )
+ {
+ implGetParagraphBoundary( sText, aBoundary, aBoundary.endPos );
+ if ( implIsValidBoundary( aBoundary, nLength ) )
+ {
+ aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
+ aResult.SegmentStart = aBoundary.startPos;
+ aResult.SegmentEnd = aBoundary.endPos;
+ }
+ }
+ }
+ break;
+ case AccessibleTextType::LINE:
+ {
+ // get line at index
+ implGetLineBoundary( sText, aBoundary, nIndex );
+ // get next line
+ if ( aBoundary.endPos < nLength )
+ {
+ implGetLineBoundary( sText, aBoundary, aBoundary.endPos );
+ if ( implIsValidBoundary( aBoundary, nLength ) )
+ {
+ aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
+ aResult.SegmentStart = aBoundary.startPos;
+ aResult.SegmentEnd = aBoundary.endPos;
+ }
+ }
+ }
+ break;
+ case AccessibleTextType::ATTRIBUTE_RUN:
+ {
+ // TODO: implGetAttributeRunBoundary() (incompatible!)
+ }
+ break;
+ default:
+ {
+ // unknown text type
+ }
+ }
+
+ return aResult;
+ }
+
+
+ bool OCommonAccessibleText::implInitTextChangedEvent(
+ std::u16string_view rOldString,
+ std::u16string_view rNewString,
+ css::uno::Any& rDeleted,
+ css::uno::Any& rInserted) // throw()
+ {
+ size_t nLenOld = rOldString.size();
+ size_t nLenNew = rNewString.size();
+
+ // equal
+ if ((0 == nLenOld) && (0 == nLenNew))
+ return false;
+
+ TextSegment aDeletedText;
+ TextSegment aInsertedText;
+
+ aDeletedText.SegmentStart = -1;
+ aDeletedText.SegmentEnd = -1;
+ aInsertedText.SegmentStart = -1;
+ aInsertedText.SegmentEnd = -1;
+
+ // insert only
+ if ((0 == nLenOld) && (nLenNew > 0))
+ {
+ aInsertedText.SegmentStart = 0;
+ aInsertedText.SegmentEnd = nLenNew;
+ aInsertedText.SegmentText = rNewString.substr( aInsertedText.SegmentStart, aInsertedText.SegmentEnd - aInsertedText.SegmentStart );
+
+ rInserted <<= aInsertedText;
+ return true;
+ }
+
+ // delete only
+ if ((nLenOld > 0) && (0 == nLenNew))
+ {
+ aDeletedText.SegmentStart = 0;
+ aDeletedText.SegmentEnd = nLenOld;
+ aDeletedText.SegmentText = rOldString.substr( aDeletedText.SegmentStart, aDeletedText.SegmentEnd - aDeletedText.SegmentStart );
+
+ rDeleted <<= aDeletedText;
+ return true;
+ }
+
+ auto pFirstDiffOld = rOldString.begin();
+ auto pLastDiffOld = rOldString.end();
+ auto pFirstDiffNew = rNewString.begin();
+ auto pLastDiffNew = rNewString.end();
+
+ // find first difference
+ while ((pFirstDiffOld < pLastDiffOld) && (pFirstDiffNew < pLastDiffNew)
+ && (*pFirstDiffOld == *pFirstDiffNew))
+ {
+ pFirstDiffOld++;
+ pFirstDiffNew++;
+ }
+
+ // equality test
+ if (pFirstDiffOld == pLastDiffOld && pFirstDiffNew == pLastDiffNew)
+ return false;
+
+ // find last difference
+ while ( ( pLastDiffOld > pFirstDiffOld) &&
+ ( pLastDiffNew > pFirstDiffNew) &&
+ (pLastDiffOld[-1] == pLastDiffNew[-1]))
+ {
+ pLastDiffOld--;
+ pLastDiffNew--;
+ }
+
+ if (pFirstDiffOld < pLastDiffOld)
+ {
+ aDeletedText.SegmentStart = pFirstDiffOld - rOldString.begin();
+ aDeletedText.SegmentEnd = pLastDiffOld - rOldString.begin();
+ aDeletedText.SegmentText = rOldString.substr( aDeletedText.SegmentStart, aDeletedText.SegmentEnd - aDeletedText.SegmentStart );
+
+ rDeleted <<= aDeletedText;
+ }
+
+ if (pFirstDiffNew < pLastDiffNew)
+ {
+ aInsertedText.SegmentStart = pFirstDiffNew - rNewString.begin();
+ aInsertedText.SegmentEnd = pLastDiffNew - rNewString.begin();
+ aInsertedText.SegmentText = rNewString.substr( aInsertedText.SegmentStart, aInsertedText.SegmentEnd - aInsertedText.SegmentStart );
+
+ rInserted <<= aInsertedText;
+ }
+ return true;
+ }
+
+
+ // OAccessibleTextHelper
+
+
+ OAccessibleTextHelper::OAccessibleTextHelper( )
+ {
+ }
+
+
+ // XAccessibleText
+
+
+ OUString OAccessibleTextHelper::getSelectedText()
+ {
+ OExternalLockGuard aGuard( this );
+
+ return OCommonAccessibleText::getSelectedText();
+ }
+
+
+ sal_Int32 OAccessibleTextHelper::getSelectionStart()
+ {
+ OExternalLockGuard aGuard( this );
+
+ return OCommonAccessibleText::getSelectionStart();
+ }
+
+
+ sal_Int32 OAccessibleTextHelper::getSelectionEnd()
+ {
+ OExternalLockGuard aGuard( this );
+
+ return OCommonAccessibleText::getSelectionEnd();
+ }
+
+
+ TextSegment OAccessibleTextHelper::getTextAtIndex( sal_Int32 nIndex, sal_Int16 aTextType )
+ {
+ OExternalLockGuard aGuard( this );
+
+ return OCommonAccessibleText::getTextAtIndex( nIndex, aTextType );
+ }
+
+
+ TextSegment OAccessibleTextHelper::getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 aTextType )
+ {
+ OExternalLockGuard aGuard( this );
+
+ return OCommonAccessibleText::getTextBeforeIndex( nIndex, aTextType );
+ }
+
+
+ TextSegment OAccessibleTextHelper::getTextBehindIndex( sal_Int32 nIndex, sal_Int16 aTextType )
+ {
+ OExternalLockGuard aGuard( this );
+
+ return OCommonAccessibleText::getTextBehindIndex( nIndex, aTextType );
+ }
+
+
+} // namespace comphelper
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/accessiblewrapper.cxx b/comphelper/source/misc/accessiblewrapper.cxx
new file mode 100644
index 0000000000..3e356f434f
--- /dev/null
+++ b/comphelper/source/misc/accessiblewrapper.cxx
@@ -0,0 +1,632 @@
+/* -*- 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 <comphelper/accessiblewrapper.hxx>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+
+using namespace ::comphelper;
+using namespace ::com::sun::star::accessibility;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+
+
+namespace comphelper
+{
+ OWrappedAccessibleChildrenManager::OWrappedAccessibleChildrenManager( const Reference< XComponentContext >& _rxContext )
+ :m_xContext( _rxContext )
+ ,m_bTransientChildren( true )
+ {
+ }
+
+
+ OWrappedAccessibleChildrenManager::~OWrappedAccessibleChildrenManager( )
+ {
+ }
+
+
+ void OWrappedAccessibleChildrenManager::setTransientChildren( bool _bSet )
+ {
+ m_bTransientChildren = _bSet;
+ }
+
+
+ void OWrappedAccessibleChildrenManager::setOwningAccessible( const Reference< XAccessible >& _rxAcc )
+ {
+ OSL_ENSURE( !m_aOwningAccessible.get().is(), "OWrappedAccessibleChildrenManager::setOwningAccessible: to be called only once!" );
+ m_aOwningAccessible = WeakReference< XAccessible >( _rxAcc );
+ }
+
+
+ void OWrappedAccessibleChildrenManager::removeFromCache( const Reference< XAccessible >& _rxKey )
+ {
+ AccessibleMap::iterator aRemovedPos = m_aChildrenMap.find( _rxKey );
+ if ( m_aChildrenMap.end() != aRemovedPos )
+ { // it was cached
+ // remove ourself as event listener
+ Reference< XComponent > xComp( aRemovedPos->first, UNO_QUERY );
+ if( xComp.is() )
+ xComp->removeEventListener( this );
+ // and remove the entry from the map
+ m_aChildrenMap.erase( aRemovedPos );
+ }
+ }
+
+
+ void OWrappedAccessibleChildrenManager::invalidateAll( )
+ {
+ // remove as event listener from the map elements
+ for( const auto& rChild : m_aChildrenMap )
+ {
+ Reference< XComponent > xComp( rChild.first, UNO_QUERY );
+ if( xComp.is() )
+ xComp->removeEventListener( this );
+ }
+ // clear the map
+ m_aChildrenMap.clear();
+ }
+
+
+ Reference< XAccessible > OWrappedAccessibleChildrenManager::getAccessibleWrapperFor(
+ const Reference< XAccessible >& _rxKey )
+ {
+ rtl::Reference< OAccessibleWrapper > xValue;
+
+ if( !_rxKey.is() )
+ {
+ // fprintf( stderr, "It was this path that was crashing stuff\n" );
+ return xValue;
+ }
+
+ // do we have this child in the cache?
+ AccessibleMap::const_iterator aPos = m_aChildrenMap.find( _rxKey );
+ if ( m_aChildrenMap.end() != aPos )
+ {
+ xValue = aPos->second;
+ }
+ else
+ { // not found in the cache, and allowed to create
+ // -> new wrapper
+ xValue = new OAccessibleWrapper( m_xContext, _rxKey, m_aOwningAccessible );
+
+ // see if we do cache children
+ if ( !m_bTransientChildren )
+ {
+ if (!m_aChildrenMap.emplace( _rxKey, xValue ).second)
+ {
+ OSL_FAIL(
+ "OWrappedAccessibleChildrenManager::"
+ "getAccessibleWrapperFor: element was already"
+ " inserted!" );
+ }
+
+ // listen for disposals of inner children - this may happen when the inner context
+ // is the owner for the inner children (it will dispose these children, and of course
+ // not our wrapper for these children)
+ Reference< XComponent > xComp( _rxKey, UNO_QUERY );
+ if ( xComp.is() )
+ xComp->addEventListener( this );
+ }
+ }
+
+ return xValue;
+ }
+
+
+ void OWrappedAccessibleChildrenManager::dispose()
+ {
+ // dispose our children
+ for( const auto& rChild : m_aChildrenMap )
+ {
+ Reference< XComponent > xComp( rChild.first, UNO_QUERY );
+ if( xComp.is() )
+ xComp->removeEventListener( this );
+
+ Reference< XComponent > xContextComponent;
+ if( rChild.second.is() )
+ xContextComponent.set( rChild.second->getContextNoCreate(),
+ ::css::uno::UNO_QUERY );
+ if( xContextComponent.is() )
+ xContextComponent->dispose();
+ }
+
+ // clear our children
+ m_aChildrenMap.clear();
+ }
+
+
+ void OWrappedAccessibleChildrenManager::implTranslateChildEventValue( const Any& _rInValue, Any& _rOutValue )
+ {
+ _rOutValue.clear();
+ Reference< XAccessible > xChild;
+ if ( _rInValue >>= xChild )
+ _rOutValue <<= getAccessibleWrapperFor( xChild );
+ }
+
+
+ void OWrappedAccessibleChildrenManager::translateAccessibleEvent( const AccessibleEventObject& _rEvent, AccessibleEventObject& _rTranslatedEvent )
+ {
+ // just in case we can't translate some of the values:
+ _rTranslatedEvent.NewValue = _rEvent.NewValue;
+ _rTranslatedEvent.OldValue = _rEvent.OldValue;
+
+ switch ( _rEvent.EventId )
+ {
+ case AccessibleEventId::CHILD:
+ case AccessibleEventId::ACTIVE_DESCENDANT_CHANGED:
+ case AccessibleEventId::CONTROLLED_BY_RELATION_CHANGED:
+ case AccessibleEventId::CONTROLLER_FOR_RELATION_CHANGED:
+ case AccessibleEventId::LABEL_FOR_RELATION_CHANGED:
+ case AccessibleEventId::LABELED_BY_RELATION_CHANGED:
+ case AccessibleEventId::CONTENT_FLOWS_FROM_RELATION_CHANGED:
+ case AccessibleEventId::CONTENT_FLOWS_TO_RELATION_CHANGED:
+ // these are events where both the old and the new value contain child references
+ implTranslateChildEventValue( _rEvent.OldValue, _rTranslatedEvent.OldValue );
+ implTranslateChildEventValue( _rEvent.NewValue, _rTranslatedEvent.NewValue );
+ break;
+
+ case AccessibleEventId::NAME_CHANGED:
+ case AccessibleEventId::DESCRIPTION_CHANGED:
+ case AccessibleEventId::ACTION_CHANGED:
+ case AccessibleEventId::STATE_CHANGED:
+ case AccessibleEventId::BOUNDRECT_CHANGED:
+ case AccessibleEventId::INVALIDATE_ALL_CHILDREN:
+ case AccessibleEventId::SELECTION_CHANGED:
+ case AccessibleEventId::VISIBLE_DATA_CHANGED:
+ case AccessibleEventId::VALUE_CHANGED:
+ case AccessibleEventId::MEMBER_OF_RELATION_CHANGED:
+ case AccessibleEventId::CARET_CHANGED:
+ case AccessibleEventId::TEXT_CHANGED:
+ case AccessibleEventId::HYPERTEXT_CHANGED:
+ case AccessibleEventId::TABLE_CAPTION_CHANGED:
+ case AccessibleEventId::TABLE_COLUMN_DESCRIPTION_CHANGED:
+ case AccessibleEventId::TABLE_COLUMN_HEADER_CHANGED:
+ case AccessibleEventId::TABLE_MODEL_CHANGED:
+ case AccessibleEventId::TABLE_ROW_DESCRIPTION_CHANGED:
+ case AccessibleEventId::TABLE_ROW_HEADER_CHANGED:
+ case AccessibleEventId::TABLE_SUMMARY_CHANGED:
+ // these Ids are also missed: SUB_WINDOW_OF_RELATION_CHANGED & TEXT_ATTRIBUTE_CHANGED
+ case AccessibleEventId::TEXT_SELECTION_CHANGED:
+ // nothing to translate
+ break;
+
+ default:
+ OSL_FAIL( "OWrappedAccessibleChildrenManager::translateAccessibleEvent: unknown (or unexpected) event id!" );
+ break;
+ }
+ }
+
+
+ void OWrappedAccessibleChildrenManager::handleChildNotification( const AccessibleEventObject& _rEvent )
+ {
+ if ( AccessibleEventId::INVALIDATE_ALL_CHILDREN == _rEvent.EventId )
+ { // clear our child map
+ invalidateAll( );
+ }
+ else if ( AccessibleEventId::CHILD == _rEvent.EventId )
+ {
+ // check if the removed or replaced element is cached
+ Reference< XAccessible > xRemoved;
+ if ( _rEvent.OldValue >>= xRemoved )
+ removeFromCache( xRemoved );
+ }
+ }
+
+
+ void SAL_CALL OWrappedAccessibleChildrenManager::disposing( const EventObject& _rSource )
+ {
+ // this should come from one of the inner XAccessible's of our children
+ Reference< XAccessible > xSource( _rSource.Source, UNO_QUERY );
+ AccessibleMap::iterator aDisposedPos = m_aChildrenMap.find( xSource );
+#if OSL_DEBUG_LEVEL > 0
+ if ( m_aChildrenMap.end() == aDisposedPos )
+ {
+ OSL_FAIL( "OWrappedAccessibleChildrenManager::disposing: where did this come from?" );
+ // helper for diagnostics
+ Reference< XAccessible > xOwningAccessible( m_aOwningAccessible );
+ Reference< XAccessibleContext > xContext;
+ try
+ {
+ if ( xOwningAccessible.is() )
+ xContext = xOwningAccessible->getAccessibleContext();
+ if ( xContext.is() )
+ {
+ //TODO: do something
+ //OUString sName = xContext->getAccessibleName();
+ //OUString sDescription = xContext->getAccessibleDescription();
+ //sal_Int32 nPlaceYourBreakpointHere = 0;
+ }
+ }
+ catch( const Exception& /*e*/ )
+ {
+ // silent this, it's only diagnostics which failed
+ }
+ }
+#endif
+ if ( m_aChildrenMap.end() != aDisposedPos )
+ {
+ m_aChildrenMap.erase( aDisposedPos );
+ }
+ }
+
+ OAccessibleWrapper::OAccessibleWrapper( const Reference< XComponentContext >& _rxContext,
+ const Reference< XAccessible >& _rxInnerAccessible, const Reference< XAccessible >& _rxParentAccessible )
+ :OAccessibleWrapper_Base( )
+ ,OComponentProxyAggregation( _rxContext, Reference< XComponent >( _rxInnerAccessible, UNO_QUERY ) )
+ ,m_xParentAccessible( _rxParentAccessible )
+ ,m_xInnerAccessible( _rxInnerAccessible )
+ {
+ }
+
+
+ OAccessibleWrapper::~OAccessibleWrapper( )
+ {
+ if ( !m_rBHelper.bDisposed )
+ {
+ acquire(); // to prevent duplicate dtor calls
+ dispose();
+ }
+ }
+
+
+ IMPLEMENT_FORWARD_XTYPEPROVIDER2( OAccessibleWrapper, OComponentProxyAggregation, OAccessibleWrapper_Base )
+ IMPLEMENT_FORWARD_REFCOUNT( OAccessibleWrapper, OComponentProxyAggregation )
+
+
+ Any OAccessibleWrapper::queryInterface( const Type& _rType )
+ {
+ // #111089# instead of the inner XAccessible the proxy XAccessible must be returned
+ Any aReturn = OAccessibleWrapper_Base::queryInterface( _rType );
+ if ( !aReturn.hasValue() )
+ aReturn = OComponentProxyAggregation::queryInterface( _rType );
+
+ return aReturn;
+ }
+
+
+ Reference< XAccessibleContext > OAccessibleWrapper::getContextNoCreate( ) const
+ {
+ return m_aContext;
+ }
+
+
+ rtl::Reference<OAccessibleContextWrapper> OAccessibleWrapper::createAccessibleContext( const Reference< XAccessibleContext >& _rxInnerContext )
+ {
+ return new OAccessibleContextWrapper( getComponentContext(), _rxInnerContext, this, m_xParentAccessible );
+ }
+
+
+ Reference< XAccessibleContext > SAL_CALL OAccessibleWrapper::getAccessibleContext( )
+ {
+ // see if the context is still alive (we cache it)
+ Reference< XAccessibleContext > xContext = m_aContext;
+ if ( !xContext.is() )
+ {
+ // create a new context
+ Reference< XAccessibleContext > xInnerContext = m_xInnerAccessible->getAccessibleContext( );
+ if ( xInnerContext.is() )
+ {
+ xContext = createAccessibleContext( xInnerContext );
+ // cache it
+ m_aContext = WeakReference< XAccessibleContext >( xContext );
+ }
+ }
+
+ return xContext;
+ }
+
+ OAccessibleContextWrapperHelper::OAccessibleContextWrapperHelper(
+ const Reference< XComponentContext >& _rxContext,
+ ::cppu::OBroadcastHelper& _rBHelper,
+ const Reference< XAccessibleContext >& _rxInnerAccessibleContext,
+ const Reference< XAccessible >& _rxOwningAccessible,
+ const Reference< XAccessible >& _rxParentAccessible )
+ :OComponentProxyAggregationHelper( _rxContext, _rBHelper )
+ ,m_xInnerContext( _rxInnerAccessibleContext )
+ ,m_xOwningAccessible( _rxOwningAccessible )
+ ,m_xParentAccessible( _rxParentAccessible )
+ // initialize the mapper for our children
+ ,m_xChildMapper( new OWrappedAccessibleChildrenManager( getComponentContext() ) )
+ {
+ // determine if we're allowed to cache children
+ sal_Int64 aStates = m_xInnerContext->getAccessibleStateSet( );
+ m_xChildMapper->setTransientChildren( aStates & AccessibleStateType::MANAGES_DESCENDANTS );
+
+ m_xChildMapper->setOwningAccessible( m_xOwningAccessible );
+ }
+
+
+ void OAccessibleContextWrapperHelper::aggregateProxy( oslInterlockedCount& _rRefCount, ::cppu::OWeakObject& _rDelegator )
+ {
+ Reference< XComponent > xInnerComponent( m_xInnerContext, UNO_QUERY );
+ OSL_ENSURE( xInnerComponent.is(), "OComponentProxyAggregation::aggregateProxy: accessible is no XComponent!" );
+ if ( xInnerComponent.is() )
+ componentAggregateProxyFor( xInnerComponent, _rRefCount, _rDelegator );
+
+ // add as event listener to the inner context, because we want to multiplex the AccessibleEvents
+ osl_atomic_increment( &_rRefCount );
+ {
+ Reference< XAccessibleEventBroadcaster > xBroadcaster( m_xInner, UNO_QUERY );
+ if ( xBroadcaster.is() )
+ xBroadcaster->addAccessibleEventListener( this );
+ }
+ osl_atomic_decrement( &_rRefCount );
+ }
+
+
+ OAccessibleContextWrapperHelper::~OAccessibleContextWrapperHelper( )
+ {
+ OSL_ENSURE( m_rBHelper.bDisposed, "OAccessibleContextWrapperHelper::~OAccessibleContextWrapperHelper: you should ensure (in your dtor) that the object is disposed!" );
+ }
+
+
+ Any SAL_CALL OAccessibleContextWrapperHelper::queryInterface( const Type& _rType )
+ {
+ Any aReturn = OComponentProxyAggregationHelper::queryInterface( _rType );
+ if ( !aReturn.hasValue() )
+ aReturn = OAccessibleContextWrapperHelper_Base::queryInterface( _rType );
+ return aReturn;
+ }
+
+
+ IMPLEMENT_FORWARD_XTYPEPROVIDER2( OAccessibleContextWrapperHelper, OComponentProxyAggregationHelper, OAccessibleContextWrapperHelper_Base )
+
+
+ sal_Int64 OAccessibleContextWrapperHelper::baseGetAccessibleChildCount( )
+ {
+ return m_xInnerContext->getAccessibleChildCount();
+ }
+
+
+ Reference< XAccessible > OAccessibleContextWrapperHelper::baseGetAccessibleChild( sal_Int64 i )
+ {
+ // get the child of the wrapped component
+ Reference< XAccessible > xInnerChild = m_xInnerContext->getAccessibleChild( i );
+ return m_xChildMapper->getAccessibleWrapperFor( xInnerChild );
+ }
+
+
+ Reference< XAccessibleRelationSet > OAccessibleContextWrapperHelper::baseGetAccessibleRelationSet( )
+ {
+ return m_xInnerContext->getAccessibleRelationSet();
+ // TODO: if this relation set would contain relations to siblings, we would normally need
+ // to wrap them, too...
+ }
+
+
+ void SAL_CALL OAccessibleContextWrapperHelper::notifyEvent( const AccessibleEventObject& _rEvent )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ if ( AccessibleEventId::STATE_CHANGED == _rEvent.EventId )
+ {
+ bool bChildTransienceChanged = false;
+ sal_Int64 nChangeState = 0;
+ if ( _rEvent.OldValue >>= nChangeState )
+ bChildTransienceChanged = bChildTransienceChanged || AccessibleStateType::MANAGES_DESCENDANTS == nChangeState;
+ if ( _rEvent.NewValue >>= nChangeState )
+ bChildTransienceChanged = bChildTransienceChanged || AccessibleStateType::MANAGES_DESCENDANTS == nChangeState;
+ OSL_ENSURE( !bChildTransienceChanged, "OAccessibleContextWrapperHelper::notifyEvent: MANAGES_DESCENDANTS is not expected to change during runtime!" );
+ // if this asserts, then we would need to update our m_bTransientChildren flag here,
+ // as well as (potentially) our child cache
+ }
+#endif
+ AccessibleEventObject aTranslatedEvent( _rEvent );
+
+ {
+ ::osl::MutexGuard aGuard( m_rBHelper.rMutex );
+
+ // translate the event
+ queryInterface( cppu::UnoType<XInterface>::get() ) >>= aTranslatedEvent.Source;
+ m_xChildMapper->translateAccessibleEvent( _rEvent, aTranslatedEvent );
+
+ // see if any of these notifications affect our child manager
+ m_xChildMapper->handleChildNotification( _rEvent );
+
+ if ( aTranslatedEvent.NewValue == m_xInner )
+ aTranslatedEvent.NewValue <<= aTranslatedEvent.Source;
+ if ( aTranslatedEvent.OldValue == m_xInner )
+ aTranslatedEvent.OldValue <<= aTranslatedEvent.Source;
+ }
+
+ notifyTranslatedEvent( aTranslatedEvent );
+ }
+
+
+ void SAL_CALL OAccessibleContextWrapperHelper::dispose()
+ {
+ ::osl::MutexGuard aGuard( m_rBHelper.rMutex );
+
+ // stop multiplexing events
+ Reference< XAccessibleEventBroadcaster > xBroadcaster( m_xInner, UNO_QUERY );
+ OSL_ENSURE( xBroadcaster.is(), "OAccessibleContextWrapperHelper::disposing(): inner context is no broadcaster!" );
+ if ( xBroadcaster.is() )
+ xBroadcaster->removeAccessibleEventListener( this );
+
+ // dispose the child cache/map
+ m_xChildMapper->dispose();
+
+ // let the base class dispose the inner component
+ OComponentProxyAggregationHelper::dispose();
+ }
+
+
+ void SAL_CALL OAccessibleContextWrapperHelper::disposing( const EventObject& _rEvent )
+ {
+ // simply disambiguate this
+ OComponentProxyAggregationHelper::disposing( _rEvent );
+ }
+
+ IMPLEMENT_FORWARD_XINTERFACE2( OAccessibleContextWrapper, OAccessibleContextWrapper_CBase, OAccessibleContextWrapperHelper )
+
+
+ IMPLEMENT_FORWARD_XTYPEPROVIDER2( OAccessibleContextWrapper, OAccessibleContextWrapper_CBase, OAccessibleContextWrapperHelper )
+
+
+ OAccessibleContextWrapper::OAccessibleContextWrapper( const Reference< XComponentContext >& _rxContext,
+ const Reference< XAccessibleContext >& _rxInnerAccessibleContext, const Reference< XAccessible >& _rxOwningAccessible,
+ const Reference< XAccessible >& _rxParentAccessible )
+ :OAccessibleContextWrapper_CBase( m_aMutex )
+ ,OAccessibleContextWrapperHelper( _rxContext, rBHelper, _rxInnerAccessibleContext, _rxOwningAccessible, _rxParentAccessible )
+ ,m_nNotifierClient( 0 )
+ {
+ aggregateProxy( m_refCount, *this );
+ }
+
+
+ OAccessibleContextWrapper::~OAccessibleContextWrapper()
+ {
+ }
+
+
+ sal_Int64 SAL_CALL OAccessibleContextWrapper::getAccessibleChildCount( )
+ {
+ return baseGetAccessibleChildCount();
+ }
+
+
+ Reference< XAccessible > SAL_CALL OAccessibleContextWrapper::getAccessibleChild( sal_Int64 i )
+ {
+ return baseGetAccessibleChild( i );
+ }
+
+
+ Reference< XAccessible > SAL_CALL OAccessibleContextWrapper::getAccessibleParent( )
+ {
+ return m_xParentAccessible;
+ }
+
+
+ sal_Int64 SAL_CALL OAccessibleContextWrapper::getAccessibleIndexInParent( )
+ {
+ return m_xInnerContext->getAccessibleIndexInParent();
+ }
+
+
+ sal_Int16 SAL_CALL OAccessibleContextWrapper::getAccessibleRole( )
+ {
+ return m_xInnerContext->getAccessibleRole();
+ }
+
+
+ OUString SAL_CALL OAccessibleContextWrapper::getAccessibleDescription( )
+ {
+ return m_xInnerContext->getAccessibleDescription();
+ }
+
+
+ OUString SAL_CALL OAccessibleContextWrapper::getAccessibleName( )
+ {
+ return m_xInnerContext->getAccessibleName();
+ }
+
+
+ Reference< XAccessibleRelationSet > SAL_CALL OAccessibleContextWrapper::getAccessibleRelationSet( )
+ {
+ return baseGetAccessibleRelationSet();
+ }
+
+
+ sal_Int64 SAL_CALL OAccessibleContextWrapper::getAccessibleStateSet( )
+ {
+ return m_xInnerContext->getAccessibleStateSet();
+ }
+
+
+ Locale SAL_CALL OAccessibleContextWrapper::getLocale( )
+ {
+ return m_xInnerContext->getLocale();
+ }
+
+
+ void OAccessibleContextWrapper::notifyTranslatedEvent( const AccessibleEventObject& _rEvent )
+ {
+ if ( m_nNotifierClient )
+ AccessibleEventNotifier::addEvent( m_nNotifierClient, _rEvent );
+ }
+
+
+ void SAL_CALL OAccessibleContextWrapper::addAccessibleEventListener( const Reference< XAccessibleEventListener >& _rxListener )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( !m_nNotifierClient )
+ m_nNotifierClient = AccessibleEventNotifier::registerClient( );
+ AccessibleEventNotifier::addEventListener( m_nNotifierClient, _rxListener );
+ }
+
+
+ void SAL_CALL OAccessibleContextWrapper::removeAccessibleEventListener( const Reference< XAccessibleEventListener >& _rxListener )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( m_nNotifierClient )
+ {
+ if ( 0 == AccessibleEventNotifier::removeEventListener( m_nNotifierClient, _rxListener ) )
+ {
+ AccessibleEventNotifier::TClientId nId( m_nNotifierClient );
+ m_nNotifierClient = 0;
+ AccessibleEventNotifier::revokeClient( nId );
+ }
+ }
+ }
+
+
+ void OAccessibleContextWrapper::implDisposing(const css::lang::EventObject* pEvent)
+ {
+ AccessibleEventNotifier::TClientId nClientId( 0 );
+
+ // --- <mutex lock> -----------------------------------------
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ // prepare notifying our AccessibleListeners
+ if ( m_nNotifierClient )
+ {
+ nClientId = m_nNotifierClient;
+ m_nNotifierClient = 0;
+ }
+ }
+ // --- </mutex lock> -----------------------------------------
+
+ // let the base class do
+ if (pEvent)
+ OAccessibleContextWrapperHelper::disposing(*pEvent);
+ else
+ OAccessibleContextWrapperHelper::dispose();
+
+ // notify the disposal
+ if ( nClientId )
+ AccessibleEventNotifier::revokeClientNotifyDisposing( nClientId, *this );
+ }
+
+ void SAL_CALL OAccessibleContextWrapper::disposing()
+ {
+ implDisposing(nullptr);
+ }
+
+ void SAL_CALL OAccessibleContextWrapper::disposing(const css::lang::EventObject& rEvent)
+ {
+ assert(rEvent.Source == Reference<XInterface>(m_xInnerContext, UNO_QUERY)
+ && "OAccessibleContextWrapper::disposing called with event source that's not the "
+ "wrapped a11y context");
+
+ implDisposing(&rEvent);
+ }
+} // namespace accessibility
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/anycompare.cxx b/comphelper/source/misc/anycompare.cxx
new file mode 100644
index 0000000000..8a23877239
--- /dev/null
+++ b/comphelper/source/misc/anycompare.cxx
@@ -0,0 +1,453 @@
+/* -*- 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 <memory>
+#include <optional>
+#include <comphelper/anycompare.hxx>
+#include <typelib/typedescription.hxx>
+
+#include <com/sun/star/util/Date.hpp>
+#include <com/sun/star/util/Time.hpp>
+#include <com/sun/star/util/DateTime.hpp>
+
+#include "typedescriptionref.hxx"
+
+namespace comphelper
+{
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::Type;
+ using ::com::sun::star::uno::TypeDescription;
+ using ::com::sun::star::uno::TypeClass_CHAR;
+ using ::com::sun::star::uno::TypeClass_BOOLEAN;
+ using ::com::sun::star::uno::TypeClass_BYTE;
+ using ::com::sun::star::uno::TypeClass_SHORT;
+ using ::com::sun::star::uno::TypeClass_UNSIGNED_SHORT;
+ using ::com::sun::star::uno::TypeClass_LONG;
+ using ::com::sun::star::uno::TypeClass_UNSIGNED_LONG;
+ using ::com::sun::star::uno::TypeClass_HYPER;
+ using ::com::sun::star::uno::TypeClass_UNSIGNED_HYPER;
+ using ::com::sun::star::uno::TypeClass_FLOAT;
+ using ::com::sun::star::uno::TypeClass_DOUBLE;
+ using ::com::sun::star::uno::TypeClass_STRING;
+ using ::com::sun::star::uno::TypeClass_TYPE;
+ using ::com::sun::star::uno::TypeClass_ENUM;
+ using ::com::sun::star::uno::TypeClass_INTERFACE;
+ using ::com::sun::star::uno::TypeClass_STRUCT;
+ using ::com::sun::star::i18n::XCollator;
+ using ::com::sun::star::util::Date;
+ using ::com::sun::star::util::Time;
+ using ::com::sun::star::util::DateTime;
+ using ::comphelper::detail::TypeDescriptionRef;
+
+ namespace {
+
+ class DatePredicateLess : public IKeyPredicateLess
+ {
+ public:
+ virtual bool isLess( css::uno::Any const & _lhs, css::uno::Any const & _rhs ) const override
+ {
+ Date lhs, rhs;
+ if ( !( _lhs >>= lhs )
+ || !( _rhs >>= rhs )
+ )
+ throw css::lang::IllegalArgumentException("bad ordering", css::uno::Reference<css::uno::XInterface>(), -1);
+ // FIXME Timezone?
+
+ if ( lhs.Year < rhs.Year )
+ return true;
+ if ( lhs.Year > rhs.Year )
+ return false;
+
+ if ( lhs.Month < rhs.Month )
+ return true;
+ if ( lhs.Month > rhs.Month )
+ return false;
+
+ if ( lhs.Day < rhs.Day )
+ return true;
+ return false;
+ }
+ };
+
+ class TimePredicateLess : public IKeyPredicateLess
+ {
+ public:
+ virtual bool isLess( css::uno::Any const & _lhs, css::uno::Any const & _rhs ) const override
+ {
+ Time lhs, rhs;
+ if ( !( _lhs >>= lhs )
+ || !( _rhs >>= rhs )
+ )
+ throw css::lang::IllegalArgumentException("bad ordering", css::uno::Reference<css::uno::XInterface>(), -1);
+ // FIXME Timezone?
+
+ if ( lhs.Hours < rhs.Hours )
+ return true;
+ if ( lhs.Hours > rhs.Hours )
+ return false;
+
+ if ( lhs.Minutes < rhs.Minutes )
+ return true;
+ if ( lhs.Minutes > rhs.Minutes )
+ return false;
+
+ if ( lhs.Seconds < rhs.Seconds )
+ return true;
+ if ( lhs.Seconds > rhs.Seconds )
+ return false;
+
+ if ( lhs.NanoSeconds < rhs.NanoSeconds )
+ return true;
+ return false;
+ }
+ };
+
+ class DateTimePredicateLess : public IKeyPredicateLess
+ {
+ public:
+ virtual bool isLess( css::uno::Any const & _lhs, css::uno::Any const & _rhs ) const override
+ {
+ DateTime lhs, rhs;
+ if ( !( _lhs >>= lhs )
+ || !( _rhs >>= rhs )
+ )
+ throw css::lang::IllegalArgumentException("bad ordering", css::uno::Reference<css::uno::XInterface>(), -1);
+ // FIXME Timezone?
+
+ if ( lhs.Year < rhs.Year )
+ return true;
+ if ( lhs.Year > rhs.Year )
+ return false;
+
+ if ( lhs.Month < rhs.Month )
+ return true;
+ if ( lhs.Month > rhs.Month )
+ return false;
+
+ if ( lhs.Day < rhs.Day )
+ return true;
+ if ( lhs.Day > rhs.Day )
+ return false;
+
+ if ( lhs.Hours < rhs.Hours )
+ return true;
+ if ( lhs.Hours > rhs.Hours )
+ return false;
+
+ if ( lhs.Minutes < rhs.Minutes )
+ return true;
+ if ( lhs.Minutes > rhs.Minutes )
+ return false;
+
+ if ( lhs.Seconds < rhs.Seconds )
+ return true;
+ if ( lhs.Seconds > rhs.Seconds )
+ return false;
+
+ if ( lhs.NanoSeconds < rhs.NanoSeconds )
+ return true;
+ return false;
+ }
+ };
+
+ bool anyLess( void const * lhs, typelib_TypeDescriptionReference * lhsType,
+ void const * rhs, typelib_TypeDescriptionReference * rhsType );
+
+ // For compound types we need to compare them member by member until we've
+ // checked them all or found a member that differs. For inequality checks
+ // we need to call anyLess() twice in both directions, this function does that.
+ std::optional<bool> anyCompare( void const * lhs, typelib_TypeDescriptionReference * lhsType,
+ void const * rhs, typelib_TypeDescriptionReference * rhsType )
+ {
+ if( anyLess( lhs, lhsType, rhs, rhsType ))
+ return std::optional( true );
+ if( anyLess( rhs, rhsType, lhs, lhsType ))
+ return std::optional( false );
+ return std::nullopt; // equal, so can't yet tell if anyLess() should return
+ }
+
+ // This is typelib_typedescription_equals(), but returns -1/0/1 values like strcmp().
+ int compareTypes( const typelib_TypeDescription * lhsType,
+ const typelib_TypeDescription * rhsType )
+ {
+ if( lhsType == rhsType )
+ return 0;
+ if( lhsType->eTypeClass != rhsType->eTypeClass )
+ return lhsType->eTypeClass - rhsType->eTypeClass;
+ if( lhsType->pTypeName->length != rhsType->pTypeName->length )
+ return lhsType->pTypeName->length - rhsType->pTypeName->length;
+ return rtl_ustr_compare( lhsType->pTypeName->buffer, rhsType->pTypeName->buffer );
+ }
+
+ bool anyLess( void const * lhs, typelib_TypeDescriptionReference * lhsType,
+ void const * rhs, typelib_TypeDescriptionReference * rhsType )
+ {
+ if (lhsType->eTypeClass != rhsType->eTypeClass)
+ return lhsType->eTypeClass < rhsType->eTypeClass;
+
+ if (lhsType->eTypeClass == typelib_TypeClass_VOID) {
+ return false;
+ }
+ assert(lhs != nullptr);
+ assert(rhs != nullptr);
+
+ switch (lhsType->eTypeClass) {
+ case typelib_TypeClass_INTERFACE:
+ return lhs < rhs;
+ case typelib_TypeClass_STRUCT:
+ case typelib_TypeClass_EXCEPTION: {
+ TypeDescription lhsTypeDescr( lhsType );
+ if (!lhsTypeDescr.is())
+ lhsTypeDescr.makeComplete();
+ if (!lhsTypeDescr.is())
+ throw css::lang::IllegalArgumentException("bad ordering", css::uno::Reference<css::uno::XInterface>(), -1);
+ TypeDescription rhsTypeDescr( rhsType );
+ if (!rhsTypeDescr.is())
+ rhsTypeDescr.makeComplete();
+ if (!rhsTypeDescr.is())
+ throw css::lang::IllegalArgumentException("bad ordering", css::uno::Reference<css::uno::XInterface>(), -1);
+ int compare = compareTypes( lhsTypeDescr.get(), rhsTypeDescr.get());
+ if( compare != 0 )
+ return compare < 0;
+
+ typelib_CompoundTypeDescription * compType =
+ reinterpret_cast< typelib_CompoundTypeDescription * >(
+ lhsTypeDescr.get() );
+ sal_Int32 nDescr = compType->nMembers;
+
+ if (compType->pBaseTypeDescription) {
+ std::optional<bool> subLess = anyCompare(
+ lhs, reinterpret_cast<
+ typelib_TypeDescription * >(
+ compType->pBaseTypeDescription)->pWeakRef,
+ rhs, reinterpret_cast<
+ typelib_TypeDescription * >(
+ compType->pBaseTypeDescription)->pWeakRef);
+ if(subLess.has_value())
+ return *subLess;
+ }
+
+ typelib_TypeDescriptionReference ** ppTypeRefs =
+ compType->ppTypeRefs;
+ sal_Int32 * memberOffsets = compType->pMemberOffsets;
+
+ for ( sal_Int32 nPos = 0; nPos < nDescr; ++nPos )
+ {
+ TypeDescriptionRef memberType( ppTypeRefs[ nPos ] );
+ if (!memberType.is())
+ throw css::lang::IllegalArgumentException("bad ordering", css::uno::Reference<css::uno::XInterface>(), -1);
+ std::optional<bool> subLess = anyCompare(
+ static_cast< char const * >(
+ lhs ) + memberOffsets[ nPos ],
+ memberType->pWeakRef,
+ static_cast< char const * >(
+ rhs ) + memberOffsets[ nPos ],
+ memberType->pWeakRef);
+ if(subLess.has_value())
+ return *subLess;
+ }
+ return false; // equal
+ }
+ case typelib_TypeClass_SEQUENCE: {
+ uno_Sequence * lhsSeq = *static_cast< uno_Sequence * const * >(lhs);
+ uno_Sequence * rhsSeq = *static_cast< uno_Sequence * const * >(rhs);
+ if( lhsSeq->nElements != rhsSeq->nElements)
+ return lhsSeq->nElements < rhsSeq->nElements;
+ sal_Int32 nElements = lhsSeq->nElements;
+
+ TypeDescriptionRef lhsTypeDescr( lhsType );
+ if (!lhsTypeDescr.is())
+ throw css::lang::IllegalArgumentException("bad ordering", css::uno::Reference<css::uno::XInterface>(), -1);
+ TypeDescriptionRef rhsTypeDescr( rhsType );
+ if (!rhsTypeDescr.is())
+ throw css::lang::IllegalArgumentException("bad ordering", css::uno::Reference<css::uno::XInterface>(), -1);
+ int compare = compareTypes( lhsTypeDescr.get(), rhsTypeDescr.get());
+ if( compare != 0 )
+ return compare < 0;
+
+ typelib_TypeDescriptionReference * elementTypeRef =
+ reinterpret_cast< typelib_IndirectTypeDescription * >(lhsTypeDescr.get())->pType;
+ TypeDescriptionRef elementTypeDescr( elementTypeRef );
+ if (!elementTypeDescr.is())
+ throw css::lang::IllegalArgumentException("bad ordering", css::uno::Reference<css::uno::XInterface>(), -1);
+ assert( elementTypeDescr.equals( TypeDescriptionRef(
+ reinterpret_cast< typelib_IndirectTypeDescription * >(lhsTypeDescr.get())->pType )));
+
+ sal_Int32 nElementSize = elementTypeDescr->nSize;
+ if (nElements > 0)
+ {
+ char const * lhsElements = lhsSeq->elements;
+ char const * rhsElements = rhsSeq->elements;
+ for ( sal_Int32 nPos = 0; nPos < nElements; ++nPos )
+ {
+ std::optional<bool> subLess = anyCompare(
+ lhsElements + (nElementSize * nPos),
+ elementTypeDescr->pWeakRef,
+ rhsElements + (nElementSize * nPos),
+ elementTypeDescr->pWeakRef );
+ if(subLess.has_value())
+ return *subLess;
+ }
+ }
+ return false; // equal
+ }
+ case typelib_TypeClass_ANY: {
+ uno_Any const * lhsAny = static_cast< uno_Any const * >(lhs);
+ uno_Any const * rhsAny = static_cast< uno_Any const * >(rhs);
+ return anyLess( lhsAny->pData, lhsAny->pType, rhsAny->pData, rhsAny->pType );
+ }
+ case typelib_TypeClass_TYPE: {
+ OUString const & lhsTypeName = OUString::unacquired(
+ &(*static_cast< typelib_TypeDescriptionReference * const * >(lhs))->pTypeName);
+ OUString const & rhsTypeName = OUString::unacquired(
+ &(*static_cast< typelib_TypeDescriptionReference * const * >(rhs))->pTypeName);
+ return lhsTypeName < rhsTypeName;
+ }
+ case typelib_TypeClass_STRING: {
+ OUString const & lhsStr = OUString::unacquired(
+ static_cast< rtl_uString * const * >(lhs) );
+ OUString const & rhsStr = OUString::unacquired(
+ static_cast< rtl_uString * const * >(rhs) );
+ return lhsStr < rhsStr;
+ }
+ case typelib_TypeClass_ENUM: {
+ TypeDescription lhsTypeDescr( lhsType );
+ if (!lhsTypeDescr.is())
+ lhsTypeDescr.makeComplete();
+ if (!lhsTypeDescr.is())
+ throw css::lang::IllegalArgumentException("bad ordering", css::uno::Reference<css::uno::XInterface>(), -1);
+ TypeDescription rhsTypeDescr( rhsType );
+ if (!rhsTypeDescr.is())
+ rhsTypeDescr.makeComplete();
+ if (!rhsTypeDescr.is())
+ throw css::lang::IllegalArgumentException("bad ordering", css::uno::Reference<css::uno::XInterface>(), -1);
+ int compare = compareTypes( lhsTypeDescr.get(), rhsTypeDescr.get());
+ if( compare != 0 )
+ return compare < 0;
+
+ return *static_cast< int const * >(lhs) < *static_cast< int const * >(rhs);
+ }
+ case typelib_TypeClass_BOOLEAN:
+ return *static_cast< sal_Bool const * >(lhs) < *static_cast< sal_Bool const * >(rhs);
+ case typelib_TypeClass_CHAR:
+ return *static_cast< sal_Unicode const * >(lhs) < *static_cast< sal_Unicode const * >(rhs);
+ case typelib_TypeClass_FLOAT:
+ return *static_cast< float const * >(lhs) < *static_cast< float const * >(rhs);
+ case typelib_TypeClass_DOUBLE:
+ return *static_cast< double const * >(lhs) < *static_cast< double const * >(rhs);
+ case typelib_TypeClass_BYTE:
+ return *static_cast< sal_Int8 const * >(lhs) < *static_cast< sal_Int8 const * >(rhs);
+ case typelib_TypeClass_SHORT:
+ return *static_cast< sal_Int16 const * >(lhs) < *static_cast< sal_Int16 const * >(rhs);
+ case typelib_TypeClass_UNSIGNED_SHORT:
+ return *static_cast< sal_uInt16 const * >(lhs) < *static_cast< sal_uInt16 const * >(rhs);
+ case typelib_TypeClass_LONG:
+ return *static_cast< sal_Int32 const * >(lhs) < *static_cast< sal_Int32 const * >(rhs);
+ case typelib_TypeClass_UNSIGNED_LONG:
+ return *static_cast< sal_uInt32 const * >(lhs) < *static_cast< sal_uInt32 const * >(rhs);
+ case typelib_TypeClass_HYPER:
+ return *static_cast< sal_Int64 const * >(lhs) < *static_cast< sal_Int64 const * >(rhs);
+ case typelib_TypeClass_UNSIGNED_HYPER:
+ return *static_cast< sal_uInt64 const * >(lhs) < *static_cast< sal_uInt64 const * >(rhs);
+ // case typelib_TypeClass_UNKNOWN:
+ // case typelib_TypeClass_SERVICE:
+ // case typelib_TypeClass_MODULE:
+ default:
+ return false;
+ }
+ }
+
+ } // namespace
+
+ std::unique_ptr< IKeyPredicateLess > getStandardLessPredicate( Type const & i_type, Reference< XCollator > const & i_collator )
+ {
+ std::unique_ptr< IKeyPredicateLess > pComparator;
+ switch ( i_type.getTypeClass() )
+ {
+ case TypeClass_CHAR:
+ pComparator.reset( new ScalarPredicateLess< sal_Unicode > );
+ break;
+ case TypeClass_BOOLEAN:
+ pComparator.reset( new ScalarPredicateLess< bool > );
+ break;
+ case TypeClass_BYTE:
+ pComparator.reset( new ScalarPredicateLess< sal_Int8 > );
+ break;
+ case TypeClass_SHORT:
+ pComparator.reset( new ScalarPredicateLess< sal_Int16 > );
+ break;
+ case TypeClass_UNSIGNED_SHORT:
+ pComparator.reset( new ScalarPredicateLess< sal_uInt16 > );
+ break;
+ case TypeClass_LONG:
+ pComparator.reset( new ScalarPredicateLess< sal_Int32 > );
+ break;
+ case TypeClass_UNSIGNED_LONG:
+ pComparator.reset( new ScalarPredicateLess< sal_uInt32 > );
+ break;
+ case TypeClass_HYPER:
+ pComparator.reset( new ScalarPredicateLess< sal_Int64 > );
+ break;
+ case TypeClass_UNSIGNED_HYPER:
+ pComparator.reset( new ScalarPredicateLess< sal_uInt64 > );
+ break;
+ case TypeClass_FLOAT:
+ pComparator.reset( new ScalarPredicateLess< float > );
+ break;
+ case TypeClass_DOUBLE:
+ pComparator.reset( new ScalarPredicateLess< double > );
+ break;
+ case TypeClass_STRING:
+ if ( i_collator.is() )
+ pComparator.reset( new StringCollationPredicateLess( i_collator ) );
+ else
+ pComparator.reset( new StringPredicateLess );
+ break;
+ case TypeClass_TYPE:
+ pComparator.reset( new TypePredicateLess );
+ break;
+ case TypeClass_ENUM:
+ pComparator.reset( new EnumPredicateLess( i_type ) );
+ break;
+ case TypeClass_INTERFACE:
+ pComparator.reset( new InterfacePredicateLess );
+ break;
+ case TypeClass_STRUCT:
+ if ( i_type.equals( ::cppu::UnoType< Date >::get() ) )
+ pComparator.reset( new DatePredicateLess );
+ else if ( i_type.equals( ::cppu::UnoType< Time >::get() ) )
+ pComparator.reset( new TimePredicateLess );
+ else if ( i_type.equals( ::cppu::UnoType< DateTime >::get() ) )
+ pComparator.reset( new DateTimePredicateLess );
+ break;
+ default:
+ break;
+ }
+ return pComparator;
+ }
+
+ bool anyLess( css::uno::Any const & lhs, css::uno::Any const & rhs)
+ {
+ return anyLess( lhs.getValue(), lhs.getValueTypeRef(), rhs.getValue(), rhs.getValueTypeRef());
+ }
+
+} // namespace comphelper
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/anytohash.cxx b/comphelper/source/misc/anytohash.cxx
new file mode 100644
index 0000000000..4e97ea124d
--- /dev/null
+++ b/comphelper/source/misc/anytohash.cxx
@@ -0,0 +1,210 @@
+/* -*- 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 <comphelper/anytohash.hxx>
+
+#include <o3tl/hash_combine.hxx>
+#include <typelib/typedescription.hxx>
+
+#include <com/sun/star/uno/Sequence.hxx>
+
+#include "typedescriptionref.hxx"
+
+using namespace ::com::sun::star;
+using ::com::sun::star::uno::TypeDescription;
+using ::comphelper::detail::TypeDescriptionRef;
+
+namespace comphelper {
+namespace {
+
+std::optional<size_t> hashValue( size_t hash,
+ void const * val, typelib_TypeDescriptionReference * typeRef )
+{
+ o3tl::hash_combine( hash, typeRef->eTypeClass );
+ if (typeRef->eTypeClass == typelib_TypeClass_VOID) {
+ return hash;
+ }
+ assert(val != nullptr);
+
+ switch (typeRef->eTypeClass) {
+ case typelib_TypeClass_INTERFACE: {
+ return std::nullopt; // not implemented
+ }
+ case typelib_TypeClass_STRUCT:
+ case typelib_TypeClass_EXCEPTION: {
+ TypeDescription typeDescr( typeRef );
+ if (!typeDescr.is())
+ typeDescr.makeComplete();
+ if (!typeDescr.is())
+ return std::nullopt;
+
+ typelib_CompoundTypeDescription * compType =
+ reinterpret_cast< typelib_CompoundTypeDescription * >(
+ typeDescr.get() );
+ sal_Int32 nDescr = compType->nMembers;
+
+ if (compType->pBaseTypeDescription) {
+ std::optional<size_t> tmpHash = hashValue(
+ hash, val, reinterpret_cast<
+ typelib_TypeDescription * >(
+ compType->pBaseTypeDescription)->pWeakRef);
+ if(!tmpHash.has_value())
+ return std::nullopt;
+ hash = *tmpHash;
+ }
+
+ typelib_TypeDescriptionReference ** ppTypeRefs =
+ compType->ppTypeRefs;
+ sal_Int32 * memberOffsets = compType->pMemberOffsets;
+
+ for ( sal_Int32 nPos = 0; nPos < nDescr; ++nPos )
+ {
+ TypeDescriptionRef memberType( ppTypeRefs[ nPos ] );
+ if (!memberType.is())
+ return std::nullopt;
+
+ std::optional<size_t> tmpHash = hashValue( hash,
+ static_cast< char const * >(
+ val ) + memberOffsets[ nPos ],
+ memberType->pWeakRef );
+ if(!tmpHash.has_value())
+ return std::nullopt;
+ hash = *tmpHash;
+ }
+ break;
+ }
+ case typelib_TypeClass_SEQUENCE: {
+ TypeDescriptionRef typeDescr( typeRef );
+ if (!typeDescr.is())
+ return std::nullopt;
+
+ typelib_TypeDescriptionReference * elementTypeRef =
+ reinterpret_cast<
+ typelib_IndirectTypeDescription * >(typeDescr.get())->pType;
+ TypeDescriptionRef elementTypeDescr( elementTypeRef );
+ if (!elementTypeDescr.is())
+ return std::nullopt;
+
+ sal_Int32 nElementSize = elementTypeDescr->nSize;
+ uno_Sequence * seq =
+ *static_cast< uno_Sequence * const * >(val);
+ sal_Int32 nElements = seq->nElements;
+
+ if (nElements > 0)
+ {
+ char const * pElements = seq->elements;
+ for ( sal_Int32 nPos = 0; nPos < nElements; ++nPos )
+ {
+ std::optional<size_t> tmpHash = hashValue( hash,
+ pElements + (nElementSize * nPos),
+ elementTypeDescr->pWeakRef );
+ if(!tmpHash.has_value())
+ return std::nullopt;
+ hash = *tmpHash;
+ }
+ }
+ break;
+ }
+ case typelib_TypeClass_ANY: {
+ uno_Any const * pAny = static_cast< uno_Any const * >(val);
+ return hashValue( hash, pAny->pData, pAny->pType );
+ }
+ case typelib_TypeClass_TYPE: {
+ OUString const & str = OUString::unacquired(
+ &(*static_cast<
+ typelib_TypeDescriptionReference * const * >(val)
+ )->pTypeName );
+ o3tl::hash_combine( hash, str.hashCode() );
+ break;
+ }
+ case typelib_TypeClass_STRING: {
+ OUString const & str = OUString::unacquired(
+ static_cast< rtl_uString * const * >(val) );
+ o3tl::hash_combine( hash, str.hashCode() );
+ break;
+ }
+ case typelib_TypeClass_ENUM: {
+ TypeDescription typeDescr( typeRef );
+ if (!typeDescr.is())
+ typeDescr.makeComplete();
+ if (!typeDescr.is())
+ return std::nullopt;
+
+ o3tl::hash_combine( hash, *static_cast< int const * >(val));
+ break;
+ }
+ case typelib_TypeClass_BOOLEAN:
+ if (*static_cast< sal_Bool const * >(val))
+ o3tl::hash_combine( hash, true );
+ else
+ o3tl::hash_combine( hash, false );
+ break;
+ case typelib_TypeClass_CHAR: {
+ o3tl::hash_combine( hash, *static_cast< sal_Unicode const * >(val));
+ break;
+ }
+ case typelib_TypeClass_FLOAT:
+ o3tl::hash_combine( hash, *static_cast< float const * >(val) );
+ break;
+ case typelib_TypeClass_DOUBLE:
+ o3tl::hash_combine( hash, *static_cast< double const * >(val) );
+ break;
+ case typelib_TypeClass_BYTE:
+ o3tl::hash_combine( hash, *static_cast< sal_Int8 const * >(val) );
+ break;
+ case typelib_TypeClass_SHORT:
+ o3tl::hash_combine( hash, *static_cast< sal_Int16 const * >(val) );
+ break;
+ case typelib_TypeClass_UNSIGNED_SHORT:
+ o3tl::hash_combine( hash, *static_cast< sal_uInt16 const * >(val) );
+ break;
+ case typelib_TypeClass_LONG:
+ o3tl::hash_combine( hash, *static_cast< sal_Int32 const * >(val) );
+ break;
+ case typelib_TypeClass_UNSIGNED_LONG:
+ o3tl::hash_combine( hash, *static_cast< sal_uInt32 const * >(val) );
+ break;
+ case typelib_TypeClass_HYPER:
+ o3tl::hash_combine( hash, *static_cast< sal_Int64 const * >(val) );
+ break;
+ case typelib_TypeClass_UNSIGNED_HYPER:
+ o3tl::hash_combine( hash, *static_cast< sal_uInt64 const * >(val) );
+ break;
+// case typelib_TypeClass_UNKNOWN:
+// case typelib_TypeClass_SERVICE:
+// case typelib_TypeClass_MODULE:
+ default:
+ return std::nullopt;
+ }
+ return hash;
+}
+
+} // anon namespace
+
+
+std::optional<size_t> anyToHash( uno::Any const & value )
+{
+ size_t hash = 0;
+ return hashValue( hash, value.getValue(), value.getValueTypeRef());
+}
+
+} // namespace comphelper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/anytostring.cxx b/comphelper/source/misc/anytostring.cxx
new file mode 100644
index 0000000000..ebc338b0b4
--- /dev/null
+++ b/comphelper/source/misc/anytostring.cxx
@@ -0,0 +1,316 @@
+/* -*- 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 <comphelper/anytostring.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <typelib/typedescription.hxx>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+#include "typedescriptionref.hxx"
+
+using namespace ::com::sun::star;
+using ::com::sun::star::uno::TypeDescription;
+using ::comphelper::detail::TypeDescriptionRef;
+
+namespace comphelper {
+namespace {
+
+void appendTypeError(
+ OUStringBuffer & buf, const typelib_TypeDescriptionReference * typeRef )
+{
+ buf.append( "<cannot get type description of type " );
+ buf.append( OUString::unacquired( &typeRef->pTypeName ) );
+ buf.append( '>' );
+}
+
+void appendChar( OUStringBuffer & buf, sal_Unicode c )
+{
+ if (c < ' ' || c > '~') {
+ buf.append( "\\X" );
+ OUString const s(
+ OUString::number( static_cast< sal_Int32 >(c), 16 ) );
+ for ( sal_Int32 f = 4 - s.getLength(); f > 0; --f )
+ buf.append( '0' );
+ buf.append( s );
+ }
+ else {
+ buf.append( c );
+ }
+}
+
+
+void appendValue( OUStringBuffer & buf,
+ void const * val, typelib_TypeDescriptionReference * typeRef,
+ bool prependType )
+{
+ if (typeRef->eTypeClass == typelib_TypeClass_VOID) {
+ buf.append( "void" );
+ return;
+ }
+ assert(val != nullptr);
+
+ if (prependType &&
+ typeRef->eTypeClass != typelib_TypeClass_STRING &&
+ typeRef->eTypeClass != typelib_TypeClass_CHAR &&
+ typeRef->eTypeClass != typelib_TypeClass_BOOLEAN)
+ {
+ buf.append( '(' );
+ buf.append( OUString::unacquired( &typeRef->pTypeName ) );
+ buf.append( ") " );
+ }
+
+ switch (typeRef->eTypeClass) {
+ case typelib_TypeClass_INTERFACE: {
+ buf.append( '@' );
+ buf.append( reinterpret_cast< sal_Int64 >(
+ *static_cast< void * const * >(val) ), 16 );
+ uno::Reference< lang::XServiceInfo > xServiceInfo(
+ *static_cast< uno::XInterface * const * >(val),
+ uno::UNO_QUERY );
+ if (xServiceInfo.is()) {
+ buf.append( " (ImplementationName = \"" );
+ buf.append( xServiceInfo->getImplementationName() );
+ buf.append( "\")" );
+ }
+ break;
+ }
+ case typelib_TypeClass_STRUCT:
+ case typelib_TypeClass_EXCEPTION: {
+ buf.append( "{ " );
+ TypeDescription typeDescr( typeRef );
+ if (!typeDescr.is())
+ typeDescr.makeComplete();
+ if (!typeDescr.is()) {
+ appendTypeError( buf, typeRef );
+ }
+ else {
+ typelib_CompoundTypeDescription * compType =
+ reinterpret_cast< typelib_CompoundTypeDescription * >(
+ typeDescr.get() );
+ sal_Int32 nDescr = compType->nMembers;
+
+ if (compType->pBaseTypeDescription) {
+ appendValue(
+ buf, val, reinterpret_cast<
+ typelib_TypeDescription * >(
+ compType->pBaseTypeDescription)->pWeakRef, false );
+ if (nDescr > 0)
+ buf.append( ", " );
+ }
+
+ typelib_TypeDescriptionReference ** ppTypeRefs =
+ compType->ppTypeRefs;
+ sal_Int32 * memberOffsets = compType->pMemberOffsets;
+ rtl_uString ** ppMemberNames = compType->ppMemberNames;
+
+ for ( sal_Int32 nPos = 0; nPos < nDescr; ++nPos )
+ {
+ buf.append( ppMemberNames[ nPos ] );
+ buf.append( " = " );
+ TypeDescriptionRef memberType( ppTypeRefs[ nPos ] );
+ if (!memberType.is()) {
+ appendTypeError( buf, ppTypeRefs[ nPos ] );
+ }
+ else {
+ appendValue( buf,
+ static_cast< char const * >(
+ val ) + memberOffsets[ nPos ],
+ memberType->pWeakRef, true );
+ }
+ if (nPos < (nDescr - 1))
+ buf.append( ", " );
+ }
+ }
+ buf.append( " }" );
+ break;
+ }
+ case typelib_TypeClass_SEQUENCE: {
+ TypeDescriptionRef typeDescr( typeRef );
+ if (!typeDescr.is()) {
+ appendTypeError( buf,typeRef );
+ }
+ else {
+ typelib_TypeDescriptionReference * elementTypeRef =
+ reinterpret_cast<
+ typelib_IndirectTypeDescription * >(typeDescr.get())->pType;
+ TypeDescriptionRef elementTypeDescr( elementTypeRef );
+ if (!elementTypeDescr.is())
+ {
+ appendTypeError( buf, elementTypeRef );
+ }
+ else
+ {
+ sal_Int32 nElementSize = elementTypeDescr->nSize;
+ uno_Sequence * seq =
+ *static_cast< uno_Sequence * const * >(val);
+ sal_Int32 nElements = seq->nElements;
+
+ if (nElements > 0)
+ {
+ buf.append( "{ " );
+ char const * pElements = seq->elements;
+ for ( sal_Int32 nPos = 0; nPos < nElements; ++nPos )
+ {
+ appendValue(
+ buf, pElements + (nElementSize * nPos),
+ elementTypeDescr->pWeakRef, false );
+ if (nPos < (nElements - 1))
+ buf.append( ", " );
+ }
+ buf.append( " }" );
+ }
+ else
+ {
+ buf.append( "{}" );
+ }
+ }
+ }
+ break;
+ }
+ case typelib_TypeClass_ANY: {
+ buf.append( "{ " );
+ uno_Any const * pAny = static_cast< uno_Any const * >(val);
+ appendValue( buf, pAny->pData, pAny->pType, true );
+ buf.append( " }" );
+ break;
+ }
+ case typelib_TypeClass_TYPE:
+ buf.append( (*static_cast<
+ typelib_TypeDescriptionReference * const * >(val)
+ )->pTypeName );
+ break;
+ case typelib_TypeClass_STRING: {
+ buf.append( '\"' );
+ OUString const & str = OUString::unacquired(
+ static_cast< rtl_uString * const * >(val) );
+ sal_Int32 len = str.getLength();
+ for ( sal_Int32 pos = 0; pos < len; ++pos )
+ {
+ sal_Unicode c = str[ pos ];
+ if (c == '\"')
+ buf.append( "\\\"" );
+ else if (c == '\\')
+ buf.append( "\\\\" );
+ else
+ appendChar( buf, c );
+ }
+ buf.append( '\"' );
+ break;
+ }
+ case typelib_TypeClass_ENUM: {
+ TypeDescription typeDescr( typeRef );
+ if (!typeDescr.is())
+ typeDescr.makeComplete();
+ if (!typeDescr.is()) {
+ appendTypeError( buf, typeRef );
+ }
+ else
+ {
+ sal_Int32 * pValues =
+ reinterpret_cast< typelib_EnumTypeDescription * >(
+ typeDescr.get() )->pEnumValues;
+ sal_Int32 nPos = reinterpret_cast< typelib_EnumTypeDescription * >(
+ typeDescr.get() )->nEnumValues;
+ while (nPos--)
+ {
+ if (pValues[ nPos ] == *static_cast< int const * >(val))
+ break;
+ }
+ if (nPos >= 0)
+ {
+ buf.append( reinterpret_cast< typelib_EnumTypeDescription * >(
+ typeDescr.get() )->ppEnumNames[ nPos ] );
+ }
+ else
+ {
+ buf.append( "?unknown enum value?" );
+ }
+ }
+ break;
+ }
+ case typelib_TypeClass_BOOLEAN:
+ if (*static_cast< sal_Bool const * >(val))
+ buf.append( "true" );
+ else
+ buf.append( "false" );
+ break;
+ case typelib_TypeClass_CHAR: {
+ buf.append( '\'' );
+ sal_Unicode c = *static_cast< sal_Unicode const * >(val);
+ if (c == '\'')
+ buf.append( "\\\'" );
+ else if (c == '\\')
+ buf.append( "\\\\" );
+ else
+ appendChar( buf, c );
+ buf.append( '\'' );
+ break;
+ }
+ case typelib_TypeClass_FLOAT:
+ buf.append( *static_cast< float const * >(val) );
+ break;
+ case typelib_TypeClass_DOUBLE:
+ buf.append( *static_cast< double const * >(val) );
+ break;
+ case typelib_TypeClass_BYTE:
+ buf.append( static_cast< sal_Int32 >(
+ *static_cast< sal_Int8 const * >(val) ) );
+ break;
+ case typelib_TypeClass_SHORT:
+ buf.append( static_cast< sal_Int32 >(
+ *static_cast< sal_Int16 const * >(val) ) );
+ break;
+ case typelib_TypeClass_UNSIGNED_SHORT:
+ buf.append( static_cast< sal_Int32 >(
+ *static_cast< sal_uInt16 const * >(val) ) );
+ break;
+ case typelib_TypeClass_LONG:
+ buf.append( *static_cast< sal_Int32 const * >(val) );
+ break;
+ case typelib_TypeClass_UNSIGNED_LONG:
+ buf.append( static_cast< sal_Int64 >(
+ *static_cast< sal_uInt32 const * >(val) ) );
+ break;
+ case typelib_TypeClass_HYPER:
+ case typelib_TypeClass_UNSIGNED_HYPER:
+ buf.append( *static_cast< sal_Int64 const * >(val) );
+ break;
+// case typelib_TypeClass_UNKNOWN:
+// case typelib_TypeClass_SERVICE:
+// case typelib_TypeClass_MODULE:
+ default:
+ buf.append( '?' );
+ break;
+ }
+}
+
+} // anon namespace
+
+
+OUString anyToString( uno::Any const & value )
+{
+ OUStringBuffer buf;
+ appendValue( buf, value.getValue(), value.getValueTypeRef(), true );
+ return buf.makeStringAndClear();
+}
+
+} // namespace comphelper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/asyncnotification.cxx b/comphelper/source/misc/asyncnotification.cxx
new file mode 100644
index 0000000000..cb8a2f2511
--- /dev/null
+++ b/comphelper/source/misc/asyncnotification.cxx
@@ -0,0 +1,259 @@
+/* -*- 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 <comphelper/asyncnotification.hxx>
+#include <comphelper/scopeguard.hxx>
+#include <mutex>
+#include <condition_variable>
+
+#include <cassert>
+#include <stdexcept>
+#include <vector>
+#include <algorithm>
+
+namespace comphelper
+{
+ AnyEvent::AnyEvent()
+ {
+ }
+
+ AnyEvent::~AnyEvent()
+ {
+ }
+
+ namespace {
+
+ struct ProcessableEvent
+ {
+ AnyEventRef aEvent;
+ ::rtl::Reference< IEventProcessor > xProcessor;
+ };
+
+ struct EqualProcessor
+ {
+ const ::rtl::Reference< IEventProcessor >& rProcessor;
+ explicit EqualProcessor( const ::rtl::Reference< IEventProcessor >& _rProcessor ) :rProcessor( _rProcessor ) { }
+
+ bool operator()( const ProcessableEvent& _rEvent )
+ {
+ return _rEvent.xProcessor.get() == rProcessor.get();
+ }
+ };
+
+ }
+
+ struct EventNotifierImpl
+ {
+ std::mutex aMutex;
+ std::condition_variable aPendingActions;
+ std::vector< ProcessableEvent > aEvents;
+ bool bTerminate;
+ // only used for AsyncEventNotifierAutoJoin
+ char const* name;
+ std::shared_ptr<AsyncEventNotifierAutoJoin> pKeepThisAlive;
+
+ EventNotifierImpl()
+ : bTerminate(false)
+ , name(nullptr)
+ {
+ }
+ };
+
+ AsyncEventNotifierBase::AsyncEventNotifierBase()
+ : m_xImpl(new EventNotifierImpl)
+ {
+ }
+
+
+ AsyncEventNotifierBase::~AsyncEventNotifierBase()
+ {
+ }
+
+
+ void AsyncEventNotifierBase::removeEventsForProcessor( const ::rtl::Reference< IEventProcessor >& _xProcessor )
+ {
+ std::scoped_lock aGuard( m_xImpl->aMutex );
+
+ // remove all events for this processor
+ m_xImpl->aEvents.erase(std::remove_if( m_xImpl->aEvents.begin(), m_xImpl->aEvents.end(), EqualProcessor( _xProcessor ) ), m_xImpl->aEvents.end());
+ }
+
+
+ void SAL_CALL AsyncEventNotifierBase::terminate()
+ {
+ std::scoped_lock aGuard( m_xImpl->aMutex );
+
+ // remember the termination request
+ m_xImpl->bTerminate = true;
+
+ // awake the thread
+ m_xImpl->aPendingActions.notify_all();
+ }
+
+
+ void AsyncEventNotifierBase::addEvent( const AnyEventRef& _rEvent, const ::rtl::Reference< IEventProcessor >& _xProcessor )
+ {
+ std::scoped_lock aGuard( m_xImpl->aMutex );
+
+ // remember this event
+ m_xImpl->aEvents.emplace_back( ProcessableEvent {_rEvent, _xProcessor} );
+
+ // awake the thread
+ m_xImpl->aPendingActions.notify_all();
+ }
+
+
+ void AsyncEventNotifierBase::execute()
+ {
+ for (;;)
+ {
+ std::vector< ProcessableEvent > aEvents;
+ {
+ std::unique_lock aGuard(m_xImpl->aMutex);
+ m_xImpl->aPendingActions.wait(aGuard,
+ [this] { return m_xImpl->bTerminate || !m_xImpl->aEvents.empty(); } );
+ if (m_xImpl->bTerminate)
+ return;
+ else
+ std::swap(aEvents, m_xImpl->aEvents);
+ }
+ for (ProcessableEvent& rEvent : aEvents)
+ {
+ assert(rEvent.xProcessor.is());
+ rEvent.xProcessor->processEvent(*rEvent.aEvent);
+ }
+ aEvents.clear();
+ }
+ }
+
+ AsyncEventNotifier::AsyncEventNotifier(char const* name)
+ : salhelper::Thread(name)
+ {
+ }
+
+ AsyncEventNotifier::~AsyncEventNotifier()
+ {
+ }
+
+ void AsyncEventNotifier::execute()
+ {
+ return AsyncEventNotifierBase::execute();
+ }
+
+ void AsyncEventNotifier::terminate()
+ {
+ return AsyncEventNotifierBase::terminate();
+ }
+
+ namespace {
+
+ std::mutex& GetTheNotifiersMutex()
+ {
+ static std::mutex MUTEX;
+ return MUTEX;
+ }
+
+ }
+
+ static std::vector<std::weak_ptr<AsyncEventNotifierAutoJoin>> g_Notifiers;
+
+ void JoinAsyncEventNotifiers()
+ {
+ std::vector<std::weak_ptr<AsyncEventNotifierAutoJoin>> notifiers;
+ {
+ std::scoped_lock g(GetTheNotifiersMutex());
+ notifiers = g_Notifiers;
+ }
+ for (std::weak_ptr<AsyncEventNotifierAutoJoin> const& wNotifier : notifiers)
+ {
+ std::shared_ptr<AsyncEventNotifierAutoJoin> const pNotifier(
+ wNotifier.lock());
+ if (pNotifier)
+ {
+ pNotifier->terminate();
+ pNotifier->join();
+ }
+ }
+ // note it's possible that g_Notifiers isn't empty now in case of leaks,
+ // particularly since the UNO service manager isn't disposed yet
+ }
+
+ AsyncEventNotifierAutoJoin::AsyncEventNotifierAutoJoin(char const* name)
+ {
+ m_xImpl->name = name;
+ }
+
+ AsyncEventNotifierAutoJoin::~AsyncEventNotifierAutoJoin()
+ {
+ std::scoped_lock g(GetTheNotifiersMutex());
+ // note: this doesn't happen atomically with the refcount
+ // hence it's possible this deletes > 1 or 0 elements
+ g_Notifiers.erase(
+ std::remove_if(g_Notifiers.begin(), g_Notifiers.end(),
+ [](std::weak_ptr<AsyncEventNotifierAutoJoin> const& w) {
+ return w.expired();
+ } ),
+ g_Notifiers.end());
+ }
+
+ std::shared_ptr<AsyncEventNotifierAutoJoin>
+ AsyncEventNotifierAutoJoin::newAsyncEventNotifierAutoJoin(char const* name)
+ {
+ std::shared_ptr<AsyncEventNotifierAutoJoin> const ret(
+ new AsyncEventNotifierAutoJoin(name));
+ std::scoped_lock g(GetTheNotifiersMutex());
+ g_Notifiers.push_back(ret);
+ return ret;
+ }
+
+ void AsyncEventNotifierAutoJoin::terminate()
+ {
+ return AsyncEventNotifierBase::terminate();
+ }
+
+ void AsyncEventNotifierAutoJoin::launch(std::shared_ptr<AsyncEventNotifierAutoJoin> const& xThis)
+ {
+ // see salhelper::Thread::launch
+ xThis->m_xImpl->pKeepThisAlive = xThis;
+ comphelper::ScopeGuard g([&xThis] { xThis->m_xImpl->pKeepThisAlive.reset(); });
+ if (!xThis->create()) {
+ throw std::runtime_error("osl::Thread::create failed");
+ }
+ g.dismiss();
+ }
+
+ void AsyncEventNotifierAutoJoin::run()
+ {
+ // see salhelper::Thread::run
+ comphelper::ScopeGuard g([this] { onTerminated(); });
+ setName(m_xImpl->name);
+ execute();
+ g.dismiss();
+ }
+
+ void AsyncEventNotifierAutoJoin::onTerminated()
+ {
+ // try to delete "this"
+ m_xImpl->pKeepThisAlive.reset();
+ }
+
+} // namespace comphelper
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/asyncquithandler.cxx b/comphelper/source/misc/asyncquithandler.cxx
new file mode 100644
index 0000000000..a04534ec92
--- /dev/null
+++ b/comphelper/source/misc/asyncquithandler.cxx
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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 <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/XDesktop2.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+
+#include <comphelper/asyncquithandler.hxx>
+#include <comphelper/processfactory.hxx>
+
+AsyncQuitHandler::AsyncQuitHandler() {}
+
+AsyncQuitHandler& AsyncQuitHandler::instance()
+{
+ static AsyncQuitHandler aInst;
+ return aInst;
+}
+
+void AsyncQuitHandler::QuitApplication()
+{
+ css::uno::Reference<css::frame::XDesktop2> xDesktop
+ = css::frame::Desktop::create(comphelper::getProcessComponentContext());
+ xDesktop->terminate();
+}
+
+IMPL_STATIC_LINK_NOARG(AsyncQuitHandler, OnAsyncQuit, void*, void) { QuitApplication(); }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/automationinvokedzone.cxx b/comphelper/source/misc/automationinvokedzone.cxx
new file mode 100644
index 0000000000..4a71c4a518
--- /dev/null
+++ b/comphelper/source/misc/automationinvokedzone.cxx
@@ -0,0 +1,33 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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 <cassert>
+
+#include <comphelper/automationinvokedzone.hxx>
+
+namespace comphelper::Automation
+{
+thread_local static int nActiveount = 0;
+
+bool AutomationInvokedZone::isActive() { return nActiveount > 0; }
+
+AutomationInvokedZone::AutomationInvokedZone()
+{
+ assert(nActiveount < 1000);
+ nActiveount++;
+}
+
+AutomationInvokedZone::~AutomationInvokedZone()
+{
+ assert(nActiveount > 0);
+ nActiveount--;
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/backupfilehelper.cxx b/comphelper/source/misc/backupfilehelper.cxx
new file mode 100644
index 0000000000..87e1035aa0
--- /dev/null
+++ b/comphelper/source/misc/backupfilehelper.cxx
@@ -0,0 +1,2504 @@
+/* -*- 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/ustring.hxx>
+#include <rtl/bootstrap.hxx>
+#include <sal/log.hxx>
+#include <osl/file.hxx>
+#include <comphelper/backupfilehelper.hxx>
+#include <comphelper/DirectoryHelper.hxx>
+#include <rtl/crc.h>
+#include <algorithm>
+#include <deque>
+#include <memory>
+#include <string_view>
+#include <utility>
+#include <vector>
+#include <zlib.h>
+
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/CommandFailedException.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/deployment/ExtensionManager.hpp>
+#include <com/sun/star/xml/dom/XDocumentBuilder.hpp>
+#include <com/sun/star/xml/dom/DocumentBuilder.hpp>
+#include <com/sun/star/xml/dom/XElement.hpp>
+#include <com/sun/star/xml/dom/XNodeList.hpp>
+#include <com/sun/star/xml/dom/XText.hpp>
+#include <com/sun/star/xml/sax/XSAXSerializable.hpp>
+#include <com/sun/star/xml/sax/Writer.hpp>
+#include <com/sun/star/xml/sax/XWriter.hpp>
+#include <com/sun/star/io/XStream.hpp>
+#include <com/sun/star/io/TempFile.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <cppuhelper/exc_hlp.hxx>
+
+using namespace comphelper;
+using namespace css;
+using namespace css::xml::dom;
+
+const sal_uInt32 BACKUP_FILE_HELPER_BLOCK_SIZE = 16384;
+
+namespace
+{
+ typedef std::shared_ptr< osl::File > FileSharedPtr;
+
+ sal_uInt32 createCrc32(FileSharedPtr const & rCandidate, sal_uInt32 nOffset)
+ {
+ sal_uInt32 nCrc32(0);
+
+ if (rCandidate && osl::File::E_None == rCandidate->open(osl_File_OpenFlag_Read))
+ {
+ sal_uInt8 aArray[BACKUP_FILE_HELPER_BLOCK_SIZE];
+ sal_uInt64 nBytesTransfer(0);
+ sal_uInt64 nSize(0);
+
+ rCandidate->getSize(nSize);
+
+ // set offset in source file - should be zero due to crc32 should
+ // only be needed to be created for new entries, gets loaded with old
+ // ones
+ if (osl::File::E_None == rCandidate->setPos(osl_Pos_Absolut, sal_Int64(nOffset)))
+ {
+ while (nSize != 0)
+ {
+ const sal_uInt64 nToTransfer(std::min(nSize, sal_uInt64(BACKUP_FILE_HELPER_BLOCK_SIZE)));
+
+ if (osl::File::E_None == rCandidate->read(static_cast<void*>(aArray), nToTransfer, nBytesTransfer) && nBytesTransfer == nToTransfer)
+ {
+ // add to crc and reduce size
+ nCrc32 = rtl_crc32(nCrc32, static_cast<void*>(aArray), static_cast<sal_uInt32>(nBytesTransfer));
+ nSize -= nToTransfer;
+ }
+ else
+ {
+ // error - reset to zero again
+ nSize = nCrc32 = 0;
+ }
+ }
+ }
+
+ rCandidate->close();
+ }
+
+ return nCrc32;
+ }
+
+ bool read_sal_uInt32(FileSharedPtr const & rFile, sal_uInt32& rTarget)
+ {
+ sal_uInt8 aArray[4];
+ sal_uInt64 nBaseRead(0);
+
+ // read rTarget
+ if (osl::File::E_None == rFile->read(static_cast<void*>(aArray), 4, nBaseRead) && 4 == nBaseRead)
+ {
+ rTarget = (sal_uInt32(aArray[0]) << 24) + (sal_uInt32(aArray[1]) << 16) + (sal_uInt32(aArray[2]) << 8) + sal_uInt32(aArray[3]);
+ return true;
+ }
+
+ return false;
+ }
+
+ bool write_sal_uInt32(oslFileHandle& rHandle, sal_uInt32 nSource)
+ {
+ sal_uInt8 aArray[4];
+ sal_uInt64 nBaseWritten(0);
+
+ // write nSource
+ aArray[0] = sal_uInt8((nSource & 0xff000000) >> 24);
+ aArray[1] = sal_uInt8((nSource & 0x00ff0000) >> 16);
+ aArray[2] = sal_uInt8((nSource & 0x0000ff00) >> 8);
+ aArray[3] = sal_uInt8(nSource & 0x000000ff);
+
+ return osl_File_E_None == osl_writeFile(rHandle, static_cast<const void*>(aArray), 4, &nBaseWritten) && 4 == nBaseWritten;
+ }
+
+ bool read_OString(FileSharedPtr const & rFile, OString& rTarget)
+ {
+ sal_uInt32 nLength(0);
+
+ if (!read_sal_uInt32(rFile, nLength))
+ {
+ return false;
+ }
+
+ sal_uInt64 nPos;
+ if (osl::File::E_None != rFile->getPos(nPos))
+ return false;
+
+ sal_uInt64 nSize;
+ if (osl::File::E_None != rFile->getSize(nSize))
+ return false;
+
+ const auto nRemainingSize = nSize - nPos;
+ if (nLength > nRemainingSize)
+ return false;
+
+ std::vector<char> aTarget(nLength);
+ sal_uInt64 nBaseRead(0);
+
+ // read rTarget
+ if (osl::File::E_None == rFile->read(static_cast<void*>(aTarget.data()), nLength, nBaseRead) && nLength == nBaseRead)
+ {
+ rTarget = OString(aTarget.data(), static_cast<sal_Int32>(nBaseRead));
+ return true;
+ }
+
+ return false;
+ }
+
+ bool write_OString(oslFileHandle& rHandle, const OString& rSource)
+ {
+ const sal_uInt32 nLength(rSource.getLength());
+
+ if (!write_sal_uInt32(rHandle, nLength))
+ {
+ return false;
+ }
+
+ sal_uInt64 nBaseWritten(0);
+
+ return osl_File_E_None == osl_writeFile(rHandle, static_cast<const void*>(rSource.getStr()), nLength, &nBaseWritten) && nLength == nBaseWritten;
+ }
+
+ OUString createFileURL(
+ std::u16string_view rURL, std::u16string_view rName, std::u16string_view rExt)
+ {
+ OUString aRetval;
+
+ if (!rURL.empty() && !rName.empty())
+ {
+ aRetval = OUString::Concat(rURL) + "/" + rName;
+
+ if (!rExt.empty())
+ {
+ aRetval += OUString::Concat(".") + rExt;
+ }
+ }
+
+ return aRetval;
+ }
+
+ OUString createPackURL(std::u16string_view rURL, std::u16string_view rName)
+ {
+ OUString aRetval;
+
+ if (!rURL.empty() && !rName.empty())
+ {
+ aRetval = OUString::Concat(rURL) + "/" + rName + ".pack";
+ }
+
+ return aRetval;
+ }
+}
+
+namespace
+{
+ enum PackageRepository { USER, SHARED, BUNDLED };
+
+ class ExtensionInfoEntry
+ {
+ private:
+ OString maName; // extension name
+ PackageRepository maRepository; // user|shared|bundled
+ bool mbEnabled; // state
+
+ public:
+ ExtensionInfoEntry()
+ : maRepository(USER),
+ mbEnabled(false)
+ {
+ }
+
+ ExtensionInfoEntry(OString aName, bool bEnabled)
+ : maName(std::move(aName)),
+ maRepository(USER),
+ mbEnabled(bEnabled)
+ {
+ }
+
+ ExtensionInfoEntry(const uno::Reference< deployment::XPackage >& rxPackage)
+ : maName(OUStringToOString(rxPackage->getName(), RTL_TEXTENCODING_ASCII_US)),
+ maRepository(USER),
+ mbEnabled(false)
+ {
+ // check maRepository
+ const OString aRepName(OUStringToOString(rxPackage->getRepositoryName(), RTL_TEXTENCODING_ASCII_US));
+
+ if (aRepName == "shared")
+ {
+ maRepository = SHARED;
+ }
+ else if (aRepName == "bundled")
+ {
+ maRepository = BUNDLED;
+ }
+
+ // check mbEnabled
+ const beans::Optional< beans::Ambiguous< sal_Bool > > option(
+ rxPackage->isRegistered(uno::Reference< task::XAbortChannel >(),
+ uno::Reference< ucb::XCommandEnvironment >()));
+
+ if (option.IsPresent)
+ {
+ ::beans::Ambiguous< sal_Bool > const& reg = option.Value;
+
+ if (!reg.IsAmbiguous)
+ {
+ mbEnabled = reg.Value;
+ }
+ }
+ }
+
+ bool isSameExtension(const ExtensionInfoEntry& rComp) const
+ {
+ return (maRepository == rComp.maRepository && maName == rComp.maName);
+ }
+
+ bool operator<(const ExtensionInfoEntry& rComp) const
+ {
+ if (maRepository == rComp.maRepository)
+ {
+ if (maName == rComp.maName)
+ {
+ return mbEnabled < rComp.mbEnabled;
+ }
+ else
+ {
+ return 0 > maName.compareTo(rComp.maName);
+ }
+ }
+ else
+ {
+ return maRepository < rComp.maRepository;
+ }
+ }
+
+ bool read_entry(FileSharedPtr const & rFile)
+ {
+ // read maName
+ if (!read_OString(rFile, maName))
+ {
+ return false;
+ }
+
+ // read maRepository
+ sal_uInt32 nState(0);
+
+ if (read_sal_uInt32(rFile, nState))
+ {
+ maRepository = static_cast< PackageRepository >(nState);
+ }
+ else
+ {
+ return false;
+ }
+
+ // read mbEnabled
+ if (read_sal_uInt32(rFile, nState))
+ {
+ mbEnabled = static_cast< bool >(nState);
+ }
+ else
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ bool write_entry(oslFileHandle& rHandle) const
+ {
+ // write maName;
+ if (!write_OString(rHandle, maName))
+ {
+ return false;
+ }
+
+ // write maRepository
+ sal_uInt32 nState(maRepository);
+
+ if (!write_sal_uInt32(rHandle, nState))
+ {
+ return false;
+ }
+
+ // write mbEnabled
+ nState = static_cast< sal_uInt32 >(mbEnabled);
+
+ return write_sal_uInt32(rHandle, nState);
+ }
+
+ const OString& getName() const
+ {
+ return maName;
+ }
+
+ bool isEnabled() const
+ {
+ return mbEnabled;
+ }
+ };
+
+ typedef std::vector< ExtensionInfoEntry > ExtensionInfoEntryVector;
+
+ constexpr OUString gaRegPath { u"/registry/com.sun.star.comp.deployment.bundle.PackageRegistryBackend/backenddb.xml"_ustr };
+
+ class ExtensionInfo
+ {
+ private:
+ ExtensionInfoEntryVector maEntries;
+
+ public:
+ ExtensionInfo()
+ {
+ }
+
+ const ExtensionInfoEntryVector& getExtensionInfoEntryVector() const
+ {
+ return maEntries;
+ }
+
+ void reset()
+ {
+ // clear all data
+ maEntries.clear();
+ }
+
+ void createUsingXExtensionManager()
+ {
+ // clear all data
+ reset();
+
+ // create content from current extension configuration
+ uno::Sequence< uno::Sequence< uno::Reference< deployment::XPackage > > > xAllPackages;
+ uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ uno::Reference< deployment::XExtensionManager > m_xExtensionManager = deployment::ExtensionManager::get(xContext);
+
+ try
+ {
+ xAllPackages = m_xExtensionManager->getAllExtensions(uno::Reference< task::XAbortChannel >(),
+ uno::Reference< ucb::XCommandEnvironment >());
+ }
+ catch (const deployment::DeploymentException &)
+ {
+ return;
+ }
+ catch (const ucb::CommandFailedException &)
+ {
+ return;
+ }
+ catch (const ucb::CommandAbortedException &)
+ {
+ return;
+ }
+ catch (const lang::IllegalArgumentException & e)
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException( e.Message,
+ e.Context, anyEx );
+ }
+
+ for (const uno::Sequence< uno::Reference< deployment::XPackage > > & xPackageList : std::as_const(xAllPackages))
+ {
+ for (const uno::Reference< deployment::XPackage > & xPackage : xPackageList)
+ {
+ if (xPackage.is())
+ {
+ maEntries.emplace_back(xPackage);
+ }
+ }
+ }
+
+ if (!maEntries.empty())
+ {
+ // sort the list
+ std::sort(maEntries.begin(), maEntries.end());
+ }
+ }
+
+ private:
+ void visitNodesXMLRead(const uno::Reference< xml::dom::XElement >& rElement)
+ {
+ if (!rElement.is())
+ return;
+
+ const OUString aTagName(rElement->getTagName());
+
+ if (aTagName == "extension")
+ {
+ OUString aAttrUrl(rElement->getAttribute("url"));
+ const OUString aAttrRevoked(rElement->getAttribute("revoked"));
+
+ if (!aAttrUrl.isEmpty())
+ {
+ const sal_Int32 nIndex(aAttrUrl.lastIndexOf('/'));
+
+ if (nIndex > 0 && aAttrUrl.getLength() > nIndex + 1)
+ {
+ aAttrUrl = aAttrUrl.copy(nIndex + 1);
+ }
+
+ const bool bEnabled(aAttrRevoked.isEmpty() || !aAttrRevoked.toBoolean());
+ maEntries.emplace_back(
+ OUStringToOString(aAttrUrl, RTL_TEXTENCODING_ASCII_US),
+ bEnabled);
+ }
+ }
+ else
+ {
+ uno::Reference< xml::dom::XNodeList > aList = rElement->getChildNodes();
+
+ if (aList.is())
+ {
+ const sal_Int32 nLength(aList->getLength());
+
+ for (sal_Int32 a(0); a < nLength; a++)
+ {
+ const uno::Reference< xml::dom::XElement > aChild(aList->item(a), uno::UNO_QUERY);
+
+ if (aChild.is())
+ {
+ visitNodesXMLRead(aChild);
+ }
+ }
+ }
+ }
+ }
+
+ public:
+ void createUserExtensionRegistryEntriesFromXML(std::u16string_view rUserConfigWorkURL)
+ {
+ const OUString aPath(
+ OUString::Concat(rUserConfigWorkURL) + "/uno_packages/cache" + gaRegPath);
+ createExtensionRegistryEntriesFromXML(aPath);
+ }
+
+ void createSharedExtensionRegistryEntriesFromXML(std::u16string_view rUserConfigWorkURL)
+ {
+ const OUString aPath(
+ OUString::Concat(rUserConfigWorkURL) + "/extensions/shared" + gaRegPath);
+ createExtensionRegistryEntriesFromXML(aPath);
+ }
+
+ void createBundledExtensionRegistryEntriesFromXML(std::u16string_view rUserConfigWorkURL)
+ {
+ const OUString aPath(
+ OUString::Concat(rUserConfigWorkURL) + "/extensions/bundled" + gaRegPath);
+ createExtensionRegistryEntriesFromXML(aPath);
+ }
+
+
+ void createExtensionRegistryEntriesFromXML(const OUString& aPath)
+ {
+ if (DirectoryHelper::fileExists(aPath))
+ {
+ uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ uno::Reference< xml::dom::XDocumentBuilder > xBuilder(xml::dom::DocumentBuilder::create(xContext));
+ uno::Reference< xml::dom::XDocument > aDocument = xBuilder->parseURI(aPath);
+
+ if (aDocument.is())
+ {
+ visitNodesXMLRead(aDocument->getDocumentElement());
+ }
+ }
+
+ if (!maEntries.empty())
+ {
+ // sort the list
+ std::sort(maEntries.begin(), maEntries.end());
+ }
+ }
+
+ private:
+ static bool visitNodesXMLChange(
+ const OUString& rTagToSearch,
+ const uno::Reference< xml::dom::XElement >& rElement,
+ const ExtensionInfoEntryVector& rToBeEnabled,
+ const ExtensionInfoEntryVector& rToBeDisabled)
+ {
+ bool bChanged(false);
+
+ if (rElement.is())
+ {
+ const OUString aTagName(rElement->getTagName());
+
+ if (aTagName == rTagToSearch)
+ {
+ const OString aAttrUrl(OUStringToOString(rElement->getAttribute("url"), RTL_TEXTENCODING_ASCII_US));
+ const OUString aAttrRevoked(rElement->getAttribute("revoked"));
+ const bool bEnabled(aAttrRevoked.isEmpty() || !aAttrRevoked.toBoolean());
+
+ if (!aAttrUrl.isEmpty())
+ {
+ for (const auto& enable : rToBeEnabled)
+ {
+ if (-1 != aAttrUrl.indexOf(enable.getName()))
+ {
+ if (!bEnabled)
+ {
+ // needs to be enabled
+ rElement->removeAttribute("revoked");
+ bChanged = true;
+ }
+ }
+ }
+
+ for (const auto& disable : rToBeDisabled)
+ {
+ if (-1 != aAttrUrl.indexOf(disable.getName()))
+ {
+ if (bEnabled)
+ {
+ // needs to be disabled
+ rElement->setAttribute("revoked", "true");
+ bChanged = true;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ uno::Reference< xml::dom::XNodeList > aList = rElement->getChildNodes();
+
+ if (aList.is())
+ {
+ const sal_Int32 nLength(aList->getLength());
+
+ for (sal_Int32 a(0); a < nLength; a++)
+ {
+ const uno::Reference< xml::dom::XElement > aChild(aList->item(a), uno::UNO_QUERY);
+
+ if (aChild.is())
+ {
+ bChanged |= visitNodesXMLChange(
+ rTagToSearch,
+ aChild,
+ rToBeEnabled,
+ rToBeDisabled);
+ }
+ }
+ }
+ }
+ }
+
+ return bChanged;
+ }
+
+ static void visitNodesXMLChangeOneCase(
+ const OUString& rUnoPackagReg,
+ const OUString& rTagToSearch,
+ const ExtensionInfoEntryVector& rToBeEnabled,
+ const ExtensionInfoEntryVector& rToBeDisabled)
+ {
+ if (!DirectoryHelper::fileExists(rUnoPackagReg))
+ return;
+
+ uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ uno::Reference< xml::dom::XDocumentBuilder > xBuilder = xml::dom::DocumentBuilder::create(xContext);
+ uno::Reference< xml::dom::XDocument > aDocument = xBuilder->parseURI(rUnoPackagReg);
+
+ if (!aDocument.is())
+ return;
+
+ if (!visitNodesXMLChange(
+ rTagToSearch,
+ aDocument->getDocumentElement(),
+ rToBeEnabled,
+ rToBeDisabled))
+ return;
+
+ // did change - write back
+ uno::Reference< xml::sax::XSAXSerializable > xSerializer(aDocument, uno::UNO_QUERY);
+
+ if (!xSerializer.is())
+ return;
+
+ // create a SAXWriter
+ uno::Reference< xml::sax::XWriter > const xSaxWriter = xml::sax::Writer::create(xContext);
+ uno::Reference< io::XTempFile > xTempFile = io::TempFile::create(xContext);
+ uno::Reference< io::XOutputStream > xOutStrm = xTempFile->getOutputStream();
+
+ // set output stream and do the serialization
+ xSaxWriter->setOutputStream(xOutStrm);
+ xSerializer->serialize(xSaxWriter, uno::Sequence< beans::StringPair >());
+
+ // get URL from temp file
+ OUString aTempURL = xTempFile->getUri();
+
+ // copy back file
+ if (aTempURL.isEmpty() || !DirectoryHelper::fileExists(aTempURL))
+ return;
+
+ if (DirectoryHelper::fileExists(rUnoPackagReg))
+ {
+ osl::File::remove(rUnoPackagReg);
+ }
+
+#if OSL_DEBUG_LEVEL > 1
+ SAL_WARN_IF(osl::FileBase::E_None != osl::File::move(aTempURL, rUnoPackagReg), "comphelper.backupfilehelper", "could not copy back modified Extension configuration file");
+#else
+ osl::File::move(aTempURL, rUnoPackagReg);
+#endif
+ }
+
+ public:
+ static void changeEnableDisableStateInXML(
+ std::u16string_view rUserConfigWorkURL,
+ const ExtensionInfoEntryVector& rToBeEnabled,
+ const ExtensionInfoEntryVector& rToBeDisabled)
+ {
+ static constexpr OUString aRegPathFront(u"/uno_packages/cache/registry/com.sun.star.comp.deployment."_ustr);
+ static constexpr OUString aRegPathBack(u".PackageRegistryBackend/backenddb.xml"_ustr);
+ // first appearance to check
+ {
+ const OUString aUnoPackagReg(OUString::Concat(rUserConfigWorkURL) + aRegPathFront + "bundle" + aRegPathBack);
+
+ visitNodesXMLChangeOneCase(
+ aUnoPackagReg,
+ "extension",
+ rToBeEnabled,
+ rToBeDisabled);
+ }
+
+ // second appearance to check
+ {
+ const OUString aUnoPackagReg(OUString::Concat(rUserConfigWorkURL) + aRegPathFront + "configuration" + aRegPathBack);
+
+ visitNodesXMLChangeOneCase(
+ aUnoPackagReg,
+ "configuration",
+ rToBeEnabled,
+ rToBeDisabled);
+ }
+
+ // third appearance to check
+ {
+ const OUString aUnoPackagReg(OUString::Concat(rUserConfigWorkURL) + aRegPathFront + "script" + aRegPathBack);
+
+ visitNodesXMLChangeOneCase(
+ aUnoPackagReg,
+ "script",
+ rToBeEnabled,
+ rToBeDisabled);
+ }
+ }
+
+ bool read_entries(FileSharedPtr const & rFile)
+ {
+ // read NumExtensionEntries
+ sal_uInt32 nExtEntries(0);
+
+ if (!read_sal_uInt32(rFile, nExtEntries))
+ {
+ return false;
+ }
+
+ // coverity#1373663 Untrusted loop bound, check file size
+ // isn't utterly broken
+ sal_uInt64 nFileSize(0);
+ rFile->getSize(nFileSize);
+ if (nFileSize < nExtEntries)
+ return false;
+
+ for (sal_uInt32 a(0); a < nExtEntries; a++)
+ {
+ ExtensionInfoEntry aNewEntry;
+
+ if (aNewEntry.read_entry(rFile))
+ {
+ maEntries.push_back(aNewEntry);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ bool write_entries(oslFileHandle& rHandle) const
+ {
+ const sal_uInt32 nExtEntries(maEntries.size());
+
+ if (!write_sal_uInt32(rHandle, nExtEntries))
+ {
+ return false;
+ }
+
+ for (const auto& a : maEntries)
+ {
+ if (!a.write_entry(rHandle))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ bool createTempFile(OUString& rTempFileName)
+ {
+ oslFileHandle aHandle;
+ bool bRetval(false);
+
+ // create current configuration
+ if (maEntries.empty())
+ {
+ createUsingXExtensionManager();
+ }
+
+ // open target temp file and write current configuration to it - it exists until deleted
+ if (osl::File::E_None == osl::FileBase::createTempFile(nullptr, &aHandle, &rTempFileName))
+ {
+ bRetval = write_entries(aHandle);
+
+ // close temp file - it exists until deleted
+ osl_closeFile(aHandle);
+ }
+
+ return bRetval;
+ }
+
+ bool areThereEnabledExtensions() const
+ {
+ for (const auto& a : maEntries)
+ {
+ if (a.isEnabled())
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ };
+}
+
+namespace
+{
+ class PackedFileEntry
+ {
+ private:
+ sal_uInt32 mnFullFileSize; // size in bytes of unpacked original file
+ sal_uInt32 mnPackFileSize; // size in bytes in file backup package (smaller if compressed, same if not)
+ sal_uInt32 mnOffset; // offset in File (zero identifies new file)
+ sal_uInt32 mnCrc32; // checksum
+ FileSharedPtr maFile; // file where to find the data (at offset)
+ bool const mbDoCompress; // flag if this file is scheduled to be compressed when written
+
+ bool copy_content_straight(oslFileHandle& rTargetHandle)
+ {
+ if (!maFile || osl::File::E_None != maFile->open(osl_File_OpenFlag_Read))
+ return false;
+
+ sal_uInt8 aArray[BACKUP_FILE_HELPER_BLOCK_SIZE];
+ sal_uInt64 nBytesTransfer(0);
+ sal_uInt64 nSize(getPackFileSize());
+
+ // set offset in source file - when this is zero, a new file is to be added
+ if (osl::File::E_None == maFile->setPos(osl_Pos_Absolut, sal_Int64(getOffset())))
+ {
+ while (nSize != 0)
+ {
+ const sal_uInt64 nToTransfer(std::min(nSize, sal_uInt64(BACKUP_FILE_HELPER_BLOCK_SIZE)));
+
+ if (osl::File::E_None != maFile->read(static_cast<void*>(aArray), nToTransfer, nBytesTransfer) || nBytesTransfer != nToTransfer)
+ {
+ break;
+ }
+
+ if (osl_File_E_None != osl_writeFile(rTargetHandle, static_cast<const void*>(aArray), nToTransfer, &nBytesTransfer) || nBytesTransfer != nToTransfer)
+ {
+ break;
+ }
+
+ nSize -= nToTransfer;
+ }
+ }
+
+ maFile->close();
+ return (0 == nSize);
+ }
+
+ bool copy_content_compress(oslFileHandle& rTargetHandle)
+ {
+ if (!maFile || osl::File::E_None != maFile->open(osl_File_OpenFlag_Read))
+ return false;
+
+ sal_uInt8 aArray[BACKUP_FILE_HELPER_BLOCK_SIZE];
+ sal_uInt8 aBuffer[BACKUP_FILE_HELPER_BLOCK_SIZE];
+ sal_uInt64 nBytesTransfer(0);
+ sal_uInt64 nSize(getPackFileSize());
+ z_stream zstream;
+ memset(&zstream, 0, sizeof(zstream));
+
+ if (Z_OK == deflateInit(&zstream, Z_BEST_COMPRESSION))
+ {
+ // set offset in source file - when this is zero, a new file is to be added
+ if (osl::File::E_None == maFile->setPos(osl_Pos_Absolut, sal_Int64(getOffset())))
+ {
+ bool bOkay(true);
+
+ while (bOkay && nSize != 0)
+ {
+ const sal_uInt64 nToTransfer(std::min(nSize, sal_uInt64(BACKUP_FILE_HELPER_BLOCK_SIZE)));
+
+ if (osl::File::E_None != maFile->read(static_cast<void*>(aArray), nToTransfer, nBytesTransfer) || nBytesTransfer != nToTransfer)
+ {
+ break;
+ }
+
+ zstream.avail_in = nToTransfer;
+ zstream.next_in = reinterpret_cast<unsigned char*>(aArray);
+
+ do {
+ zstream.avail_out = BACKUP_FILE_HELPER_BLOCK_SIZE;
+ zstream.next_out = reinterpret_cast<unsigned char*>(aBuffer);
+#if !defined Z_PREFIX
+ const sal_Int64 nRetval(deflate(&zstream, nSize == nToTransfer ? Z_FINISH : Z_NO_FLUSH));
+#else
+ const sal_Int64 nRetval(z_deflate(&zstream, nSize == nToTransfer ? Z_FINISH : Z_NO_FLUSH));
+#endif
+ if (Z_STREAM_ERROR == nRetval)
+ {
+ bOkay = false;
+ }
+ else
+ {
+ const sal_uInt64 nAvailable(BACKUP_FILE_HELPER_BLOCK_SIZE - zstream.avail_out);
+
+ if (osl_File_E_None != osl_writeFile(rTargetHandle, static_cast<const void*>(aBuffer), nAvailable, &nBytesTransfer) || nBytesTransfer != nAvailable)
+ {
+ bOkay = false;
+ }
+ }
+ } while (bOkay && 0 == zstream.avail_out);
+
+ if (!bOkay)
+ {
+ break;
+ }
+
+ nSize -= nToTransfer;
+ }
+
+#if !defined Z_PREFIX
+ deflateEnd(&zstream);
+#else
+ z_deflateEnd(&zstream);
+#endif
+ }
+ }
+
+ maFile->close();
+
+ // get compressed size and add to entry
+ if (mnFullFileSize == mnPackFileSize && mnFullFileSize == zstream.total_in)
+ {
+ mnPackFileSize = zstream.total_out;
+ }
+
+ return (0 == nSize);
+ }
+
+ bool copy_content_uncompress(oslFileHandle& rTargetHandle)
+ {
+ if (!maFile || osl::File::E_None != maFile->open(osl_File_OpenFlag_Read))
+ return false;
+
+ sal_uInt8 aArray[BACKUP_FILE_HELPER_BLOCK_SIZE];
+ sal_uInt8 aBuffer[BACKUP_FILE_HELPER_BLOCK_SIZE];
+ sal_uInt64 nBytesTransfer(0);
+ sal_uInt64 nSize(getPackFileSize());
+ z_stream zstream;
+ memset(&zstream, 0, sizeof(zstream));
+
+ if (Z_OK == inflateInit(&zstream))
+ {
+ // set offset in source file - when this is zero, a new file is to be added
+ if (osl::File::E_None == maFile->setPos(osl_Pos_Absolut, sal_Int64(getOffset())))
+ {
+ bool bOkay(true);
+
+ while (bOkay && nSize != 0)
+ {
+ const sal_uInt64 nToTransfer(std::min(nSize, sal_uInt64(BACKUP_FILE_HELPER_BLOCK_SIZE)));
+
+ if (osl::File::E_None != maFile->read(static_cast<void*>(aArray), nToTransfer, nBytesTransfer) || nBytesTransfer != nToTransfer)
+ {
+ break;
+ }
+
+ zstream.avail_in = nToTransfer;
+ zstream.next_in = reinterpret_cast<unsigned char*>(aArray);
+
+ do {
+ zstream.avail_out = BACKUP_FILE_HELPER_BLOCK_SIZE;
+ zstream.next_out = reinterpret_cast<unsigned char*>(aBuffer);
+#if !defined Z_PREFIX
+ const sal_Int64 nRetval(inflate(&zstream, Z_NO_FLUSH));
+#else
+ const sal_Int64 nRetval(z_inflate(&zstream, Z_NO_FLUSH));
+#endif
+ if (Z_STREAM_ERROR == nRetval)
+ {
+ bOkay = false;
+ }
+ else
+ {
+ const sal_uInt64 nAvailable(BACKUP_FILE_HELPER_BLOCK_SIZE - zstream.avail_out);
+
+ if (osl_File_E_None != osl_writeFile(rTargetHandle, static_cast<const void*>(aBuffer), nAvailable, &nBytesTransfer) || nBytesTransfer != nAvailable)
+ {
+ bOkay = false;
+ }
+ }
+ } while (bOkay && 0 == zstream.avail_out);
+
+ if (!bOkay)
+ {
+ break;
+ }
+
+ nSize -= nToTransfer;
+ }
+
+#if !defined Z_PREFIX
+ deflateEnd(&zstream);
+#else
+ z_deflateEnd(&zstream);
+#endif
+ }
+ }
+
+ maFile->close();
+ return (0 == nSize);
+ }
+
+
+ public:
+ // create new, uncompressed entry
+ PackedFileEntry(
+ sal_uInt32 nFullFileSize,
+ sal_uInt32 nCrc32,
+ FileSharedPtr xFile,
+ bool bDoCompress)
+ : mnFullFileSize(nFullFileSize),
+ mnPackFileSize(nFullFileSize),
+ mnOffset(0),
+ mnCrc32(nCrc32),
+ maFile(std::move(xFile)),
+ mbDoCompress(bDoCompress)
+ {
+ }
+
+ // create entry to be loaded as header (read_header)
+ PackedFileEntry()
+ : mnFullFileSize(0),
+ mnPackFileSize(0),
+ mnOffset(0),
+ mnCrc32(0),
+ mbDoCompress(false)
+ {
+ }
+
+ sal_uInt32 getFullFileSize() const
+ {
+ return mnFullFileSize;
+ }
+
+ sal_uInt32 getPackFileSize() const
+ {
+ return mnPackFileSize;
+ }
+
+ sal_uInt32 getOffset() const
+ {
+ return mnOffset;
+ }
+
+ void setOffset(sal_uInt32 nOffset)
+ {
+ mnOffset = nOffset;
+ }
+
+ static sal_uInt32 getEntrySize()
+ {
+ return 12;
+ }
+
+ sal_uInt32 getCrc32() const
+ {
+ return mnCrc32;
+ }
+
+ bool read_header(FileSharedPtr const & rFile)
+ {
+ if (!rFile)
+ {
+ return false;
+ }
+
+ maFile = rFile;
+
+ // read and compute full file size
+ if (!read_sal_uInt32(rFile, mnFullFileSize))
+ {
+ return false;
+ }
+
+ // read and compute entry crc32
+ if (!read_sal_uInt32(rFile, mnCrc32))
+ {
+ return false;
+ }
+
+ // read and compute packed size
+ if (!read_sal_uInt32(rFile, mnPackFileSize))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ bool write_header(oslFileHandle& rHandle) const
+ {
+ // write full file size
+ if (!write_sal_uInt32(rHandle, mnFullFileSize))
+ {
+ return false;
+ }
+
+ // write crc32
+ if (!write_sal_uInt32(rHandle, mnCrc32))
+ {
+ return false;
+ }
+
+ // write packed file size
+ if (!write_sal_uInt32(rHandle, mnPackFileSize))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ bool copy_content(oslFileHandle& rTargetHandle, bool bUncompress)
+ {
+ if (bUncompress)
+ {
+ if (getFullFileSize() == getPackFileSize())
+ {
+ // not compressed, just copy
+ return copy_content_straight(rTargetHandle);
+ }
+ else
+ {
+ // compressed, need to uncompress on copy
+ return copy_content_uncompress(rTargetHandle);
+ }
+ }
+ else if (0 == getOffset())
+ {
+ if (mbDoCompress)
+ {
+ // compressed wanted, need to compress on copy
+ return copy_content_compress(rTargetHandle);
+ }
+ else
+ {
+ // not compressed, straight copy
+ return copy_content_straight(rTargetHandle);
+ }
+ }
+ else
+ {
+ return copy_content_straight(rTargetHandle);
+ }
+ }
+ };
+}
+
+namespace
+{
+ class PackedFile
+ {
+ private:
+ const OUString maURL;
+ std::deque< PackedFileEntry >
+ maPackedFileEntryVector;
+ bool mbChanged;
+
+ public:
+ PackedFile(const OUString& rURL)
+ : maURL(rURL),
+ mbChanged(false)
+ {
+ FileSharedPtr aSourceFile = std::make_shared<osl::File>(rURL);
+
+ if (osl::File::E_None == aSourceFile->open(osl_File_OpenFlag_Read))
+ {
+ sal_uInt64 nBaseLen(0);
+ aSourceFile->getSize(nBaseLen);
+
+ // we need at least File_ID and num entries -> 8byte
+ if (8 < nBaseLen)
+ {
+ sal_uInt8 aArray[4];
+ sal_uInt64 nBaseRead(0);
+
+ // read and check File_ID
+ if (osl::File::E_None == aSourceFile->read(static_cast< void* >(aArray), 4, nBaseRead) && 4 == nBaseRead)
+ {
+ if ('P' == aArray[0] && 'A' == aArray[1] && 'C' == aArray[2] && 'K' == aArray[3])
+ {
+ // read and compute num entries in this file
+ if (osl::File::E_None == aSourceFile->read(static_cast<void*>(aArray), 4, nBaseRead) && 4 == nBaseRead)
+ {
+ sal_uInt32 nEntries((sal_uInt32(aArray[0]) << 24) + (sal_uInt32(aArray[1]) << 16) + (sal_uInt32(aArray[2]) << 8) + sal_uInt32(aArray[3]));
+
+ // if there are entries (and less than max), read them
+ if (nEntries >= 1 && nEntries <= 10)
+ {
+ for (sal_uInt32 a(0); a < nEntries; a++)
+ {
+ // create new entry, read header (size, crc and PackedSize),
+ // set offset and source file
+ PackedFileEntry aEntry;
+
+ if (aEntry.read_header(aSourceFile))
+ {
+ // add to local data
+ maPackedFileEntryVector.push_back(aEntry);
+ }
+ else
+ {
+ // error
+ nEntries = 0;
+ }
+ }
+
+ if (0 == nEntries)
+ {
+ // on read error clear local data
+ maPackedFileEntryVector.clear();
+ }
+ else
+ {
+ // calculate and set offsets to file binary content
+ sal_uInt32 nHeaderSize(8);
+
+ nHeaderSize += maPackedFileEntryVector.size() * PackedFileEntry::getEntrySize();
+
+ sal_uInt32 nOffset(nHeaderSize);
+
+ for (auto& b : maPackedFileEntryVector)
+ {
+ b.setOffset(nOffset);
+ nOffset += b.getPackFileSize();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ aSourceFile->close();
+ }
+
+ if (maPackedFileEntryVector.empty())
+ {
+ // on error or no data get rid of pack file
+ osl::File::remove(maURL);
+ }
+ }
+
+ void flush()
+ {
+ bool bRetval(true);
+
+ if (maPackedFileEntryVector.empty())
+ {
+ // get rid of (now?) empty pack file
+ osl::File::remove(maURL);
+ }
+ else if (mbChanged)
+ {
+ // need to create a new pack file, do this in a temp file to which data
+ // will be copied from local file (so keep it here until this is done)
+ oslFileHandle aHandle = nullptr;
+ OUString aTempURL;
+
+ // open target temp file - it exists until deleted
+ if (osl::File::E_None == osl::FileBase::createTempFile(nullptr, &aHandle, &aTempURL))
+ {
+ sal_uInt8 aArray[4];
+ sal_uInt64 nBaseWritten(0);
+
+ aArray[0] = 'P';
+ aArray[1] = 'A';
+ aArray[2] = 'C';
+ aArray[3] = 'K';
+
+ // write File_ID
+ if (osl_File_E_None == osl_writeFile(aHandle, static_cast<const void*>(aArray), 4, &nBaseWritten) && 4 == nBaseWritten)
+ {
+ const sal_uInt32 nSize(maPackedFileEntryVector.size());
+
+ // write number of entries
+ if (write_sal_uInt32(aHandle, nSize))
+ {
+ // write placeholder for headers. Due to the fact that
+ // PackFileSize for newly added files gets set during
+ // writing the content entry, write headers after content
+ // is written. To do so, write placeholders here
+ sal_uInt32 nWriteSize(0);
+
+ nWriteSize += maPackedFileEntryVector.size() * PackedFileEntry::getEntrySize();
+
+ aArray[0] = aArray[1] = aArray[2] = aArray[3] = 0;
+
+ for (sal_uInt32 a(0); bRetval && a < nWriteSize; a++)
+ {
+ if (osl_File_E_None != osl_writeFile(aHandle, static_cast<const void*>(aArray), 1, &nBaseWritten) || 1 != nBaseWritten)
+ {
+ bRetval = false;
+ }
+ }
+
+ if (bRetval)
+ {
+ // write contents - this may adapt PackFileSize for new
+ // files
+ for (auto& candidate : maPackedFileEntryVector)
+ {
+ if (!candidate.copy_content(aHandle, false))
+ {
+ bRetval = false;
+ break;
+ }
+ }
+ }
+
+ if (bRetval)
+ {
+ // seek back to header start (at position 8)
+ if (osl_File_E_None != osl_setFilePos(aHandle, osl_Pos_Absolut, sal_Int64(8)))
+ {
+ bRetval = false;
+ }
+ }
+
+ if (bRetval)
+ {
+ // write headers
+ for (const auto& candidate : maPackedFileEntryVector)
+ {
+ if (!candidate.write_header(aHandle))
+ {
+ // error
+ bRetval = false;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // close temp file (in all cases) - it exists until deleted
+ osl_closeFile(aHandle);
+
+ if (bRetval)
+ {
+ // copy over existing file by first deleting original
+ // and moving the temp file to old original
+ osl::File::remove(maURL);
+ osl::File::move(aTempURL, maURL);
+ }
+
+ // delete temp file (in all cases - it may be moved already)
+ osl::File::remove(aTempURL);
+ }
+ }
+
+ bool tryPush(FileSharedPtr const & rFileCandidate, bool bCompress)
+ {
+ sal_uInt64 nFileSize(0);
+
+ if (rFileCandidate && osl::File::E_None == rFileCandidate->open(osl_File_OpenFlag_Read))
+ {
+ rFileCandidate->getSize(nFileSize);
+ rFileCandidate->close();
+ }
+
+ if (0 == nFileSize)
+ {
+ // empty file offered
+ return false;
+ }
+
+ bool bNeedToAdd(false);
+ sal_uInt32 nCrc32(0);
+
+ if (maPackedFileEntryVector.empty())
+ {
+ // no backup yet, add as 1st backup
+ bNeedToAdd = true;
+ }
+ else
+ {
+ // already backups there, check if different from last entry
+ const PackedFileEntry& aLastEntry = maPackedFileEntryVector.back();
+
+ // check if file is different
+ if (aLastEntry.getFullFileSize() != static_cast<sal_uInt32>(nFileSize))
+ {
+ // different size, different file
+ bNeedToAdd = true;
+ }
+ else
+ {
+ // same size, check crc32
+ nCrc32 = createCrc32(rFileCandidate, 0);
+
+ if (nCrc32 != aLastEntry.getCrc32())
+ {
+ // different crc, different file
+ bNeedToAdd = true;
+ }
+ }
+ }
+
+ if (bNeedToAdd)
+ {
+ // create crc32 if not yet done
+ if (0 == nCrc32)
+ {
+ nCrc32 = createCrc32(rFileCandidate, 0);
+ }
+
+ // create a file entry for a new file. Offset is set automatically
+ // to 0 to mark the entry as new file entry
+ maPackedFileEntryVector.emplace_back(
+ static_cast< sal_uInt32 >(nFileSize),
+ nCrc32,
+ rFileCandidate,
+ bCompress);
+
+ mbChanged = true;
+ }
+
+ return bNeedToAdd;
+ }
+
+ bool tryPop(oslFileHandle& rHandle)
+ {
+ if (maPackedFileEntryVector.empty())
+ return false;
+
+ // already backups there, check if different from last entry
+ PackedFileEntry& aLastEntry = maPackedFileEntryVector.back();
+
+ // here the uncompress flag has to be determined, true
+ // means to add the file compressed, false means to add it
+ // uncompressed
+ bool bRetval = aLastEntry.copy_content(rHandle, true);
+
+ if (bRetval)
+ {
+ maPackedFileEntryVector.pop_back();
+ mbChanged = true;
+ }
+
+ return bRetval;
+ }
+
+ void tryReduceToNumBackups(sal_uInt16 nNumBackups)
+ {
+ while (maPackedFileEntryVector.size() > nNumBackups)
+ {
+ maPackedFileEntryVector.pop_front();
+ mbChanged = true;
+ }
+ }
+
+ bool empty() const
+ {
+ return maPackedFileEntryVector.empty();
+ }
+ };
+}
+
+namespace comphelper
+{
+ sal_uInt16 BackupFileHelper::mnMaxAllowedBackups = 10;
+ bool BackupFileHelper::mbExitWasCalled = false;
+ bool BackupFileHelper::mbSafeModeDirExists = false;
+ OUString BackupFileHelper::maInitialBaseURL;
+ OUString BackupFileHelper::maUserConfigBaseURL;
+ OUString BackupFileHelper::maUserConfigWorkURL;
+ OUString BackupFileHelper::maRegModName;
+ OUString BackupFileHelper::maExt;
+
+ const OUString& BackupFileHelper::getInitialBaseURL()
+ {
+ if (maInitialBaseURL.isEmpty())
+ {
+ // try to access user layer configuration file URL, the one that
+ // points to registrymodifications.xcu
+ OUString conf("${CONFIGURATION_LAYERS}");
+ rtl::Bootstrap::expandMacros(conf);
+ static constexpr OUString aTokenUser(u"user:"_ustr);
+ sal_Int32 nStart(conf.indexOf(aTokenUser));
+
+ if (-1 != nStart)
+ {
+ nStart += aTokenUser.getLength();
+ sal_Int32 nEnd(conf.indexOf(' ', nStart));
+
+ if (-1 == nEnd)
+ {
+ nEnd = conf.getLength();
+ }
+
+ maInitialBaseURL = conf.copy(nStart, nEnd - nStart);
+ (void)maInitialBaseURL.startsWith("!", &maInitialBaseURL);
+ }
+
+ if (!maInitialBaseURL.isEmpty())
+ {
+ // split URL at extension and at last path separator
+ maUserConfigBaseURL = DirectoryHelper::splitAtLastToken(
+ DirectoryHelper::splitAtLastToken(maInitialBaseURL, '.', maExt), '/',
+ maRegModName);
+ }
+
+ if (!maUserConfigBaseURL.isEmpty())
+ {
+ // check if SafeModeDir exists
+ mbSafeModeDirExists = DirectoryHelper::dirExists(maUserConfigBaseURL + "/" + getSafeModeName());
+ }
+
+ maUserConfigWorkURL = maUserConfigBaseURL;
+
+ if (mbSafeModeDirExists)
+ {
+ // adapt work URL to do all repair op's in the correct directory
+ maUserConfigWorkURL += "/" + getSafeModeName();
+ }
+ }
+
+ return maInitialBaseURL;
+ }
+
+ const OUString& BackupFileHelper::getSafeModeName()
+ {
+ static constexpr OUString aSafeMode(u"SafeMode"_ustr);
+
+ return aSafeMode;
+ }
+
+ BackupFileHelper::BackupFileHelper()
+ : mnNumBackups(2),
+ mnMode(1),
+ mbActive(false),
+ mbExtensions(true),
+ mbCompress(true)
+ {
+ OUString sTokenOut;
+
+ // read configuration item 'SecureUserConfig' -> bool on/off
+ if (rtl::Bootstrap::get("SecureUserConfig", sTokenOut))
+ {
+ mbActive = sTokenOut.toBoolean();
+ }
+
+ if (mbActive)
+ {
+ // ensure existence
+ getInitialBaseURL();
+
+ // if not found, we are out of business (maExt may be empty)
+ mbActive = !maInitialBaseURL.isEmpty() && !maUserConfigBaseURL.isEmpty() && !maRegModName.isEmpty();
+ }
+
+ if (mbActive && rtl::Bootstrap::get("SecureUserConfigNumCopies", sTokenOut))
+ {
+ const sal_uInt16 nConfigNumCopies(static_cast<sal_uInt16>(sTokenOut.toUInt32()));
+
+ // limit to range [1..mnMaxAllowedBackups]
+ mnNumBackups = std::clamp(mnNumBackups, nConfigNumCopies, mnMaxAllowedBackups);
+ }
+
+ if (mbActive && rtl::Bootstrap::get("SecureUserConfigMode", sTokenOut))
+ {
+ const sal_uInt16 nMode(static_cast<sal_uInt16>(sTokenOut.toUInt32()));
+
+ // limit to range [0..2]
+ mnMode = std::min(nMode, sal_uInt16(2));
+ }
+
+ if (mbActive && rtl::Bootstrap::get("SecureUserConfigExtensions", sTokenOut))
+ {
+ mbExtensions = sTokenOut.toBoolean();
+ }
+
+ if (mbActive && rtl::Bootstrap::get("SecureUserConfigCompress", sTokenOut))
+ {
+ mbCompress = sTokenOut.toBoolean();
+ }
+ }
+
+ void BackupFileHelper::setExitWasCalled()
+ {
+ mbExitWasCalled = true;
+ }
+
+ bool BackupFileHelper::getExitWasCalled()
+ {
+ return mbExitWasCalled;
+ }
+
+ void BackupFileHelper::reactOnSafeMode(bool bSafeMode)
+ {
+ // ensure existence of needed paths
+ getInitialBaseURL();
+
+ if (maUserConfigBaseURL.isEmpty())
+ return;
+
+ if (bSafeMode)
+ {
+ if (!mbSafeModeDirExists)
+ {
+ std::set< OUString > aExcludeList;
+
+ // do not move SafeMode directory itself
+ aExcludeList.insert(getSafeModeName());
+
+ // init SafeMode by creating the 'SafeMode' directory and moving
+ // all stuff there. All repairs will happen there. Both Dirs have to exist.
+ // extend maUserConfigWorkURL as needed
+ maUserConfigWorkURL = maUserConfigBaseURL + "/" + getSafeModeName();
+
+ osl::Directory::createPath(maUserConfigWorkURL);
+ DirectoryHelper::moveDirContent(maUserConfigBaseURL, maUserConfigWorkURL, aExcludeList);
+
+ // switch local flag, maUserConfigWorkURL is already reset
+ mbSafeModeDirExists = true;
+ }
+ }
+ else
+ {
+ if (mbSafeModeDirExists)
+ {
+ // SafeMode has ended, return to normal mode by moving all content
+ // from 'SafeMode' directory back to UserDirectory and deleting it.
+ // Both Dirs have to exist
+ std::set< OUString > aExcludeList;
+
+ DirectoryHelper::moveDirContent(maUserConfigWorkURL, maUserConfigBaseURL, aExcludeList);
+ osl::Directory::remove(maUserConfigWorkURL);
+
+ // switch local flag and reset maUserConfigWorkURL
+ mbSafeModeDirExists = false;
+ maUserConfigWorkURL = maUserConfigBaseURL;
+ }
+ }
+ }
+
+ void BackupFileHelper::tryPush()
+ {
+ // no push when SafeModeDir exists, it may be Office's exit after SafeMode
+ // where SafeMode flag is already deleted, but SafeModeDir cleanup is not
+ // done yet (is done at next startup)
+ if (!mbActive || mbSafeModeDirExists)
+ return;
+
+ const OUString aPackURL(getPackURL());
+
+ // ensure dir and file vectors
+ fillDirFileInfo();
+
+ // process all files in question recursively
+ if (!maDirs.empty() || !maFiles.empty())
+ {
+ tryPush_Files(
+ maDirs,
+ maFiles,
+ maUserConfigWorkURL,
+ aPackURL);
+ }
+ }
+
+ void BackupFileHelper::tryPushExtensionInfo()
+ {
+ // no push when SafeModeDir exists, it may be Office's exit after SafeMode
+ // where SafeMode flag is already deleted, but SafeModeDir cleanup is not
+ // done yet (is done at next startup)
+ if (mbActive && mbExtensions && !mbSafeModeDirExists)
+ {
+ const OUString aPackURL(getPackURL());
+
+ tryPush_extensionInfo(aPackURL);
+ }
+ }
+
+ bool BackupFileHelper::isPopPossible()
+ {
+ bool bPopPossible(false);
+
+ if (mbActive)
+ {
+ const OUString aPackURL(getPackURL());
+
+ // ensure dir and file vectors
+ fillDirFileInfo();
+
+ // process all files in question recursively
+ if (!maDirs.empty() || !maFiles.empty())
+ {
+ bPopPossible = isPopPossible_files(
+ maDirs,
+ maFiles,
+ maUserConfigWorkURL,
+ aPackURL);
+ }
+ }
+
+ return bPopPossible;
+ }
+
+ void BackupFileHelper::tryPop()
+ {
+ if (!mbActive)
+ return;
+
+ bool bDidPop(false);
+ const OUString aPackURL(getPackURL());
+
+ // ensure dir and file vectors
+ fillDirFileInfo();
+
+ // process all files in question recursively
+ if (!maDirs.empty() || !maFiles.empty())
+ {
+ bDidPop = tryPop_files(
+ maDirs,
+ maFiles,
+ maUserConfigWorkURL,
+ aPackURL);
+ }
+
+ if (bDidPop)
+ {
+ // try removal of evtl. empty directory
+ osl::Directory::remove(aPackURL);
+ }
+ }
+
+ bool BackupFileHelper::isPopPossibleExtensionInfo() const
+ {
+ bool bPopPossible(false);
+
+ if (mbActive && mbExtensions)
+ {
+ const OUString aPackURL(getPackURL());
+
+ bPopPossible = isPopPossible_extensionInfo(aPackURL);
+ }
+
+ return bPopPossible;
+ }
+
+ void BackupFileHelper::tryPopExtensionInfo()
+ {
+ if (!(mbActive && mbExtensions))
+ return;
+
+ bool bDidPop(false);
+ const OUString aPackURL(getPackURL());
+
+ bDidPop = tryPop_extensionInfo(aPackURL);
+
+ if (bDidPop)
+ {
+ // try removal of evtl. empty directory
+ osl::Directory::remove(aPackURL);
+ }
+ }
+
+ bool BackupFileHelper::isTryDisableAllExtensionsPossible()
+ {
+ // check if there are still enabled extension which can be disabled,
+ // but as we are now in SafeMode, use XML infos for this since the
+ // extensions are not loaded from XExtensionManager
+ class ExtensionInfo aExtensionInfo;
+
+ aExtensionInfo.createUserExtensionRegistryEntriesFromXML(maUserConfigWorkURL);
+
+ return aExtensionInfo.areThereEnabledExtensions();
+ }
+
+ void BackupFileHelper::tryDisableAllExtensions()
+ {
+ // disable all still enabled extensions,
+ // but as we are now in SafeMode, use XML infos for this since the
+ // extensions are not loaded from XExtensionManager
+ ExtensionInfo aCurrentExtensionInfo;
+ const ExtensionInfoEntryVector aToBeEnabled{};
+ ExtensionInfoEntryVector aToBeDisabled;
+
+ aCurrentExtensionInfo.createUserExtensionRegistryEntriesFromXML(maUserConfigWorkURL);
+
+ const ExtensionInfoEntryVector& rCurrentVector = aCurrentExtensionInfo.getExtensionInfoEntryVector();
+
+ for (const auto& rCurrentInfo : rCurrentVector)
+ {
+ if (rCurrentInfo.isEnabled())
+ {
+ aToBeDisabled.push_back(rCurrentInfo);
+ }
+ }
+
+ ExtensionInfo::changeEnableDisableStateInXML(maUserConfigWorkURL, aToBeEnabled, aToBeDisabled);
+ }
+
+ bool BackupFileHelper::isTryDeinstallUserExtensionsPossible()
+ {
+ // check if there are User Extensions installed.
+ class ExtensionInfo aExtensionInfo;
+
+ aExtensionInfo.createUserExtensionRegistryEntriesFromXML(maUserConfigWorkURL);
+
+ return !aExtensionInfo.getExtensionInfoEntryVector().empty();
+ }
+
+ void BackupFileHelper::tryDeinstallUserExtensions()
+ {
+ // delete User Extension installs
+ DirectoryHelper::deleteDirRecursively(maUserConfigWorkURL + "/uno_packages");
+ }
+
+ bool BackupFileHelper::isTryResetSharedExtensionsPossible()
+ {
+ // check if there are shared Extensions installed
+ class ExtensionInfo aExtensionInfo;
+
+ aExtensionInfo.createSharedExtensionRegistryEntriesFromXML(maUserConfigWorkURL);
+
+ return !aExtensionInfo.getExtensionInfoEntryVector().empty();
+ }
+
+ void BackupFileHelper::tryResetSharedExtensions()
+ {
+ // reset shared extension info
+ DirectoryHelper::deleteDirRecursively(maUserConfigWorkURL + "/extensions/shared");
+ }
+
+ bool BackupFileHelper::isTryResetBundledExtensionsPossible()
+ {
+ // check if there are shared Extensions installed
+ class ExtensionInfo aExtensionInfo;
+
+ aExtensionInfo.createBundledExtensionRegistryEntriesFromXML(maUserConfigWorkURL);
+
+ return !aExtensionInfo.getExtensionInfoEntryVector().empty();
+ }
+
+ void BackupFileHelper::tryResetBundledExtensions()
+ {
+ // reset shared extension info
+ DirectoryHelper::deleteDirRecursively(maUserConfigWorkURL + "/extensions/bundled");
+ }
+
+ const std::vector< OUString >& BackupFileHelper::getCustomizationDirNames()
+ {
+ static std::vector< OUString > aDirNames =
+ {
+ "config", // UI config stuff
+ "registry", // most of the registry stuff
+ "psprint", // not really needed, can be abandoned
+ "store", // not really needed, can be abandoned
+ "temp", // not really needed, can be abandoned
+ "pack" // own backup dir
+ };
+
+ return aDirNames;
+ }
+
+ const std::vector< OUString >& BackupFileHelper::getCustomizationFileNames()
+ {
+ static std::vector< OUString > aFileNames =
+ {
+ "registrymodifications.xcu" // personal registry stuff
+ };
+
+ return aFileNames;
+ }
+
+ namespace {
+ uno::Reference<XElement> lcl_getConfigElement(const uno::Reference<XDocument>& xDocument, const OUString& rPath,
+ const OUString& rKey, const OUString& rValue)
+ {
+ uno::Reference< XElement > itemElement = xDocument->createElement("item");
+ itemElement->setAttribute("oor:path", rPath);
+
+ uno::Reference< XElement > propElement = xDocument->createElement("prop");
+ propElement->setAttribute("oor:name", rKey);
+ propElement->setAttribute("oor:op", "replace"); // Replace any other options
+
+ uno::Reference< XElement > valueElement = xDocument->createElement("value");
+ uno::Reference< XText > textElement = xDocument->createTextNode(rValue);
+
+ valueElement->appendChild(textElement);
+ propElement->appendChild(valueElement);
+ itemElement->appendChild(propElement);
+
+ return itemElement;
+ }
+ }
+
+ void BackupFileHelper::tryDisableHWAcceleration()
+ {
+ const OUString aRegistryModifications(maUserConfigWorkURL + "/registrymodifications.xcu");
+ if (!DirectoryHelper::fileExists(aRegistryModifications))
+ return;
+
+ uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ uno::Reference< XDocumentBuilder > xBuilder = DocumentBuilder::create(xContext);
+ uno::Reference< XDocument > xDocument = xBuilder->parseURI(aRegistryModifications);
+ uno::Reference< XElement > xRootElement = xDocument->getDocumentElement();
+
+ xRootElement->appendChild(lcl_getConfigElement(xDocument, "/org.openoffice.Office.Common/VCL",
+ "DisableOpenGL", "true"));
+ xRootElement->appendChild(lcl_getConfigElement(xDocument, "/org.openoffice.Office.Common/Misc",
+ "UseOpenCL", "false"));
+ // Do not disable Skia entirely, just force its CPU-based raster mode.
+ xRootElement->appendChild(lcl_getConfigElement(xDocument, "/org.openoffice.Office.Common/VCL",
+ "ForceSkia", "false"));
+ xRootElement->appendChild(lcl_getConfigElement(xDocument, "/org.openoffice.Office.Common/VCL",
+ "ForceSkiaRaster", "true"));
+
+ OUString aTempURL;
+ {
+ // use the scope to make sure that the temp file gets properly closed before move
+
+ // write back
+ uno::Reference< xml::sax::XSAXSerializable > xSerializer(xDocument, uno::UNO_QUERY);
+
+ if (!xSerializer.is())
+ return;
+
+ // create a SAXWriter
+ uno::Reference< xml::sax::XWriter > const xSaxWriter = xml::sax::Writer::create(xContext);
+ uno::Reference< io::XTempFile > xTempFile = io::TempFile::create(xContext);
+ xTempFile->setRemoveFile(false); // avoid removal of tempfile when leaving the scope
+ uno::Reference< io::XOutputStream > xOutStrm = xTempFile->getOutputStream();
+
+ // set output stream and do the serialization
+ xSaxWriter->setOutputStream(xOutStrm);
+ xSerializer->serialize(xSaxWriter, uno::Sequence< beans::StringPair >());
+
+ // get URL from temp file
+ aTempURL = xTempFile->getUri();
+ }
+
+ // copy back file
+ if (aTempURL.isEmpty() || !DirectoryHelper::fileExists(aTempURL))
+ return;
+
+ if (DirectoryHelper::fileExists(aRegistryModifications))
+ {
+ osl::File::remove(aRegistryModifications);
+ }
+
+ int result = osl::File::move(aTempURL, aRegistryModifications);
+ SAL_WARN_IF(result != osl::FileBase::E_None, "comphelper.backupfilehelper", "could not copy back modified Extension configuration file");
+ }
+
+ bool BackupFileHelper::isTryResetCustomizationsPossible()
+ {
+ // return true if not all of the customization selection dirs or files are deleted
+ const std::vector< OUString >& rDirs = getCustomizationDirNames();
+
+ for (const auto& a : rDirs)
+ {
+ if (DirectoryHelper::dirExists(maUserConfigWorkURL + "/" + a))
+ {
+ return true;
+ }
+ }
+
+ const std::vector< OUString >& rFiles = getCustomizationFileNames();
+
+ for (const auto& b : rFiles)
+ {
+ if (DirectoryHelper::fileExists(maUserConfigWorkURL + "/" + b))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ void BackupFileHelper::tryResetCustomizations()
+ {
+ // delete all of the customization selection dirs
+ const std::vector< OUString >& rDirs = getCustomizationDirNames();
+
+ for (const auto& a : rDirs)
+ {
+ DirectoryHelper::deleteDirRecursively(maUserConfigWorkURL + "/" + a);
+ }
+
+ const std::vector< OUString >& rFiles = getCustomizationFileNames();
+
+ for (const auto& b : rFiles)
+ {
+ osl::File::remove(maUserConfigWorkURL + "/" + b);
+ }
+ }
+
+ void BackupFileHelper::tryResetUserProfile()
+ {
+ // completely delete the current UserProfile
+ DirectoryHelper::deleteDirRecursively(maUserConfigWorkURL);
+ }
+
+ const OUString& BackupFileHelper::getUserProfileURL()
+ {
+ return maUserConfigBaseURL;
+ }
+
+ const OUString& BackupFileHelper::getUserProfileWorkURL()
+ {
+ return maUserConfigWorkURL;
+ }
+
+ /////////////////// helpers ///////////////////////
+
+ OUString BackupFileHelper::getPackURL()
+ {
+ return OUString(maUserConfigWorkURL + "/pack");
+ }
+
+ /////////////////// file push helpers ///////////////////////
+
+ bool BackupFileHelper::tryPush_Files(
+ const std::set< OUString >& rDirs,
+ const std::set< std::pair< OUString, OUString > >& rFiles,
+ std::u16string_view rSourceURL, // source dir without trailing '/'
+ const OUString& rTargetURL // target dir without trailing '/'
+ )
+ {
+ bool bDidPush(false);
+ osl::Directory::createPath(rTargetURL);
+
+ // process files
+ for (const auto& file : rFiles)
+ {
+ bDidPush |= tryPush_file(
+ rSourceURL,
+ rTargetURL,
+ file.first,
+ file.second);
+ }
+
+ // process dirs
+ for (const auto& dir : rDirs)
+ {
+ OUString aNewSourceURL(OUString::Concat(rSourceURL) + "/" + dir);
+ OUString aNewTargetURL(rTargetURL + "/" + dir);
+ std::set< OUString > aNewDirs;
+ std::set< std::pair< OUString, OUString > > aNewFiles;
+
+ DirectoryHelper::scanDirsAndFiles(
+ aNewSourceURL,
+ aNewDirs,
+ aNewFiles);
+
+ if (!aNewDirs.empty() || !aNewFiles.empty())
+ {
+ bDidPush |= tryPush_Files(
+ aNewDirs,
+ aNewFiles,
+ aNewSourceURL,
+ aNewTargetURL);
+ }
+ }
+
+ if (!bDidPush)
+ {
+ // try removal of evtl. empty directory
+ osl::Directory::remove(rTargetURL);
+ }
+
+ return bDidPush;
+ }
+
+ bool BackupFileHelper::tryPush_file(
+ std::u16string_view rSourceURL, // source dir without trailing '/'
+ std::u16string_view rTargetURL, // target dir without trailing '/'
+ std::u16string_view rName, // filename
+ std::u16string_view rExt // extension (or empty)
+ )
+ {
+ const OUString aFileURL(createFileURL(rSourceURL, rName, rExt));
+
+ if (DirectoryHelper::fileExists(aFileURL))
+ {
+ const OUString aPackURL(createPackURL(rTargetURL, rName));
+ PackedFile aPackedFile(aPackURL);
+ FileSharedPtr aBaseFile = std::make_shared<osl::File>(aFileURL);
+
+ if (aPackedFile.tryPush(aBaseFile, mbCompress))
+ {
+ // reduce to allowed number and flush
+ aPackedFile.tryReduceToNumBackups(mnNumBackups);
+ aPackedFile.flush();
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /////////////////// file pop possibilities helper ///////////////////////
+
+ bool BackupFileHelper::isPopPossible_files(
+ const std::set< OUString >& rDirs,
+ const std::set< std::pair< OUString, OUString > >& rFiles,
+ std::u16string_view rSourceURL, // source dir without trailing '/'
+ std::u16string_view rTargetURL // target dir without trailing '/'
+ )
+ {
+ bool bPopPossible(false);
+
+ // process files
+ for (const auto& file : rFiles)
+ {
+ bPopPossible |= isPopPossible_file(
+ rSourceURL,
+ rTargetURL,
+ file.first,
+ file.second);
+ }
+
+ // process dirs
+ for (const auto& dir : rDirs)
+ {
+ OUString aNewSourceURL(OUString::Concat(rSourceURL) + "/" + dir);
+ OUString aNewTargetURL(OUString::Concat(rTargetURL) + "/" + dir);
+ std::set< OUString > aNewDirs;
+ std::set< std::pair< OUString, OUString > > aNewFiles;
+
+ DirectoryHelper::scanDirsAndFiles(
+ aNewSourceURL,
+ aNewDirs,
+ aNewFiles);
+
+ if (!aNewDirs.empty() || !aNewFiles.empty())
+ {
+ bPopPossible |= isPopPossible_files(
+ aNewDirs,
+ aNewFiles,
+ aNewSourceURL,
+ aNewTargetURL);
+ }
+ }
+
+ return bPopPossible;
+ }
+
+ bool BackupFileHelper::isPopPossible_file(
+ std::u16string_view rSourceURL, // source dir without trailing '/'
+ std::u16string_view rTargetURL, // target dir without trailing '/'
+ std::u16string_view rName, // filename
+ std::u16string_view rExt // extension (or empty)
+ )
+ {
+ const OUString aFileURL(createFileURL(rSourceURL, rName, rExt));
+
+ if (DirectoryHelper::fileExists(aFileURL))
+ {
+ const OUString aPackURL(createPackURL(rTargetURL, rName));
+ PackedFile aPackedFile(aPackURL);
+
+ return !aPackedFile.empty();
+ }
+
+ return false;
+ }
+
+ /////////////////// file pop helpers ///////////////////////
+
+ bool BackupFileHelper::tryPop_files(
+ const std::set< OUString >& rDirs,
+ const std::set< std::pair< OUString, OUString > >& rFiles,
+ std::u16string_view rSourceURL, // source dir without trailing '/'
+ const OUString& rTargetURL // target dir without trailing '/'
+ )
+ {
+ bool bDidPop(false);
+
+ // process files
+ for (const auto& file : rFiles)
+ {
+ bDidPop |= tryPop_file(
+ rSourceURL,
+ rTargetURL,
+ file.first,
+ file.second);
+ }
+
+ // process dirs
+ for (const auto& dir : rDirs)
+ {
+ OUString aNewSourceURL(OUString::Concat(rSourceURL) + "/" + dir);
+ OUString aNewTargetURL(rTargetURL + "/" + dir);
+ std::set< OUString > aNewDirs;
+ std::set< std::pair< OUString, OUString > > aNewFiles;
+
+ DirectoryHelper::scanDirsAndFiles(
+ aNewSourceURL,
+ aNewDirs,
+ aNewFiles);
+
+ if (!aNewDirs.empty() || !aNewFiles.empty())
+ {
+ bDidPop |= tryPop_files(
+ aNewDirs,
+ aNewFiles,
+ aNewSourceURL,
+ aNewTargetURL);
+ }
+ }
+
+ if (bDidPop)
+ {
+ // try removal of evtl. empty directory
+ osl::Directory::remove(rTargetURL);
+ }
+
+ return bDidPop;
+ }
+
+ bool BackupFileHelper::tryPop_file(
+ std::u16string_view rSourceURL, // source dir without trailing '/'
+ std::u16string_view rTargetURL, // target dir without trailing '/'
+ std::u16string_view rName, // filename
+ std::u16string_view rExt // extension (or empty)
+ )
+ {
+ const OUString aFileURL(createFileURL(rSourceURL, rName, rExt));
+
+ if (!DirectoryHelper::fileExists(aFileURL))
+ return false;
+
+ // try Pop for base file
+ const OUString aPackURL(createPackURL(rTargetURL, rName));
+ PackedFile aPackedFile(aPackURL);
+
+ if (aPackedFile.empty())
+ return false;
+
+ oslFileHandle aHandle;
+ OUString aTempURL;
+
+ // open target temp file - it exists until deleted
+ if (osl::File::E_None != osl::FileBase::createTempFile(nullptr, &aHandle, &aTempURL))
+ return false;
+
+ bool bRetval(aPackedFile.tryPop(aHandle));
+
+ // close temp file (in all cases) - it exists until deleted
+ osl_closeFile(aHandle);
+
+ if (bRetval)
+ {
+ // copy over existing file by first deleting original
+ // and moving the temp file to old original
+ osl::File::remove(aFileURL);
+ osl::File::move(aTempURL, aFileURL);
+
+ // reduce to allowed number and flush
+ aPackedFile.tryReduceToNumBackups(mnNumBackups);
+ aPackedFile.flush();
+ }
+
+ // delete temp file (in all cases - it may be moved already)
+ osl::File::remove(aTempURL);
+
+ return bRetval;
+ }
+
+ /////////////////// ExtensionInfo helpers ///////////////////////
+
+ bool BackupFileHelper::tryPush_extensionInfo(
+ std::u16string_view rTargetURL // target dir without trailing '/'
+ )
+ {
+ ExtensionInfo aExtensionInfo;
+ OUString aTempURL;
+ bool bRetval(false);
+
+ // create current configuration and write to temp file - it exists until deleted
+ if (aExtensionInfo.createTempFile(aTempURL))
+ {
+ const OUString aPackURL(createPackURL(rTargetURL, u"ExtensionInfo"));
+ PackedFile aPackedFile(aPackURL);
+ FileSharedPtr aBaseFile = std::make_shared<osl::File>(aTempURL);
+
+ if (aPackedFile.tryPush(aBaseFile, mbCompress))
+ {
+ // reduce to allowed number and flush
+ aPackedFile.tryReduceToNumBackups(mnNumBackups);
+ aPackedFile.flush();
+ bRetval = true;
+ }
+ }
+
+ // delete temp file (in all cases)
+ osl::File::remove(aTempURL);
+ return bRetval;
+ }
+
+ bool BackupFileHelper::isPopPossible_extensionInfo(
+ std::u16string_view rTargetURL // target dir without trailing '/'
+ )
+ {
+ // extensionInfo always exists internally, no test needed
+ const OUString aPackURL(createPackURL(rTargetURL, u"ExtensionInfo"));
+ PackedFile aPackedFile(aPackURL);
+
+ return !aPackedFile.empty();
+ }
+
+ bool BackupFileHelper::tryPop_extensionInfo(
+ std::u16string_view rTargetURL // target dir without trailing '/'
+ )
+ {
+ // extensionInfo always exists internally, no test needed
+ const OUString aPackURL(createPackURL(rTargetURL, u"ExtensionInfo"));
+ PackedFile aPackedFile(aPackURL);
+
+ if (aPackedFile.empty())
+ return false;
+
+ oslFileHandle aHandle;
+ OUString aTempURL;
+
+ // open target temp file - it exists until deleted
+ if (osl::File::E_None != osl::FileBase::createTempFile(nullptr, &aHandle, &aTempURL))
+ return false;
+
+ bool bRetval(aPackedFile.tryPop(aHandle));
+
+ // close temp file (in all cases) - it exists until deleted
+ osl_closeFile(aHandle);
+
+ if (bRetval)
+ {
+ // last config is in temp file, load it to ExtensionInfo
+ ExtensionInfo aLoadedExtensionInfo;
+ FileSharedPtr aBaseFile = std::make_shared<osl::File>(aTempURL);
+
+ if (osl::File::E_None == aBaseFile->open(osl_File_OpenFlag_Read))
+ {
+ if (aLoadedExtensionInfo.read_entries(aBaseFile))
+ {
+ // get current extension info, but from XML config files
+ ExtensionInfo aCurrentExtensionInfo;
+
+ aCurrentExtensionInfo.createUserExtensionRegistryEntriesFromXML(maUserConfigWorkURL);
+
+ // now we have loaded last_working (aLoadedExtensionInfo) and
+ // current (aCurrentExtensionInfo) ExtensionInfo and may react on
+ // differences by de/activating these as needed
+ const ExtensionInfoEntryVector& aUserEntries = aCurrentExtensionInfo.getExtensionInfoEntryVector();
+ const ExtensionInfoEntryVector& rLoadedVector = aLoadedExtensionInfo.getExtensionInfoEntryVector();
+ ExtensionInfoEntryVector aToBeDisabled;
+ ExtensionInfoEntryVector aToBeEnabled;
+
+ for (const auto& rCurrentInfo : aUserEntries)
+ {
+ const ExtensionInfoEntry* pLoadedInfo = nullptr;
+
+ for (const auto& rLoadedInfo : rLoadedVector)
+ {
+ if (rCurrentInfo.isSameExtension(rLoadedInfo))
+ {
+ pLoadedInfo = &rLoadedInfo;
+ break;
+ }
+ }
+
+ if (nullptr != pLoadedInfo)
+ {
+ // loaded info contains information about the Extension rCurrentInfo
+ const bool bCurrentEnabled(rCurrentInfo.isEnabled());
+ const bool bLoadedEnabled(pLoadedInfo->isEnabled());
+
+ if (bCurrentEnabled && !bLoadedEnabled)
+ {
+ aToBeDisabled.push_back(rCurrentInfo);
+ }
+ else if (!bCurrentEnabled && bLoadedEnabled)
+ {
+ aToBeEnabled.push_back(rCurrentInfo);
+ }
+ }
+ else
+ {
+ // There is no loaded info about the Extension rCurrentInfo.
+ // It needs to be disabled
+ if (rCurrentInfo.isEnabled())
+ {
+ aToBeDisabled.push_back(rCurrentInfo);
+ }
+ }
+ }
+
+ if (!aToBeDisabled.empty() || !aToBeEnabled.empty())
+ {
+ ExtensionInfo::changeEnableDisableStateInXML(maUserConfigWorkURL, aToBeEnabled, aToBeDisabled);
+ }
+
+ bRetval = true;
+ }
+ }
+
+ // reduce to allowed number and flush
+ aPackedFile.tryReduceToNumBackups(mnNumBackups);
+ aPackedFile.flush();
+ }
+
+ // delete temp file (in all cases - it may be moved already)
+ osl::File::remove(aTempURL);
+
+ return bRetval;
+ }
+
+ /////////////////// FileDirInfo helpers ///////////////////////
+
+ void BackupFileHelper::fillDirFileInfo()
+ {
+ if (!maDirs.empty() || !maFiles.empty())
+ {
+ // already done
+ return;
+ }
+
+ // Information about the configuration and the role/purpose of directories in
+ // the UserConfiguration is taken from: https://wiki.documentfoundation.org/UserProfile
+
+ // fill dir and file info list to work with dependent on work mode
+ switch (mnMode)
+ {
+ case 0:
+ {
+ // simple mode: add just registrymodifications
+ // (the orig file in maInitialBaseURL)
+ maFiles.insert(std::pair< OUString, OUString >(maRegModName, maExt));
+ break;
+ }
+ case 1:
+ {
+ // defined mode: Add a selection of dirs containing User-Defined and thus
+ // valuable configuration information.
+ // This is clearly discussable in every single point and may be adapted/corrected
+ // over time. Main focus is to secure User-Defined/adapted values
+
+ // add registrymodifications (the orig file in maInitialBaseURL)
+ maFiles.insert(std::pair< OUString, OUString >(maRegModName, maExt));
+
+ // User-defined substitution table (Tools/AutoCorrect)
+ maDirs.insert("autocorr");
+
+ // User-Defined AutoText (Edit/AutoText)
+ maDirs.insert("autotext");
+
+ // User-defined Macros
+ maDirs.insert("basic");
+
+ // User-adapted toolbars for modules
+ maDirs.insert("config");
+
+ // Initial and User-defined Databases
+ maDirs.insert("database");
+
+ // most part of registry files
+ maDirs.insert("registry");
+
+ // User-Defined Scripts
+ maDirs.insert("Scripts");
+
+ // Template files
+ maDirs.insert("template");
+
+ // Custom Dictionaries
+ maDirs.insert("wordbook");
+
+ // Questionable - where and how is Extension stuff held and how
+ // does this interact with enabled/disabled states which are extra handled?
+ // Keep out of business until deeper evaluated
+ //
+ // maDirs.insert("extensions");
+ // maDirs.insert("uno-packages");
+ break;
+ }
+ case 2:
+ {
+ // whole directory. To do so, scan directory and exclude some dirs
+ // from which we know they do not need to be secured explicitly. This
+ // should already include registrymodifications, too.
+ DirectoryHelper::scanDirsAndFiles(
+ maUserConfigWorkURL,
+ maDirs,
+ maFiles);
+
+ // should not exist, but for the case an error occurred and it got
+ // copied somehow, avoid further recursive copying/saving
+ maDirs.erase("SafeMode");
+
+ // not really needed, can be abandoned
+ maDirs.erase("psprint");
+
+ // not really needed, can be abandoned
+ maDirs.erase("store");
+
+ // not really needed, can be abandoned
+ maDirs.erase("temp");
+
+ // exclude own backup dir to avoid recursion
+ maDirs.erase("pack");
+
+ break;
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/base64.cxx b/comphelper/source/misc/base64.cxx
new file mode 100644
index 0000000000..2646f297d7
--- /dev/null
+++ b/comphelper/source/misc/base64.cxx
@@ -0,0 +1,212 @@
+/* -*- 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 <cstddef>
+
+#include <comphelper/base64.hxx>
+
+#include <com/sun/star/uno/Sequence.hxx>
+
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+
+using namespace com::sun::star;
+
+namespace comphelper {
+
+const
+ char aBase64EncodeTable[] =
+ { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
+ 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
+ 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' };
+
+const
+ sal_uInt8 aBase64DecodeTable[] =
+ { 62,255,255,255, 63, // 43-47
+// + /
+
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255, 0,255,255, // 48-63
+// 0 1 2 3 4 5 6 7 8 9 =
+
+ 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 64-79
+// A B C D E F G H I J K L M N O
+
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255, // 80-95
+// P Q R S T U V W X Y Z
+
+ 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 96-111
+// a b c d e f g h i j k l m n o
+
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 }; // 112-123
+// p q r s t u v w x y z
+
+
+template <typename C>
+static void ThreeByteToFourByte(const sal_Int8* pBuffer, const sal_Int32 nStart, const sal_Int32 nFullLen, C* aCharBuffer)
+{
+ const sal_Int32 nLen(std::min(nFullLen - nStart, sal_Int32(3)));
+ assert(nLen > 0); // We are never expected to leave the output buffer uninitialized
+
+ sal_Int32 nBinaer;
+ switch (nLen)
+ {
+ case 1:
+ {
+ nBinaer = static_cast<sal_uInt8>(pBuffer[nStart + 0]) << 16;
+ }
+ break;
+ case 2:
+ {
+ nBinaer = (static_cast<sal_uInt8>(pBuffer[nStart + 0]) << 16) +
+ (static_cast<sal_uInt8>(pBuffer[nStart + 1]) << 8);
+ }
+ break;
+ default:
+ {
+ nBinaer = (static_cast<sal_uInt8>(pBuffer[nStart + 0]) << 16) +
+ (static_cast<sal_uInt8>(pBuffer[nStart + 1]) << 8) +
+ static_cast<sal_uInt8>(pBuffer[nStart + 2]);
+ }
+ break;
+ }
+
+ aCharBuffer[2] = aCharBuffer[3] = '=';
+
+ sal_uInt8 nIndex (static_cast<sal_uInt8>((nBinaer & 0xFC0000) >> 18));
+ aCharBuffer[0] = aBase64EncodeTable [nIndex];
+
+ nIndex = static_cast<sal_uInt8>((nBinaer & 0x3F000) >> 12);
+ aCharBuffer[1] = aBase64EncodeTable [nIndex];
+ if (nLen > 1)
+ {
+ nIndex = static_cast<sal_uInt8>((nBinaer & 0xFC0) >> 6);
+ aCharBuffer[2] = aBase64EncodeTable [nIndex];
+ if (nLen > 2)
+ {
+ nIndex = static_cast<sal_uInt8>((nBinaer & 0x3F));
+ aCharBuffer[3] = aBase64EncodeTable [nIndex];
+ }
+ }
+}
+
+template <typename Buffer>
+static void base64encode(Buffer& aStrBuffer, const uno::Sequence<sal_Int8>& aPass)
+{
+ sal_Int32 i(0);
+ sal_Int32 nBufferLength(aPass.getLength());
+ aStrBuffer.ensureCapacity(aStrBuffer.getLength() + (nBufferLength * 4 + 2) / 3);
+ const sal_Int8* pBuffer = aPass.getConstArray();
+ while (i < nBufferLength)
+ {
+ ThreeByteToFourByte(pBuffer, i, nBufferLength, aStrBuffer.appendUninitialized(4));
+ i += 3;
+ }
+}
+
+void Base64::encode(OStringBuffer& aStrBuffer, const uno::Sequence<sal_Int8>& aPass)
+{
+ base64encode(aStrBuffer, aPass);
+}
+
+void Base64::encode(OUStringBuffer& aStrBuffer, const uno::Sequence<sal_Int8>& aPass)
+{
+ base64encode(aStrBuffer, aPass);
+}
+
+void Base64::decode(uno::Sequence<sal_Int8>& aBuffer, std::u16string_view sBuffer)
+{
+ std::size_t nCharsDecoded = decodeSomeChars( aBuffer, sBuffer );
+ OSL_ENSURE( nCharsDecoded == sBuffer.size(), "some bytes left in base64 decoding!" );
+}
+
+std::size_t Base64::decodeSomeChars(uno::Sequence<sal_Int8>& rOutBuffer, std::u16string_view rInBuffer)
+{
+ std::size_t nInBufferLen = rInBuffer.size();
+ std::size_t nMinOutBufferLen = (nInBufferLen / 4) * 3;
+ if( o3tl::make_unsigned(rOutBuffer.getLength()) < nMinOutBufferLen )
+ rOutBuffer.realloc( nMinOutBufferLen );
+
+ const sal_Unicode *pInBuffer = rInBuffer.data();
+ sal_Int8 *pOutBuffer = rOutBuffer.getArray();
+ sal_Int8 *pOutBufferStart = pOutBuffer;
+ std::size_t nCharsDecoded = 0;
+
+ sal_uInt8 aDecodeBuffer[4];
+ sal_Int32 nBytesToDecode = 0;
+ sal_Int32 nBytesGotFromDecoding = 3;
+ std::size_t nInBufferPos= 0;
+ while( nInBufferPos < nInBufferLen )
+ {
+ sal_Unicode cChar = *pInBuffer;
+ if( cChar >= '+' && cChar <= 'z' )
+ {
+ sal_uInt8 nByte = aBase64DecodeTable[cChar-'+'];
+ if( nByte != 255 )
+ {
+ // We have found a valid character!
+ aDecodeBuffer[nBytesToDecode++] = nByte;
+
+ // One '=' character at the end means 2 out bytes
+ // Two '=' characters at the end mean 1 out bytes
+ if( '=' == cChar && nBytesToDecode > 2 )
+ nBytesGotFromDecoding--;
+ if( 4 == nBytesToDecode )
+ {
+ // Four characters found, so we may convert now!
+ sal_uInt32 nOut = (aDecodeBuffer[0] << 18) +
+ (aDecodeBuffer[1] << 12) +
+ (aDecodeBuffer[2] << 6) +
+ aDecodeBuffer[3];
+
+ *pOutBuffer++ = static_cast<sal_Int8>((nOut & 0xff0000) >> 16);
+ if( nBytesGotFromDecoding > 1 )
+ *pOutBuffer++ = static_cast<sal_Int8>((nOut & 0xff00) >> 8);
+ if( nBytesGotFromDecoding > 2 )
+ *pOutBuffer++ = static_cast<sal_Int8>(nOut & 0xff);
+ nCharsDecoded = nInBufferPos + 1;
+ nBytesToDecode = 0;
+ nBytesGotFromDecoding = 3;
+ }
+ }
+ else
+ {
+ nCharsDecoded++;
+ }
+ }
+ else
+ {
+ nCharsDecoded++;
+ }
+
+ nInBufferPos++;
+ pInBuffer++;
+ }
+ if( (pOutBuffer - pOutBufferStart) != rOutBuffer.getLength() )
+ rOutBuffer.realloc( pOutBuffer - pOutBufferStart );
+
+ return nCharsDecoded;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/compbase.cxx b/comphelper/source/misc/compbase.cxx
new file mode 100644
index 0000000000..d88a534777
--- /dev/null
+++ b/comphelper/source/misc/compbase.cxx
@@ -0,0 +1,232 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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 <comphelper/compbase.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+namespace comphelper
+{
+WeakComponentImplHelperBase::~WeakComponentImplHelperBase() {}
+
+// css::lang::XComponent
+void SAL_CALL WeakComponentImplHelperBase::dispose()
+{
+ std::unique_lock aGuard(m_aMutex);
+ if (m_bDisposed)
+ return;
+ m_bDisposed = true;
+ disposing(aGuard);
+ if (!aGuard.owns_lock())
+ aGuard.lock();
+ css::lang::EventObject aEvt(static_cast<OWeakObject*>(this));
+ maEventListeners.disposeAndClear(aGuard, aEvt);
+}
+
+void WeakComponentImplHelperBase::disposing(std::unique_lock<std::mutex>&) {}
+
+void SAL_CALL WeakComponentImplHelperBase::addEventListener(
+ css::uno::Reference<css::lang::XEventListener> const& rxListener)
+{
+ std::unique_lock aGuard(m_aMutex);
+ if (m_bDisposed)
+ return;
+ maEventListeners.addInterface(aGuard, rxListener);
+}
+
+void SAL_CALL WeakComponentImplHelperBase::removeEventListener(
+ css::uno::Reference<css::lang::XEventListener> const& rxListener)
+{
+ std::unique_lock aGuard(m_aMutex);
+ maEventListeners.removeInterface(aGuard, rxListener);
+}
+
+css::uno::Any SAL_CALL WeakComponentImplHelperBase::queryInterface(css::uno::Type const& rType)
+{
+ css::uno::Any aReturn = ::cppu::queryInterface(rType, static_cast<css::uno::XWeak*>(this),
+ static_cast<css::lang::XComponent*>(this));
+ if (aReturn.hasValue())
+ return aReturn;
+ return OWeakObject::queryInterface(rType);
+}
+
+static void checkInterface(css::uno::Type const& rType)
+{
+ if (css::uno::TypeClass_INTERFACE != rType.getTypeClass())
+ {
+ OUString msg("querying for interface \"" + rType.getTypeName() + "\": no interface type!");
+ SAL_WARN("cppuhelper", msg);
+ throw css::uno::RuntimeException(msg);
+ }
+}
+
+static bool isXInterface(rtl_uString* pStr)
+{
+ return OUString::unacquired(&pStr) == "com.sun.star.uno.XInterface";
+}
+
+static bool td_equals(typelib_TypeDescriptionReference const* pTDR1,
+ typelib_TypeDescriptionReference const* pTDR2)
+{
+ return ((pTDR1 == pTDR2)
+ || OUString::unacquired(&pTDR1->pTypeName) == OUString::unacquired(&pTDR2->pTypeName));
+}
+
+static cppu::type_entry* getTypeEntries(cppu::class_data* cd)
+{
+ cppu::type_entry* pEntries = cd->m_typeEntries;
+ if (!cd->m_storedTypeRefs) // not inited?
+ {
+ static std::mutex aMutex;
+ std::scoped_lock guard(aMutex);
+ if (!cd->m_storedTypeRefs) // not inited?
+ {
+ // get all types
+ for (sal_Int32 n = cd->m_nTypes; n--;)
+ {
+ cppu::type_entry* pEntry = &pEntries[n];
+ css::uno::Type const& rType = (*pEntry->m_type.getCppuType)(nullptr);
+ OSL_ENSURE(rType.getTypeClass() == css::uno::TypeClass_INTERFACE,
+ "### wrong helper init: expected interface!");
+ OSL_ENSURE(
+ !isXInterface(rType.getTypeLibType()->pTypeName),
+ "### want to implement XInterface: template argument is XInterface?!?!?!");
+ if (rType.getTypeClass() != css::uno::TypeClass_INTERFACE)
+ {
+ OUString msg("type \"" + rType.getTypeName() + "\" is no interface type!");
+ SAL_WARN("cppuhelper", msg);
+ throw css::uno::RuntimeException(msg);
+ }
+ // ref is statically held by getCppuType()
+ pEntry->m_type.typeRef = rType.getTypeLibType();
+ }
+ OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
+ cd->m_storedTypeRefs = true;
+ }
+ }
+ else
+ {
+ OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
+ }
+ return pEntries;
+}
+
+static void* makeInterface(sal_IntPtr nOffset, void* that)
+{
+ return (static_cast<char*>(that) + nOffset);
+}
+
+static bool recursivelyFindType(typelib_TypeDescriptionReference const* demandedType,
+ typelib_InterfaceTypeDescription const* type, sal_IntPtr* offset)
+{
+ // This code assumes that the vtables of a multiple-inheritance class (the
+ // offset amount by which to adjust the this pointer) follow one another in
+ // the object layout, and that they contain slots for the inherited classes
+ // in a specific order. In theory, that need not hold for any given
+ // platform; in practice, it seems to work well on all supported platforms:
+next:
+ for (sal_Int32 i = 0; i < type->nBaseTypes; ++i)
+ {
+ if (i > 0)
+ {
+ *offset += sizeof(void*);
+ }
+ typelib_InterfaceTypeDescription const* base = type->ppBaseTypes[i];
+ // ignore XInterface:
+ if (base->nBaseTypes > 0)
+ {
+ if (td_equals(reinterpret_cast<typelib_TypeDescriptionReference const*>(base),
+ demandedType))
+ {
+ return true;
+ }
+ // Profiling showed that it is important to speed up the common case
+ // of only one base:
+ if (type->nBaseTypes == 1)
+ {
+ type = base;
+ goto next;
+ }
+ if (recursivelyFindType(demandedType, base, offset))
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+static void* queryDeepNoXInterface(typelib_TypeDescriptionReference const* pDemandedTDR,
+ cppu::class_data* cd, void* that)
+{
+ cppu::type_entry* pEntries = getTypeEntries(cd);
+ sal_Int32 nTypes = cd->m_nTypes;
+ sal_Int32 n;
+
+ // try top interfaces without getting td
+ for (n = 0; n < nTypes; ++n)
+ {
+ if (td_equals(pEntries[n].m_type.typeRef, pDemandedTDR))
+ {
+ return makeInterface(pEntries[n].m_offset, that);
+ }
+ }
+ // query deep getting td
+ for (n = 0; n < nTypes; ++n)
+ {
+ typelib_TypeDescription* pTD = nullptr;
+ TYPELIB_DANGER_GET(&pTD, pEntries[n].m_type.typeRef);
+ if (pTD)
+ {
+ // exclude top (already tested) and bottom (XInterface) interface
+ OSL_ENSURE(reinterpret_cast<typelib_InterfaceTypeDescription*>(pTD)->nBaseTypes > 0,
+ "### want to implement XInterface:"
+ " template argument is XInterface?!?!?!");
+ sal_IntPtr offset = pEntries[n].m_offset;
+ bool found = recursivelyFindType(
+ pDemandedTDR, reinterpret_cast<typelib_InterfaceTypeDescription*>(pTD), &offset);
+ TYPELIB_DANGER_RELEASE(pTD);
+ if (found)
+ {
+ return makeInterface(offset, that);
+ }
+ }
+ else
+ {
+ OUString msg("cannot get type description for type \""
+ + OUString::unacquired(&pEntries[n].m_type.typeRef->pTypeName) + "\"!");
+ SAL_WARN("cppuhelper", msg);
+ throw css::uno::RuntimeException(msg);
+ }
+ }
+ return nullptr;
+}
+
+css::uno::Any WeakComponentImplHelper_query(css::uno::Type const& rType, cppu::class_data* cd,
+ WeakComponentImplHelperBase* pBase)
+{
+ checkInterface(rType);
+ typelib_TypeDescriptionReference* pTDR = rType.getTypeLibType();
+
+ // shortcut XInterface to WeakComponentImplHelperBase
+ if (!isXInterface(pTDR->pTypeName))
+ {
+ void* p = queryDeepNoXInterface(pTDR, cd, pBase);
+ if (p)
+ {
+ return css::uno::Any(&p, pTDR);
+ }
+ }
+ return pBase->comphelper::WeakComponentImplHelperBase::queryInterface(rType);
+}
+
+} // namespace comphelper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/comphelper/source/misc/componentbase.cxx b/comphelper/source/misc/componentbase.cxx
new file mode 100644
index 0000000000..9baec2363f
--- /dev/null
+++ b/comphelper/source/misc/componentbase.cxx
@@ -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 .
+ */
+
+
+#include <comphelper/componentbase.hxx>
+
+#include <com/sun/star/lang/NotInitializedException.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+
+
+namespace comphelper
+{
+
+
+ using ::com::sun::star::lang::NotInitializedException;
+ using ::com::sun::star::lang::DisposedException;
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::XInterface;
+
+ void ComponentBase::checkDisposed( GuardAccess ) const
+ {
+ if ( m_rBHelper.bDisposed )
+ throw DisposedException( OUString(), getComponent() );
+ }
+
+
+ void ComponentBase::checkInitialized( GuardAccess ) const
+ {
+ if ( !m_bInitialized )
+ throw NotInitializedException( OUString(), getComponent() );
+ }
+
+
+ Reference< XInterface > ComponentBase::getComponent()
+ {
+ return nullptr;
+ }
+
+
+} // namespace comphelper
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/configuration.cxx b/comphelper/source/misc/configuration.cxx
new file mode 100644
index 0000000000..6e500f6192
--- /dev/null
+++ b/comphelper/source/misc/configuration.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 <sal/config.h>
+
+#include <cassert>
+
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/configuration/ReadOnlyAccess.hpp>
+#include <com/sun/star/configuration/ReadWriteAccess.hpp>
+#include <com/sun/star/configuration/XReadWriteAccess.hpp>
+#include <com/sun/star/configuration/theDefaultProvider.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/util/XChangesListener.hpp>
+#include <com/sun/star/util/XChangesNotifier.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/XLocalizable.hpp>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+#include <comphelper/solarmutex.hxx>
+#include <comphelper/configuration.hxx>
+#include <comphelper/configurationlistener.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <i18nlangtag/languagetag.hxx>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+namespace {
+
+OUString getDefaultLocale(
+ css::uno::Reference< css::uno::XComponentContext > const & context)
+{
+ return LanguageTag(
+ css::uno::Reference< css::lang::XLocalizable >(
+ css::configuration::theDefaultProvider::get(context),
+ css::uno::UNO_QUERY_THROW)->
+ getLocale()).getBcp47(false);
+}
+
+OUString extendLocalizedPath(std::u16string_view path, OUString const & locale) {
+ SAL_WARN_IF(
+ locale.match("*"), "comphelper",
+ "Locale \"" << locale << "\" starts with \"*\"");
+ assert(locale.indexOf('&') == -1);
+ assert(locale.indexOf('"') == -1);
+ assert(locale.indexOf('\'') == -1);
+ return OUString::Concat(path) + "/['*" + locale + "']";
+}
+
+}
+
+std::shared_ptr< comphelper::ConfigurationChanges >
+comphelper::ConfigurationChanges::create()
+{
+ return detail::ConfigurationWrapper::get().createChanges();
+}
+
+comphelper::ConfigurationChanges::~ConfigurationChanges() {}
+
+void comphelper::ConfigurationChanges::commit() const {
+ access_->commitChanges();
+}
+
+comphelper::ConfigurationChanges::ConfigurationChanges(
+ css::uno::Reference< css::uno::XComponentContext > const & context):
+ access_(
+ css::configuration::ReadWriteAccess::create(
+ context, getDefaultLocale(context)))
+{}
+
+void comphelper::ConfigurationChanges::setPropertyValue(
+ OUString const & path, css::uno::Any const & value) const
+{
+ access_->replaceByHierarchicalName(path, value);
+}
+
+css::uno::Reference< css::container::XHierarchicalNameReplace >
+comphelper::ConfigurationChanges::getGroup(OUString const & path) const
+{
+ return css::uno::Reference< css::container::XHierarchicalNameReplace >(
+ access_->getByHierarchicalName(path), css::uno::UNO_QUERY_THROW);
+}
+
+css::uno::Reference< css::container::XNameContainer >
+comphelper::ConfigurationChanges::getSet(OUString const & path) const
+{
+ return css::uno::Reference< css::container::XNameContainer >(
+ access_->getByHierarchicalName(path), css::uno::UNO_QUERY_THROW);
+}
+
+comphelper::detail::ConfigurationWrapper const &
+comphelper::detail::ConfigurationWrapper::get()
+{
+ static comphelper::detail::ConfigurationWrapper WRAPPER;
+ return WRAPPER;
+}
+
+class comphelper::detail::ConfigurationChangesListener
+ : public ::cppu::WeakImplHelper<css::util::XChangesListener>
+{
+ comphelper::detail::ConfigurationWrapper& mrConfigurationWrapper;
+public:
+ ConfigurationChangesListener(comphelper::detail::ConfigurationWrapper& rWrapper)
+ : mrConfigurationWrapper(rWrapper)
+ {}
+ // util::XChangesListener
+ virtual void SAL_CALL changesOccurred( const css::util::ChangesEvent& ) override
+ {
+ std::scoped_lock aGuard(mrConfigurationWrapper.maMutex);
+ mrConfigurationWrapper.maPropertyCache.clear();
+ }
+ virtual void SAL_CALL disposing(const css::lang::EventObject&) override
+ {
+ std::scoped_lock aGuard(mrConfigurationWrapper.maMutex);
+ mrConfigurationWrapper.mbDisposed = true;
+ mrConfigurationWrapper.maPropertyCache.clear();
+ mrConfigurationWrapper.maNotifier.clear();
+ mrConfigurationWrapper.maListener.clear();
+ }
+};
+
+comphelper::detail::ConfigurationWrapper::ConfigurationWrapper():
+ context_(comphelper::getProcessComponentContext()),
+ access_(css::configuration::ReadWriteAccess::create(context_, "*")),
+ mbDisposed(false)
+{
+ // Set up a configuration notifier to invalidate the cache as needed.
+ try
+ {
+ css::uno::Reference< css::lang::XMultiServiceFactory > xConfigProvider(
+ css::configuration::theDefaultProvider::get( context_ ) );
+
+ // set root path
+ css::uno::Sequence< css::uno::Any > params {
+ css::uno::Any( css::beans::NamedValue{ "nodepath", css::uno::Any( OUString("/"))} ),
+ css::uno::Any( css::beans::NamedValue{ "locale", css::uno::Any( OUString("*"))} ) };
+
+ css::uno::Reference< css::uno::XInterface > xCfg
+ = xConfigProvider->createInstanceWithArguments(u"com.sun.star.configuration.ConfigurationAccess"_ustr,
+ params);
+
+ maNotifier = css::uno::Reference< css::util::XChangesNotifier >(xCfg, css::uno::UNO_QUERY);
+ assert(maNotifier.is());
+ maListener.set(new ConfigurationChangesListener(*this));
+ maNotifier->addChangesListener(maListener);
+ }
+ catch(const css::uno::Exception&)
+ {
+ assert(false);
+ }
+}
+
+comphelper::detail::ConfigurationWrapper::~ConfigurationWrapper()
+{
+ maPropertyCache.clear();
+ maNotifier.clear();
+ maListener.clear();
+}
+
+bool comphelper::detail::ConfigurationWrapper::isReadOnly(OUString const & path)
+ const
+{
+ return
+ (access_->getPropertyByHierarchicalName(path).Attributes
+ & css::beans::PropertyAttribute::READONLY)
+ != 0;
+}
+
+css::uno::Any comphelper::detail::ConfigurationWrapper::getPropertyValue(OUString const& path) const
+{
+ std::scoped_lock aGuard(maMutex);
+ if (mbDisposed)
+ throw css::lang::DisposedException();
+ // Cache the configuration access, since some of the keys are used in hot code.
+ auto it = maPropertyCache.find(path);
+ if( it != maPropertyCache.end())
+ return it->second;
+
+ sal_Int32 idx = path.lastIndexOf("/");
+ assert(idx!=-1);
+ OUString parentPath = path.copy(0, idx);
+ OUString childName = path.copy(idx+1);
+
+ css::uno::Reference<css::container::XNameAccess> access(
+ access_->getByHierarchicalName(parentPath), css::uno::UNO_QUERY_THROW);
+ css::uno::Any property = access->getByName(childName);
+ maPropertyCache.emplace(path, property);
+ return property;
+}
+
+void comphelper::detail::ConfigurationWrapper::setPropertyValue(
+ std::shared_ptr< ConfigurationChanges > const & batch,
+ OUString const & path, css::uno::Any const & value)
+{
+ assert(batch);
+ batch->setPropertyValue(path, value);
+}
+
+css::uno::Any
+comphelper::detail::ConfigurationWrapper::getLocalizedPropertyValue(
+ std::u16string_view path) const
+{
+ return access_->getByHierarchicalName(
+ extendLocalizedPath(path, getDefaultLocale(context_)));
+}
+
+void comphelper::detail::ConfigurationWrapper::setLocalizedPropertyValue(
+ std::shared_ptr< ConfigurationChanges > const & batch,
+ OUString const & path, css::uno::Any const & value)
+{
+ assert(batch);
+ batch->setPropertyValue(path, value);
+}
+
+css::uno::Reference< css::container::XHierarchicalNameAccess >
+comphelper::detail::ConfigurationWrapper::getGroupReadOnly(
+ OUString const & path) const
+{
+ return css::uno::Reference< css::container::XHierarchicalNameAccess >(
+ (css::configuration::ReadOnlyAccess::create(
+ context_, getDefaultLocale(context_))->
+ getByHierarchicalName(path)),
+ css::uno::UNO_QUERY_THROW);
+}
+
+css::uno::Reference< css::container::XHierarchicalNameReplace >
+comphelper::detail::ConfigurationWrapper::getGroupReadWrite(
+ std::shared_ptr< ConfigurationChanges > const & batch,
+ OUString const & path)
+{
+ assert(batch);
+ return batch->getGroup(path);
+}
+
+css::uno::Reference< css::container::XNameAccess >
+comphelper::detail::ConfigurationWrapper::getSetReadOnly(
+ OUString const & path) const
+{
+ return css::uno::Reference< css::container::XNameAccess >(
+ (css::configuration::ReadOnlyAccess::create(
+ context_, getDefaultLocale(context_))->
+ getByHierarchicalName(path)),
+ css::uno::UNO_QUERY_THROW);
+}
+
+css::uno::Reference< css::container::XNameContainer >
+comphelper::detail::ConfigurationWrapper::getSetReadWrite(
+ std::shared_ptr< ConfigurationChanges > const & batch,
+ OUString const & path)
+{
+ assert(batch);
+ return batch->getSet(path);
+}
+
+std::shared_ptr< comphelper::ConfigurationChanges >
+comphelper::detail::ConfigurationWrapper::createChanges() const {
+ return std::shared_ptr< ConfigurationChanges >(
+ new ConfigurationChanges(context_));
+}
+
+void comphelper::ConfigurationListener::addListener(ConfigurationListenerPropertyBase *pListener)
+{
+ maListeners.push_back( pListener );
+ mxConfig->addPropertyChangeListener( pListener->maName, this );
+ pListener->setProperty( mxConfig->getPropertyValue( pListener->maName ) );
+}
+
+void comphelper::ConfigurationListener::removeListener(ConfigurationListenerPropertyBase *pListener)
+{
+ auto it = std::find( maListeners.begin(), maListeners.end(), pListener );
+ if ( it != maListeners.end() )
+ {
+ maListeners.erase( it );
+ mxConfig->removePropertyChangeListener( pListener->maName, this );
+ }
+}
+
+void comphelper::ConfigurationListener::dispose()
+{
+ for (auto const& listener : maListeners)
+ {
+ mxConfig->removePropertyChangeListener( listener->maName, this );
+ listener->dispose();
+ }
+ maListeners.clear();
+ mxConfig.clear();
+ mbDisposed = true;
+}
+
+void SAL_CALL comphelper::ConfigurationListener::disposing(css::lang::EventObject const &)
+{
+ dispose();
+}
+
+void SAL_CALL comphelper::ConfigurationListener::propertyChange(
+ css::beans::PropertyChangeEvent const &rEvt )
+{
+ // Code is commonly used inside the SolarMutexGuard
+ // so to avoid concurrent writes to the property,
+ // and allow fast, lock-less access, guard here.
+ //
+ // Note that we are abusing rtl::Reference here to do acquire/release because,
+ // unlike osl::Guard, it is tolerant of null pointers, and on some code paths, the
+ // SolarMutex does not exist.
+ rtl::Reference<comphelper::SolarMutex> xMutexGuard( comphelper::SolarMutex::get() );
+
+ assert( rEvt.Source == mxConfig );
+ for (auto const& listener : maListeners)
+ {
+ if ( listener->maName == rEvt.PropertyName )
+ {
+ // ignore rEvt.NewValue - in theory it could be stale => not set.
+ css::uno::Any aValue = mxConfig->getPropertyValue( listener->maName );
+ listener->setProperty( aValue );
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/configurationhelper.cxx b/comphelper/source/misc/configurationhelper.cxx
new file mode 100644
index 0000000000..f3853baeff
--- /dev/null
+++ b/comphelper/source/misc/configurationhelper.cxx
@@ -0,0 +1,170 @@
+/* -*- 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 <comphelper/configurationhelper.hxx>
+#include <comphelper/sequence.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <com/sun/star/util/XChangesBatch.hpp>
+
+
+namespace comphelper{
+
+
+css::uno::Reference< css::uno::XInterface > ConfigurationHelper::openConfig(const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const OUString& sPackage,
+ EConfigurationModes eMode )
+{
+ css::uno::Reference< css::lang::XMultiServiceFactory > xConfigProvider(
+ css::configuration::theDefaultProvider::get( rxContext ) );
+
+ std::vector< css::uno::Any > lParams;
+ css::beans::PropertyValue aParam ;
+
+ // set root path
+ aParam.Name = "nodepath";
+ aParam.Value <<= sPackage;
+ lParams.emplace_back(aParam);
+
+ // enable all locales mode
+ if (eMode & EConfigurationModes::AllLocales)
+ {
+ aParam.Name = "locale";
+ aParam.Value <<= OUString("*");
+ lParams.emplace_back(aParam);
+ }
+
+ // open it
+ css::uno::Reference< css::uno::XInterface > xCFG;
+
+ bool bReadOnly(eMode & EConfigurationModes::ReadOnly);
+ if (bReadOnly)
+ xCFG = xConfigProvider->createInstanceWithArguments(
+ "com.sun.star.configuration.ConfigurationAccess",
+ comphelper::containerToSequence(lParams));
+ else
+ xCFG = xConfigProvider->createInstanceWithArguments(
+ "com.sun.star.configuration.ConfigurationUpdateAccess",
+ comphelper::containerToSequence(lParams));
+
+ return xCFG;
+}
+
+
+css::uno::Any ConfigurationHelper::readRelativeKey(const css::uno::Reference< css::uno::XInterface >& xCFG ,
+ const OUString& sRelPath,
+ const OUString& sKey )
+{
+ css::uno::Reference< css::container::XHierarchicalNameAccess > xAccess(xCFG, css::uno::UNO_QUERY_THROW);
+
+ css::uno::Reference< css::beans::XPropertySet > xProps;
+ xAccess->getByHierarchicalName(sRelPath) >>= xProps;
+ if (!xProps.is())
+ {
+ throw css::container::NoSuchElementException(
+ "The requested path \"" + sRelPath + "\" does not exist.");
+ }
+ return xProps->getPropertyValue(sKey);
+}
+
+
+void ConfigurationHelper::writeRelativeKey(const css::uno::Reference< css::uno::XInterface >& xCFG ,
+ const OUString& sRelPath,
+ const OUString& sKey ,
+ const css::uno::Any& aValue )
+{
+ css::uno::Reference< css::container::XHierarchicalNameAccess > xAccess(xCFG, css::uno::UNO_QUERY_THROW);
+
+ css::uno::Reference< css::beans::XPropertySet > xProps;
+ xAccess->getByHierarchicalName(sRelPath) >>= xProps;
+ if (!xProps.is())
+ {
+ throw css::container::NoSuchElementException(
+ "The requested path \"" + sRelPath + "\" does not exist.");
+ }
+ xProps->setPropertyValue(sKey, aValue);
+}
+
+
+css::uno::Reference< css::uno::XInterface > ConfigurationHelper::makeSureSetNodeExists(const css::uno::Reference< css::uno::XInterface >& xCFG ,
+ const OUString& sRelPathToSet,
+ const OUString& sSetNode )
+{
+ css::uno::Reference< css::container::XHierarchicalNameAccess > xAccess(xCFG, css::uno::UNO_QUERY_THROW);
+ css::uno::Reference< css::container::XNameAccess > xSet;
+ xAccess->getByHierarchicalName(sRelPathToSet) >>= xSet;
+ if (!xSet.is())
+ {
+ throw css::container::NoSuchElementException(
+ "The requested path \"" + sRelPathToSet + "\" does not exist." );
+ }
+
+ css::uno::Reference< css::uno::XInterface > xNode;
+ if (xSet->hasByName(sSetNode))
+ xSet->getByName(sSetNode) >>= xNode;
+ else
+ {
+ css::uno::Reference< css::lang::XSingleServiceFactory > xNodeFactory(xSet, css::uno::UNO_QUERY_THROW);
+ xNode = xNodeFactory->createInstance();
+ css::uno::Reference< css::container::XNameContainer > xSetReplace(xSet, css::uno::UNO_QUERY_THROW);
+ xSetReplace->insertByName(sSetNode, css::uno::Any(xNode));
+ }
+
+ return xNode;
+}
+
+
+css::uno::Any ConfigurationHelper::readDirectKey(const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const OUString& sPackage,
+ const OUString& sRelPath,
+ const OUString& sKey ,
+ EConfigurationModes eMode )
+{
+ css::uno::Reference< css::uno::XInterface > xCFG = ConfigurationHelper::openConfig(rxContext, sPackage, eMode);
+ return ConfigurationHelper::readRelativeKey(xCFG, sRelPath, sKey);
+}
+
+
+void ConfigurationHelper::writeDirectKey(const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const OUString& sPackage,
+ const OUString& sRelPath,
+ const OUString& sKey ,
+ const css::uno::Any& aValue ,
+ EConfigurationModes eMode )
+{
+ css::uno::Reference< css::uno::XInterface > xCFG = ConfigurationHelper::openConfig(rxContext, sPackage, eMode);
+ ConfigurationHelper::writeRelativeKey(xCFG, sRelPath, sKey, aValue);
+ ConfigurationHelper::flush(xCFG);
+}
+
+
+void ConfigurationHelper::flush(const css::uno::Reference< css::uno::XInterface >& xCFG)
+{
+ css::uno::Reference< css::util::XChangesBatch > xBatch(xCFG, css::uno::UNO_QUERY_THROW);
+ xBatch->commitChanges();
+}
+
+} // namespace comphelper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/date.cxx b/comphelper/source/misc/date.cxx
new file mode 100644
index 0000000000..b95f63f75c
--- /dev/null
+++ b/comphelper/source/misc/date.cxx
@@ -0,0 +1,210 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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 <comphelper/date.hxx>
+
+#include <cassert>
+
+namespace comphelper::date
+{
+// Once upon a time the number of days we internally handled in tools' class
+// Date was limited to MAX_DAYS 3636532. That changed with a full 16-bit year.
+// Assuming the first valid positive date in a proleptic Gregorian calendar is
+// 0001-01-01, this resulted in an end date of 9957-06-26.
+// Hence we documented that years up to and including 9956 are handled.
+/* XXX: it is unclear history why this value was chosen, the representable
+ * 9999-12-31 would be 3652060 days from 0001-01-01. Even 9998-12-31 to
+ * distinguish from a maximum possible date would be 3651695.
+ * There is connectivity/source/commontools/dbconversion.cxx that still has the
+ * same value to calculate with css::util::Date */
+/* XXX can that dbconversion cope with years > 9999 or negative years at all?
+ * Database fields may be limited to positive 4 digits. */
+
+constexpr sal_Int32 MIN_DAYS = -11968265; // -32768-01-01
+constexpr sal_Int32 MAX_DAYS = 11967900; // 32767-12-31
+
+constexpr sal_Int16 kYearMax = SAL_MAX_INT16;
+constexpr sal_Int16 kYearMin = SAL_MIN_INT16;
+
+constexpr sal_Int32 nNullDateDays = convertDateToDays(30, 12, 1899);
+static_assert(nNullDateDays == 693594);
+
+sal_Int32 convertDateToDaysNormalizing(sal_uInt16 nDay, sal_uInt16 nMonth, sal_Int16 nYear)
+{
+ // Speed-up the common null-date 1899-12-30.
+ if (nYear == 1899 && nMonth == 12 && nDay == 30)
+ return nNullDateDays;
+
+ normalize(nDay, nMonth, nYear);
+ return convertDateToDays(nDay, nMonth, nYear);
+}
+
+bool isValidDate(sal_uInt16 nDay, sal_uInt16 nMonth, sal_Int16 nYear)
+{
+ if (nYear == 0)
+ return false;
+ if (nMonth < 1 || 12 < nMonth)
+ return false;
+ if (nDay < 1 || (nDay > comphelper::date::getDaysInMonth(nMonth, nYear)))
+ return false;
+ return true;
+}
+
+void convertDaysToDate(sal_Int32 nDays, sal_uInt16& rDay, sal_uInt16& rMonth, sal_Int16& rYear)
+{
+ if (nDays <= MIN_DAYS)
+ {
+ rDay = 1;
+ rMonth = 1;
+ rYear = kYearMin;
+ return;
+ }
+ if (nDays >= MAX_DAYS)
+ {
+ rDay = 31;
+ rMonth = 12;
+ rYear = kYearMax;
+ return;
+ }
+
+ // Day 0 is -0001-12-31, day 1 is 0001-01-01
+ const sal_Int16 nSign = (nDays <= 0 ? -1 : 1);
+ sal_Int32 nTempDays;
+ sal_Int32 i = 0;
+ bool bCalc;
+
+ do
+ {
+ rYear = static_cast<sal_Int16>((nDays / 365) - (i * nSign));
+ if (rYear == 0)
+ rYear = nSign;
+ nTempDays = nDays - YearToDays(rYear);
+ bCalc = false;
+ if (nTempDays < 1)
+ {
+ i += nSign;
+ bCalc = true;
+ }
+ else
+ {
+ if (nTempDays > 365)
+ {
+ if ((nTempDays != 366) || !isLeapYear(rYear))
+ {
+ i -= nSign;
+ bCalc = true;
+ }
+ }
+ }
+ } while (bCalc);
+
+ rMonth = 1;
+ while (nTempDays > getDaysInMonth(rMonth, rYear))
+ {
+ nTempDays -= getDaysInMonth(rMonth, rYear);
+ ++rMonth;
+ }
+
+ rDay = static_cast<sal_uInt16>(nTempDays);
+}
+
+bool normalize(sal_uInt16& rDay, sal_uInt16& rMonth, sal_Int16& rYear)
+{
+ if (isValidDate(rDay, rMonth, rYear))
+ return false;
+
+ if (rDay == 0 && rMonth == 0 && rYear == 0)
+ return false; // empty date
+
+ if (rDay == 0)
+ {
+ if (rMonth == 0)
+ ; // nothing, handled below
+ else
+ --rMonth;
+ // Last day of month is determined at the end.
+ }
+
+ if (rMonth > 12)
+ {
+ rYear += rMonth / 12;
+ rMonth = rMonth % 12;
+ if (rYear == 0)
+ rYear = 1;
+ }
+ if (rMonth == 0)
+ {
+ --rYear;
+ if (rYear == 0)
+ rYear = -1;
+ rMonth = 12;
+ }
+
+ if (rYear < 0)
+ {
+ sal_uInt16 nDays;
+ while (rDay > (nDays = getDaysInMonth(rMonth, rYear)))
+ {
+ rDay -= nDays;
+ if (rMonth > 1)
+ --rMonth;
+ else
+ {
+ if (rYear == kYearMin)
+ {
+ rDay = 1;
+ rMonth = 1;
+ return true;
+ }
+ --rYear;
+ rMonth = 12;
+ }
+ }
+ }
+ else
+ {
+ sal_uInt16 nDays;
+ while (rDay > (nDays = getDaysInMonth(rMonth, rYear)))
+ {
+ rDay -= nDays;
+ if (rMonth < 12)
+ ++rMonth;
+ else
+ {
+ if (rYear == kYearMax)
+ {
+ rDay = 31;
+ rMonth = 12;
+ return true;
+ }
+ ++rYear;
+ rMonth = 1;
+ }
+ }
+ }
+
+ if (rDay == 0)
+ rDay = getDaysInMonth(rMonth, rYear);
+
+ return true;
+}
+
+} // namespace comphelper::date
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/comphelper/source/misc/debuggerinfo.cxx b/comphelper/source/misc/debuggerinfo.cxx
new file mode 100644
index 0000000000..1e7116a553
--- /dev/null
+++ b/comphelper/source/misc/debuggerinfo.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/.
+ */
+
+#include <comphelper/debuggerinfo.hxx>
+
+#include <cassert>
+#include <cstring>
+#include <ctype.h>
+
+#if defined(_WIN32)
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#elif defined MACOSX
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#elif defined UNX
+#include <unistd.h>
+#include <fcntl.h>
+#endif
+
+namespace comphelper
+{
+#if defined DBG_UTIL
+bool isDebuggerAttached()
+{
+#if defined(_WIN32)
+ return IsDebuggerPresent();
+#elif defined MACOSX
+ // https://developer.apple.com/library/archive/qa/qa1361/_index.html
+ int junk;
+ int mib[4];
+ struct kinfo_proc info;
+ size_t size;
+
+ // Initialize the flags so that, if sysctl fails for some bizarre
+ // reason, we get a predictable result.
+
+ info.kp_proc.p_flag = 0;
+
+ // Initialize mib, which tells sysctl the info we want, in this case
+ // we're looking for information about a specific process ID.
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_PID;
+ mib[3] = getpid();
+
+ // Call sysctl.
+
+ size = sizeof(info);
+ junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, nullptr, 0);
+ assert(junk == 0);
+
+ // We're being debugged if the P_TRACED flag is set.
+
+ return ((info.kp_proc.p_flag & P_TRACED) != 0);
+#elif defined LINUX
+ char buf[4096];
+ int fd = open("/proc/self/status", O_RDONLY);
+ if (fd < 0)
+ return false;
+ int size = read(fd, buf, sizeof(buf) - 1);
+ close(fd);
+ if (size < 0)
+ return false;
+ assert(size < int(sizeof(buf)) - 1);
+ buf[sizeof(buf) - 1] = '\0';
+ // "TracerPid: <pid>" for pid != 0 means something is attached
+ const char* pos = strstr(buf, "TracerPid:");
+ if (pos == nullptr)
+ return false;
+ pos += strlen("TracerPid:");
+ while (*pos != '\n' && isspace(*pos))
+ ++pos;
+ return *pos != '\n' && *pos != '0';
+#else
+ return false; // feel free to add your platform
+#endif
+}
+#endif
+
+} // namespace comphelper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/diagnose_ex.cxx b/comphelper/source/misc/diagnose_ex.cxx
new file mode 100644
index 0000000000..487f98b637
--- /dev/null
+++ b/comphelper/source/misc/diagnose_ex.cxx
@@ -0,0 +1,392 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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 <com/sun/star/configuration/CorruptedConfigurationException.hpp>
+#include <com/sun/star/configuration/backend/BackendSetupException.hpp>
+#include <com/sun/star/configuration/backend/MalformedDataException.hpp>
+#include <com/sun/star/configuration/InvalidBootstrapFileException.hpp>
+#include <com/sun/star/configuration/MissingBootstrapFileException.hpp>
+#include <com/sun/star/deployment/DependencyException.hpp>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/document/CorruptedFilterConfigurationException.hpp>
+#include <com/sun/star/document/UndoFailedException.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/WrappedTargetException.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/ldap/LdapGenericException.hpp>
+#include <com/sun/star/script/BasicErrorException.hpp>
+#include <com/sun/star/script/CannotConvertException.hpp>
+#include <com/sun/star/script/provider/ScriptExceptionRaisedException.hpp>
+#include <com/sun/star/script/provider/ScriptFrameworkErrorException.hpp>
+#include <com/sun/star/sdbc/SQLException.hpp>
+#include <com/sun/star/system/SystemShellExecuteException.hpp>
+#include <com/sun/star/task/ErrorCodeIOException.hpp>
+#include <com/sun/star/ucb/CommandFailedException.hpp>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/ucb/MissingPropertiesException.hpp>
+#include <com/sun/star/ucb/NameClashException.hpp>
+#include <com/sun/star/ucb/InteractiveIOException.hpp>
+#include <com/sun/star/util/MalformedNumberFormatException.hpp>
+#include <com/sun/star/xml/dom/DOMException.hpp>
+#include <com/sun/star/xml/sax/SAXException.hpp>
+#include <com/sun/star/xml/sax/SAXParseException.hpp>
+#include <comphelper/anytostring.hxx>
+#include <sal/log.hxx>
+#include <osl/thread.h>
+#include <rtl/strbuf.hxx>
+
+#include <comphelper/diagnose_ex.hxx>
+
+#if defined __GLIBCXX__
+#include <cxxabi.h>
+#endif
+
+static void exceptionToStringImpl(OStringBuffer& sMessage, const css::uno::Any & caught)
+{
+ auto toOString = [](OUString const & s) {
+ return OUStringToOString( s, osl_getThreadTextEncoding() );
+ };
+ // when called recursively, we might not have any exception to print
+ if (!caught.hasValue())
+ return;
+ sMessage.append(toOString(caught.getValueTypeName()));
+ css::uno::Exception exception;
+ caught >>= exception;
+ if ( !exception.Message.isEmpty() )
+ {
+ sMessage.append(" message: \"");
+ sMessage.append(toOString(exception.Message));
+ sMessage.append("\"");
+ }
+ if ( exception.Context.is() )
+ {
+ const char* pContext = typeid( *exception.Context ).name();
+#if defined __GLIBCXX__
+ // demangle the type name, not necessary under windows, we already get demangled names there
+ int status;
+ pContext = abi::__cxa_demangle( pContext, nullptr, nullptr, &status);
+#endif
+ sMessage.append(" context: ");
+ sMessage.append(pContext);
+#if defined __GLIBCXX__
+ std::free(const_cast<char *>(pContext));
+#endif
+ }
+ {
+ css::configuration::CorruptedConfigurationException specialized;
+ if ( caught >>= specialized )
+ {
+ sMessage.append(" details: ");
+ sMessage.append(toOString(specialized.Details));
+ }
+ }
+ {
+ css::configuration::InvalidBootstrapFileException specialized;
+ if ( caught >>= specialized )
+ {
+ sMessage.append(" BootstrapFileURL: ");
+ sMessage.append(toOString(specialized.BootstrapFileURL));
+ }
+ }
+ {
+ css::configuration::MissingBootstrapFileException specialized;
+ if ( caught >>= specialized )
+ {
+ sMessage.append(" BootstrapFileURL: ");
+ sMessage.append(toOString(specialized.BootstrapFileURL));
+ }
+ }
+ {
+ css::configuration::backend::MalformedDataException specialized;
+ if ( caught >>= specialized )
+ {
+ sMessage.append("\n wrapped: ");
+ exceptionToStringImpl(sMessage, specialized.ErrorDetails);
+ }
+ }
+ {
+ css::configuration::backend::BackendSetupException specialized;
+ if ( caught >>= specialized )
+ {
+ sMessage.append("\n wrapped: ");
+ exceptionToStringImpl(sMessage, specialized.BackendException);
+ }
+ }
+ {
+ css::deployment::DependencyException specialized;
+ if ( caught >>= specialized )
+ {
+ sMessage.append(" UnsatisfiedDependencies: ");
+ sMessage.append(toOString(comphelper::anyToString(css::uno::Any(specialized.UnsatisfiedDependencies))));
+ }
+ }
+ {
+ css::deployment::DeploymentException specialized;
+ if ( caught >>= specialized )
+ {
+ sMessage.append("\n wrapped: ");
+ exceptionToStringImpl(sMessage, specialized.Cause);
+ }
+ }
+ {
+ css::document::CorruptedFilterConfigurationException specialized;
+ if ( caught >>= specialized )
+ {
+ sMessage.append(" Details: ");
+ sMessage.append(toOString(specialized.Details));
+ }
+ }
+ {
+ css::document::UndoFailedException specialized;
+ if ( caught >>= specialized )
+ {
+ sMessage.append(" Reason: ");
+ sMessage.append(toOString(comphelper::anyToString(specialized.Reason)));
+ }
+ }
+ {
+ css::lang::IllegalArgumentException specialized;
+ if ( caught >>= specialized )
+ {
+ sMessage.append(" ArgumentPosition: ");
+ sMessage.append(static_cast<sal_Int32>(specialized.ArgumentPosition));
+ }
+ }
+ {
+ css::lang::WrappedTargetException specialized;
+ if ( caught >>= specialized )
+ {
+ sMessage.append("\n wrapped: ");
+ exceptionToStringImpl(sMessage, specialized.TargetException);
+ }
+ }
+ {
+ css::lang::WrappedTargetRuntimeException specialized;
+ if ( caught >>= specialized )
+ {
+ sMessage.append("\n wrapped: ");
+ exceptionToStringImpl(sMessage, specialized.TargetException);
+ }
+ }
+ {
+ css::ldap::LdapGenericException specialized;
+ if ( caught >>= specialized )
+ {
+ sMessage.append(" ErrorCode: ");
+ sMessage.append(specialized.ErrorCode);
+ }
+ }
+ {
+ css::script::BasicErrorException specialized;
+ if ( caught >>= specialized )
+ {
+ sMessage.append(" ErrorCode: ");
+ sMessage.append(specialized.ErrorCode);
+ sMessage.append(" ErrorMessageArgument: ");
+ sMessage.append(toOString(specialized.ErrorMessageArgument));
+ }
+ }
+ {
+ css::script::CannotConvertException specialized;
+ if ( caught >>= specialized )
+ {
+ sMessage.append(" DestinationTypeClass: ");
+ sMessage.append(toOString(comphelper::anyToString(css::uno::Any(specialized.DestinationTypeClass))));
+ sMessage.append(" Reason: ");
+ sMessage.append(specialized.Reason);
+ sMessage.append(" ArgumentIndex: ");
+ sMessage.append(specialized.ArgumentIndex);
+ }
+ }
+ {
+ css::script::provider::ScriptErrorRaisedException specialized;
+ if ( caught >>= specialized )
+ {
+ sMessage.append(" scriptName: ");
+ sMessage.append(toOString(specialized.scriptName));
+ sMessage.append(" language: ");
+ sMessage.append(toOString(specialized.language));
+ sMessage.append(" lineNum: ");
+ sMessage.append(specialized.lineNum);
+ }
+ }
+ {
+ css::script::provider::ScriptExceptionRaisedException specialized;
+ if ( caught >>= specialized )
+ {
+ sMessage.append(" exceptionType: ");
+ sMessage.append(toOString(specialized.exceptionType));
+ }
+ }
+ {
+ css::script::provider::ScriptFrameworkErrorException specialized;
+ if ( caught >>= specialized )
+ {
+ sMessage.append(" scriptName: ");
+ sMessage.append(toOString(specialized.scriptName));
+ sMessage.append(" language: ");
+ sMessage.append(toOString(specialized.language));
+ sMessage.append(" errorType: ");
+ sMessage.append(specialized.errorType);
+ }
+ }
+ {
+ css::sdbc::SQLException specialized;
+ if ( caught >>= specialized )
+ {
+ sMessage.append(" SQLState: ");
+ sMessage.append(toOString(specialized.SQLState));
+ sMessage.append(" ErrorCode: ");
+ sMessage.append(specialized.ErrorCode);
+ sMessage.append("\n wrapped: ");
+ exceptionToStringImpl(sMessage, specialized.NextException);
+ }
+ }
+ {
+ css::system::SystemShellExecuteException specialized;
+ if ( caught >>= specialized )
+ {
+ sMessage.append(" PosixError: ");
+ sMessage.append(specialized.PosixError);
+ }
+ }
+ {
+ css::task::ErrorCodeIOException specialized;
+ if ( caught >>= specialized )
+ {
+ sMessage.append(" errcode: ");
+ sMessage.append( specialized.ErrCode );
+ }
+ }
+ {
+ css::ucb::CommandFailedException specialized;
+ if ( caught >>= specialized )
+ {
+ sMessage.append("\n Reason: ");
+ exceptionToStringImpl( sMessage, specialized.Reason );
+ }
+ }
+ {
+ css::ucb::ContentCreationException specialized;
+ if ( caught >>= specialized )
+ {
+ sMessage.append(" eError: ");
+ sMessage.append(toOString(comphelper::anyToString( css::uno::Any(specialized.eError) )));
+ }
+ }
+ {
+ css::ucb::MissingPropertiesException specialized;
+ if ( caught >>= specialized )
+ {
+ sMessage.append(" Properties: ");
+ sMessage.append(toOString(comphelper::anyToString( css::uno::Any(specialized.Properties) )));
+ }
+ }
+ {
+ css::ucb::NameClashException specialized;
+ if ( caught >>= specialized )
+ {
+ sMessage.append(" Name: ");
+ sMessage.append(toOString( specialized.Name ));
+ }
+ }
+ {
+ css::util::MalformedNumberFormatException specialized;
+ if ( caught >>= specialized )
+ {
+ sMessage.append(" CheckPos: ");
+ sMessage.append( specialized.CheckPos );
+ }
+ }
+ {
+ css::xml::dom::DOMException specialized;
+ if ( caught >>= specialized )
+ {
+ sMessage.append(" Code: ");
+ sMessage.append(toOString(comphelper::anyToString( css::uno::Any(specialized.Code) )));
+ }
+ }
+ {
+ css::xml::dom::DOMException specialized;
+ if ( caught >>= specialized )
+ {
+ sMessage.append(" Code: ");
+ sMessage.append(toOString(comphelper::anyToString( css::uno::Any(specialized.Code) )));
+ }
+ }
+ {
+ css::xml::sax::SAXException specialized;
+ if ( caught >>= specialized )
+ {
+ sMessage.append("\n wrapped: ");
+ exceptionToStringImpl( sMessage, specialized.WrappedException );
+ }
+ }
+ {
+ css::xml::sax::SAXParseException specialized;
+ if ( caught >>= specialized )
+ {
+ sMessage.append(" PublicId: ");
+ sMessage.append(toOString( specialized.PublicId ));
+ sMessage.append(" SystemId: ");
+ sMessage.append(toOString( specialized.SystemId ));
+ sMessage.append(" LineNumber: ");
+ sMessage.append( specialized.LineNumber );
+ sMessage.append(" ColumnNumber: ");
+ sMessage.append( specialized.ColumnNumber );
+ }
+ }
+ {
+ css::ucb::InteractiveIOException specialized;
+ if ( caught >>= specialized )
+ {
+ sMessage.append(" Code: ");
+ sMessage.append( static_cast<sal_Int32>(specialized.Code) );
+ }
+ }
+}
+
+OString exceptionToString(const css::uno::Any & caught)
+{
+ OStringBuffer sMessage(512);
+ exceptionToStringImpl(sMessage, caught);
+ return sMessage.makeStringAndClear();
+}
+
+void DbgUnhandledException(const css::uno::Any & caught, const char* currentFunction, const char* fileAndLineNo,
+ const char* area, const char* explanatory)
+{
+ OStringBuffer sMessage( 512 );
+ sMessage.append( OString::Concat("DBG_UNHANDLED_EXCEPTION in ") + currentFunction);
+ if (explanatory)
+ {
+ sMessage.append(OString::Concat("\n when: ") + explanatory);
+ }
+ sMessage.append(" exception: ");
+ exceptionToStringImpl(sMessage, caught);
+
+ if (area == nullptr)
+ area = "legacy.osl";
+
+ SAL_DETAIL_LOG_FORMAT(
+ SAL_DETAIL_ENABLE_LOG_WARN, SAL_DETAIL_LOG_LEVEL_WARN,
+ area, fileAndLineNo, "%s", sMessage.getStr());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/comphelper/source/misc/dispatchcommand.cxx b/comphelper/source/misc/dispatchcommand.cxx
new file mode 100644
index 0000000000..d7b723c725
--- /dev/null
+++ b/comphelper/source/misc/dispatchcommand.cxx
@@ -0,0 +1,81 @@
+/* -*- 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 <comphelper/dispatchcommand.hxx>
+#include <comphelper/processfactory.hxx>
+
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/frame/XNotifyingDispatch.hpp>
+#include <com/sun/star/util/URL.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+
+using namespace css;
+
+namespace comphelper {
+
+bool dispatchCommand(const OUString& rCommand, const uno::Reference<css::frame::XFrame>& rFrame, const css::uno::Sequence<css::beans::PropertyValue>& rArguments, const uno::Reference<css::frame::XDispatchResultListener>& rListener)
+{
+ uno::Reference<frame::XDispatchProvider> xDispatchProvider(rFrame, uno::UNO_QUERY);
+ if (!xDispatchProvider.is())
+ return false;
+
+ util::URL aCommandURL;
+ aCommandURL.Complete = rCommand;
+ uno::Reference<uno::XComponentContext> xContext = ::comphelper::getProcessComponentContext();
+ uno::Reference<util::XURLTransformer> xParser = util::URLTransformer::create(xContext);
+ xParser->parseStrict(aCommandURL);
+
+ uno::Reference<frame::XDispatch> xDisp = xDispatchProvider->queryDispatch(aCommandURL, OUString(), 0);
+ if (!xDisp.is())
+ return false;
+
+ // And do the work...
+ if (rListener.is())
+ {
+ uno::Reference<frame::XNotifyingDispatch> xNotifyingDisp(xDisp, uno::UNO_QUERY);
+ if (xNotifyingDisp.is())
+ {
+ xNotifyingDisp->dispatchWithNotification(aCommandURL, rArguments, rListener);
+ return true;
+ }
+ }
+
+ xDisp->dispatch(aCommandURL, rArguments);
+
+ return true;
+}
+
+bool dispatchCommand(const OUString& rCommand, const css::uno::Sequence<css::beans::PropertyValue>& rArguments, const uno::Reference<css::frame::XDispatchResultListener>& rListener)
+{
+ // Target where we will execute the .uno: command
+ uno::Reference<uno::XComponentContext> xContext = ::comphelper::getProcessComponentContext();
+ uno::Reference<frame::XDesktop2> xDesktop = frame::Desktop::create(xContext);
+
+ uno::Reference<frame::XFrame> xFrame(xDesktop->getActiveFrame());
+ if (!xFrame.is())
+ xFrame = xDesktop;
+
+ return dispatchCommand(rCommand, xFrame, rArguments, rListener);
+}
+
+} // namespace comphelper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/docpasswordhelper.cxx b/comphelper/source/misc/docpasswordhelper.cxx
new file mode 100644
index 0000000000..0adb6eff9a
--- /dev/null
+++ b/comphelper/source/misc/docpasswordhelper.cxx
@@ -0,0 +1,741 @@
+/* -*- 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 <config_gpgme.h>
+
+#include <algorithm>
+#include <string_view>
+
+#include <comphelper/docpasswordhelper.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <comphelper/hash.hxx>
+#include <comphelper/base64.hxx>
+#include <comphelper/sequence.hxx>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <rtl/digest.h>
+#include <rtl/random.h>
+#include <string.h>
+
+#if HAVE_FEATURE_GPGME
+# include <context.h>
+# include <data.h>
+# include <decryptionresult.h>
+#endif
+
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::Exception;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::task::PasswordRequestMode;
+using ::com::sun::star::task::PasswordRequestMode_PASSWORD_ENTER;
+using ::com::sun::star::task::PasswordRequestMode_PASSWORD_REENTER;
+using ::com::sun::star::task::XInteractionHandler;
+
+using namespace ::com::sun::star;
+
+namespace comphelper {
+
+
+static uno::Sequence< sal_Int8 > GeneratePBKDF2Hash( std::u16string_view aPassword, const uno::Sequence< sal_Int8 >& aSalt, sal_Int32 nCount, sal_Int32 nHashLength )
+{
+ uno::Sequence< sal_Int8 > aResult;
+
+ if ( !aPassword.empty() && aSalt.hasElements() && nCount && nHashLength )
+ {
+ OString aBytePass = OUStringToOString( aPassword, RTL_TEXTENCODING_UTF8 );
+ // FIXME this is subject to the SHA1-bug tdf#114939 - see also
+ // RequestPassword() in filedlghelper.cxx
+ aResult.realloc( 16 );
+ rtl_digest_PBKDF2( reinterpret_cast < sal_uInt8 * > ( aResult.getArray() ),
+ aResult.getLength(),
+ reinterpret_cast < const sal_uInt8 * > ( aBytePass.getStr() ),
+ aBytePass.getLength(),
+ reinterpret_cast < const sal_uInt8 * > ( aSalt.getConstArray() ),
+ aSalt.getLength(),
+ nCount );
+ }
+
+ return aResult;
+}
+
+
+IDocPasswordVerifier::~IDocPasswordVerifier()
+{
+}
+
+
+uno::Sequence< beans::PropertyValue > DocPasswordHelper::GenerateNewModifyPasswordInfo( std::u16string_view aPassword )
+{
+ uno::Sequence< beans::PropertyValue > aResult;
+
+ uno::Sequence< sal_Int8 > aSalt = GenerateRandomByteSequence( 16 );
+ sal_Int32 const nPBKDF2IterationCount = 100000;
+
+ uno::Sequence< sal_Int8 > aNewHash = GeneratePBKDF2Hash(aPassword, aSalt, nPBKDF2IterationCount, 16);
+ if ( aNewHash.hasElements() )
+ {
+ aResult = { comphelper::makePropertyValue("algorithm-name", OUString( "PBKDF2" )),
+ comphelper::makePropertyValue("salt", aSalt),
+ comphelper::makePropertyValue("iteration-count", nPBKDF2IterationCount),
+ comphelper::makePropertyValue("hash", aNewHash) };
+ }
+
+ return aResult;
+}
+
+
+uno::Sequence<beans::PropertyValue>
+DocPasswordHelper::GenerateNewModifyPasswordInfoOOXML(std::u16string_view aPassword)
+{
+ uno::Sequence<beans::PropertyValue> aResult;
+
+ if (!aPassword.empty())
+ {
+ uno::Sequence<sal_Int8> aSalt = GenerateRandomByteSequence(16);
+ OUStringBuffer aBuffer(22);
+ comphelper::Base64::encode(aBuffer, aSalt);
+ OUString sSalt = aBuffer.makeStringAndClear();
+
+ sal_Int32 const nIterationCount = 100000;
+ OUString sAlgorithm("SHA-512");
+
+ const OUString sHash(GetOoxHashAsBase64(OUString(aPassword), sSalt, nIterationCount,
+ comphelper::Hash::IterCount::APPEND, sAlgorithm));
+
+ if (!sHash.isEmpty())
+ {
+ aResult = { comphelper::makePropertyValue("algorithm-name", sAlgorithm),
+ comphelper::makePropertyValue("salt", sSalt),
+ comphelper::makePropertyValue("iteration-count", nIterationCount),
+ comphelper::makePropertyValue("hash", sHash) };
+ }
+ }
+
+ return aResult;
+}
+
+
+uno::Sequence< beans::PropertyValue > DocPasswordHelper::ConvertPasswordInfo( const uno::Sequence< beans::PropertyValue >& aInfo )
+{
+ uno::Sequence< beans::PropertyValue > aResult;
+ OUString sAlgorithm, sHash, sSalt, sCount;
+ sal_Int32 nAlgorithm = 0;
+
+ for ( const auto & prop : aInfo )
+ {
+ if ( prop.Name == "cryptAlgorithmSid" )
+ {
+ prop.Value >>= sAlgorithm;
+ nAlgorithm = sAlgorithm.toInt32();
+ }
+ else if ( prop.Name == "salt" )
+ prop.Value >>= sSalt;
+ else if ( prop.Name == "cryptSpinCount" )
+ prop.Value >>= sCount;
+ else if ( prop.Name == "hash" )
+ prop.Value >>= sHash;
+ }
+
+ if (nAlgorithm == 1)
+ sAlgorithm = "MD2";
+ else if (nAlgorithm == 2)
+ sAlgorithm = "MD4";
+ else if (nAlgorithm == 3)
+ sAlgorithm = "MD5";
+ else if (nAlgorithm == 4)
+ sAlgorithm = "SHA-1";
+ else if (nAlgorithm == 5)
+ sAlgorithm = "MAC";
+ else if (nAlgorithm == 6)
+ sAlgorithm = "RIPEMD";
+ else if (nAlgorithm == 7)
+ sAlgorithm = "RIPEMD-160";
+ else if (nAlgorithm == 9)
+ sAlgorithm = "HMAC";
+ else if (nAlgorithm == 12)
+ sAlgorithm = "SHA-256";
+ else if (nAlgorithm == 13)
+ sAlgorithm = "SHA-384";
+ else if (nAlgorithm == 14)
+ sAlgorithm = "SHA-512";
+
+ if ( !sCount.isEmpty() )
+ {
+ sal_Int32 nCount = sCount.toInt32();
+ aResult = { comphelper::makePropertyValue("algorithm-name", sAlgorithm),
+ comphelper::makePropertyValue("salt", sSalt),
+ comphelper::makePropertyValue("iteration-count", nCount),
+ comphelper::makePropertyValue("hash", sHash) };
+ }
+
+ return aResult;
+}
+
+
+bool DocPasswordHelper::IsModifyPasswordCorrect( std::u16string_view aPassword, const uno::Sequence< beans::PropertyValue >& aInfo )
+{
+ bool bResult = false;
+ if ( !aPassword.empty() && aInfo.hasElements() )
+ {
+ OUString sAlgorithm;
+ uno::Any aSalt, aHash;
+ sal_Int32 nCount = 0;
+
+ for ( const auto & prop : aInfo )
+ {
+ if ( prop.Name == "algorithm-name" )
+ prop.Value >>= sAlgorithm;
+ else if ( prop.Name == "salt" )
+ aSalt = prop.Value;
+ else if ( prop.Name == "iteration-count" )
+ prop.Value >>= nCount;
+ else if ( prop.Name == "hash" )
+ aHash = prop.Value;
+ }
+
+ if ( sAlgorithm == "PBKDF2" )
+ {
+ uno::Sequence<sal_Int8> aIntSalt, aIntHash;
+ aSalt >>= aIntSalt;
+ aHash >>= aIntHash;
+ if (aIntSalt.hasElements() && nCount > 0 && aIntHash.hasElements())
+ {
+ uno::Sequence<sal_Int8> aNewHash
+ = GeneratePBKDF2Hash(aPassword, aIntSalt, nCount, aIntHash.getLength());
+ for (sal_Int32 nInd = 0; nInd < aNewHash.getLength() && nInd < aIntHash.getLength()
+ && aNewHash[nInd] == aIntHash[nInd];
+ nInd++)
+ {
+ if (nInd == aNewHash.getLength() - 1 && nInd == aIntHash.getLength() - 1)
+ bResult = true;
+ }
+ }
+ }
+ else if (nCount > 0)
+ {
+ OUString sSalt, sHash;
+ aSalt >>= sSalt;
+ aHash >>= sHash;
+ if (!sSalt.isEmpty() && !sHash.isEmpty())
+ {
+ const OUString aNewHash(GetOoxHashAsBase64(OUString(aPassword), sSalt, nCount,
+ comphelper::Hash::IterCount::APPEND,
+ sAlgorithm));
+ if (!aNewHash.isEmpty())
+ bResult = aNewHash == sHash;
+ }
+ }
+ }
+
+ return bResult;
+}
+
+
+sal_uInt32 DocPasswordHelper::GetWordHashAsUINT32(
+ std::u16string_view aUString )
+{
+ static const sal_uInt16 pInitialCode[] = {
+ 0xE1F0, // 1
+ 0x1D0F, // 2
+ 0xCC9C, // 3
+ 0x84C0, // 4
+ 0x110C, // 5
+ 0x0E10, // 6
+ 0xF1CE, // 7
+ 0x313E, // 8
+ 0x1872, // 9
+ 0xE139, // 10
+ 0xD40F, // 11
+ 0x84F9, // 12
+ 0x280C, // 13
+ 0xA96A, // 14
+ 0x4EC3 // 15
+ };
+
+ static const sal_uInt16 pEncryptionMatrix[15][7] = {
+ { 0xAEFC, 0x4DD9, 0x9BB2, 0x2745, 0x4E8A, 0x9D14, 0x2A09}, // last-14
+ { 0x7B61, 0xF6C2, 0xFDA5, 0xEB6B, 0xC6F7, 0x9DCF, 0x2BBF}, // last-13
+ { 0x4563, 0x8AC6, 0x05AD, 0x0B5A, 0x16B4, 0x2D68, 0x5AD0}, // last-12
+ { 0x0375, 0x06EA, 0x0DD4, 0x1BA8, 0x3750, 0x6EA0, 0xDD40}, // last-11
+ { 0xD849, 0xA0B3, 0x5147, 0xA28E, 0x553D, 0xAA7A, 0x44D5}, // last-10
+ { 0x6F45, 0xDE8A, 0xAD35, 0x4A4B, 0x9496, 0x390D, 0x721A}, // last-9
+ { 0xEB23, 0xC667, 0x9CEF, 0x29FF, 0x53FE, 0xA7FC, 0x5FD9}, // last-8
+ { 0x47D3, 0x8FA6, 0x8FA6, 0x1EDA, 0x3DB4, 0x7B68, 0xF6D0}, // last-7
+ { 0xB861, 0x60E3, 0xC1C6, 0x93AD, 0x377B, 0x6EF6, 0xDDEC}, // last-6
+ { 0x45A0, 0x8B40, 0x06A1, 0x0D42, 0x1A84, 0x3508, 0x6A10}, // last-5
+ { 0xAA51, 0x4483, 0x8906, 0x022D, 0x045A, 0x08B4, 0x1168}, // last-4
+ { 0x76B4, 0xED68, 0xCAF1, 0x85C3, 0x1BA7, 0x374E, 0x6E9C}, // last-3
+ { 0x3730, 0x6E60, 0xDCC0, 0xA9A1, 0x4363, 0x86C6, 0x1DAD}, // last-2
+ { 0x3331, 0x6662, 0xCCC4, 0x89A9, 0x0373, 0x06E6, 0x0DCC}, // last-1
+ { 0x1021, 0x2042, 0x4084, 0x8108, 0x1231, 0x2462, 0x48C4} // last
+ };
+
+ sal_uInt32 nResult = 0;
+ size_t nLen = aUString.size();
+
+ if ( nLen )
+ {
+ if ( nLen > 15 )
+ nLen = 15;
+
+ sal_uInt16 nHighResult = pInitialCode[nLen - 1];
+ sal_uInt16 nLowResult = 0;
+
+ for ( size_t nInd = 0; nInd < nLen; nInd++ )
+ {
+ // NO Encoding during conversion!
+ // The specification says that the low byte should be used in case it is not NULL
+ char nHighChar = static_cast<char>( aUString[nInd] >> 8 );
+ char nLowChar = static_cast<char>( aUString[nInd] & 0xFF );
+ char nChar = nLowChar ? nLowChar : nHighChar;
+
+ for ( int nMatrixInd = 0; nMatrixInd < 7; ++nMatrixInd )
+ {
+ if ( ( nChar & ( 1 << nMatrixInd ) ) != 0 )
+ nHighResult = nHighResult ^ pEncryptionMatrix[15 - nLen + nInd][nMatrixInd];
+ }
+
+ nLowResult = ( ( ( nLowResult >> 14 ) & 0x0001 ) | ( ( nLowResult << 1 ) & 0x7FFF ) ) ^ nChar;
+ }
+
+ nLowResult = static_cast<sal_uInt16>( ( ( ( nLowResult >> 14 ) & 0x001 ) | ( ( nLowResult << 1 ) & 0x7FF ) ) ^ nLen ^ 0xCE4B );
+
+ nResult = ( nHighResult << 16 ) | nLowResult;
+ }
+
+ return nResult;
+}
+
+
+sal_uInt16 DocPasswordHelper::GetXLHashAsUINT16(
+ std::u16string_view aUString,
+ rtl_TextEncoding nEnc )
+{
+ sal_uInt16 nResult = 0;
+
+ OString aString = OUStringToOString( aUString, nEnc );
+
+ if ( !aString.isEmpty() && aString.getLength() <= SAL_MAX_UINT16 )
+ {
+ for ( sal_Int32 nInd = aString.getLength() - 1; nInd >= 0; nInd-- )
+ {
+ nResult = ( ( nResult >> 14 ) & 0x01 ) | ( ( nResult << 1 ) & 0x7FFF );
+ nResult ^= aString[nInd];
+ }
+
+ nResult = ( ( nResult >> 14 ) & 0x01 ) | ( ( nResult << 1 ) & 0x7FFF );
+ nResult ^= ( 0x8000 | ( 'N' << 8 ) | 'K' );
+ nResult ^= aString.getLength();
+ }
+
+ return nResult;
+}
+
+
+Sequence< sal_Int8 > DocPasswordHelper::GetXLHashAsSequence(
+ std::u16string_view aUString )
+{
+ sal_uInt16 nHash = GetXLHashAsUINT16( aUString );
+ return {sal_Int8(nHash >> 8), sal_Int8(nHash & 0xFF)};
+}
+
+
+std::vector<unsigned char> DocPasswordHelper::GetOoxHashAsVector(
+ const OUString& rPassword,
+ const std::vector<unsigned char>& rSaltValue,
+ sal_uInt32 nSpinCount,
+ comphelper::Hash::IterCount eIterCount,
+ std::u16string_view rAlgorithmName)
+{
+ comphelper::HashType eType;
+ if (rAlgorithmName == u"SHA-512" || rAlgorithmName == u"SHA512")
+ eType = comphelper::HashType::SHA512;
+ else if (rAlgorithmName == u"SHA-256" || rAlgorithmName == u"SHA256")
+ eType = comphelper::HashType::SHA256;
+ else if (rAlgorithmName == u"SHA-384" || rAlgorithmName == u"SHA384")
+ eType = comphelper::HashType::SHA384;
+ else if (rAlgorithmName == u"SHA-1" || rAlgorithmName == u"SHA1") // "SHA1" might be in the wild
+ eType = comphelper::HashType::SHA1;
+ else if (rAlgorithmName == u"MD5")
+ eType = comphelper::HashType::MD5;
+ else
+ return std::vector<unsigned char>();
+
+ return comphelper::Hash::calculateHash( rPassword, rSaltValue, nSpinCount, eIterCount, eType);
+}
+
+
+css::uno::Sequence<sal_Int8> DocPasswordHelper::GetOoxHashAsSequence(
+ const OUString& rPassword,
+ std::u16string_view rSaltValue,
+ sal_uInt32 nSpinCount,
+ comphelper::Hash::IterCount eIterCount,
+ std::u16string_view rAlgorithmName)
+{
+ std::vector<unsigned char> aSaltVec;
+ if (!rSaltValue.empty())
+ {
+ css::uno::Sequence<sal_Int8> aSaltSeq;
+ comphelper::Base64::decode( aSaltSeq, rSaltValue);
+ aSaltVec = comphelper::sequenceToContainer<std::vector<unsigned char>>( aSaltSeq);
+ }
+
+ std::vector<unsigned char> hash( GetOoxHashAsVector( rPassword, aSaltVec, nSpinCount, eIterCount, rAlgorithmName));
+
+ return comphelper::containerToSequence<sal_Int8>( hash);
+}
+
+OUString DocPasswordHelper::GetOoxHashAsBase64(
+ const OUString& rPassword,
+ std::u16string_view rSaltValue,
+ sal_uInt32 nSpinCount,
+ comphelper::Hash::IterCount eIterCount,
+ std::u16string_view rAlgorithmName)
+{
+ css::uno::Sequence<sal_Int8> aSeq( GetOoxHashAsSequence( rPassword, rSaltValue, nSpinCount,
+ eIterCount, rAlgorithmName));
+
+ OUStringBuffer aBuf((aSeq.getLength()+2)/3*4);
+ comphelper::Base64::encode( aBuf, aSeq);
+ return aBuf.makeStringAndClear();
+}
+
+
+/*static*/ uno::Sequence< sal_Int8 > DocPasswordHelper::GenerateRandomByteSequence( sal_Int32 nLength )
+{
+ uno::Sequence< sal_Int8 > aResult( nLength );
+
+ rtlRandomPool aRandomPool = rtl_random_createPool ();
+ rtl_random_getBytes ( aRandomPool, aResult.getArray(), nLength );
+ rtl_random_destroyPool ( aRandomPool );
+
+ return aResult;
+}
+
+
+/*static*/ uno::Sequence< sal_Int8 > DocPasswordHelper::GenerateStd97Key( std::u16string_view aPassword, const uno::Sequence< sal_Int8 >& aDocId )
+{
+ uno::Sequence< sal_Int8 > aResultKey;
+ if ( !aPassword.empty() && aDocId.getLength() == 16 )
+ {
+ sal_uInt16 pPassData[16] = {};
+
+ sal_Int32 nPassLen = std::min< sal_Int32 >( aPassword.size(), 15 );
+ memcpy( pPassData, aPassword.data(), nPassLen * sizeof(pPassData[0]) );
+
+ aResultKey = GenerateStd97Key( pPassData, aDocId );
+ }
+
+ return aResultKey;
+}
+
+
+/*static*/ uno::Sequence< sal_Int8 > DocPasswordHelper::GenerateStd97Key( const sal_uInt16 pPassData[16], const uno::Sequence< sal_Int8 >& aDocId )
+{
+ uno::Sequence< sal_Int8 > aResultKey;
+
+ if ( aDocId.getLength() == 16 )
+ aResultKey = GenerateStd97Key(pPassData, reinterpret_cast<const sal_uInt8*>(aDocId.getConstArray()));
+
+ return aResultKey;
+}
+
+
+/*static*/ uno::Sequence< sal_Int8 > DocPasswordHelper::GenerateStd97Key( const sal_uInt16 pPassData[16], const sal_uInt8 pDocId[16] )
+{
+ uno::Sequence< sal_Int8 > aResultKey;
+ if ( pPassData[0] )
+ {
+ sal_uInt8 pKeyData[64] = {};
+
+ sal_Int32 nInd = 0;
+
+ // Fill PassData into KeyData.
+ for ( nInd = 0; nInd < 16 && pPassData[nInd]; nInd++)
+ {
+ pKeyData[2*nInd] = sal::static_int_cast< sal_uInt8 >( (pPassData[nInd] >> 0) & 0xff );
+ pKeyData[2*nInd + 1] = sal::static_int_cast< sal_uInt8 >( (pPassData[nInd] >> 8) & 0xff );
+ }
+
+ pKeyData[2*nInd] = 0x80;
+ pKeyData[56] = sal::static_int_cast< sal_uInt8 >( nInd << 4 );
+
+ // Fill raw digest of KeyData into KeyData.
+ rtlDigest hDigest = rtl_digest_create ( rtl_Digest_AlgorithmMD5 );
+ (void)rtl_digest_updateMD5 (
+ hDigest, pKeyData, sizeof(pKeyData));
+ (void)rtl_digest_rawMD5 (
+ hDigest, pKeyData, RTL_DIGEST_LENGTH_MD5);
+
+ // Update digest with KeyData and Unique.
+ for ( nInd = 0; nInd < 16; nInd++ )
+ {
+ rtl_digest_updateMD5( hDigest, pKeyData, 5 );
+ rtl_digest_updateMD5( hDigest, pDocId, 16 );
+ }
+
+ // Update digest with padding.
+ pKeyData[16] = 0x80;
+ memset( pKeyData + 17, 0, sizeof(pKeyData) - 17 );
+ pKeyData[56] = 0x80;
+ pKeyData[57] = 0x0a;
+
+ rtl_digest_updateMD5( hDigest, &(pKeyData[16]), sizeof(pKeyData) - 16 );
+
+ // Fill raw digest of above updates
+ aResultKey.realloc( RTL_DIGEST_LENGTH_MD5 );
+ rtl_digest_rawMD5 ( hDigest, reinterpret_cast<sal_uInt8*>(aResultKey.getArray()), aResultKey.getLength() );
+
+ // Erase KeyData array and leave.
+ rtl_secureZeroMemory (pKeyData, sizeof(pKeyData));
+
+ rtl_digest_destroy(hDigest);
+ }
+
+ return aResultKey;
+}
+
+
+/*static*/ css::uno::Sequence< css::beans::NamedValue > DocPasswordHelper::requestAndVerifyDocPassword(
+ IDocPasswordVerifier& rVerifier,
+ const css::uno::Sequence< css::beans::NamedValue >& rMediaEncData,
+ const OUString& rMediaPassword,
+ const Reference< XInteractionHandler >& rxInteractHandler,
+ const OUString& rDocumentUrl,
+ DocPasswordRequestType eRequestType,
+ const std::vector< OUString >* pDefaultPasswords,
+ bool* pbIsDefaultPassword )
+{
+ css::uno::Sequence< css::beans::NamedValue > aEncData;
+ OUString aPassword;
+ DocPasswordVerifierResult eResult = DocPasswordVerifierResult::WrongPassword;
+
+ sal_Int32 nMediaEncDataCount = rMediaEncData.getLength();
+
+ // tdf#93389: if the document is being restored from autorecovery, we need to add encryption
+ // data also for real document type.
+ // TODO: get real filter name here (from CheckPasswd_Impl), to only add necessary data
+ bool bForSalvage = false;
+ if (nMediaEncDataCount)
+ {
+ for (auto& val : rMediaEncData)
+ {
+ if (val.Name == "ForSalvage")
+ {
+ --nMediaEncDataCount; // don't consider this element below
+ val.Value >>= bForSalvage;
+ break;
+ }
+ }
+ }
+
+ // first, try provided default passwords
+ if( pbIsDefaultPassword )
+ *pbIsDefaultPassword = false;
+ if( pDefaultPasswords )
+ {
+ for( const auto& rPassword : *pDefaultPasswords )
+ {
+ OSL_ENSURE( !rPassword.isEmpty(), "DocPasswordHelper::requestAndVerifyDocPassword - unexpected empty default password" );
+ if( !rPassword.isEmpty() )
+ {
+ eResult = rVerifier.verifyPassword( rPassword, aEncData );
+ if (eResult == DocPasswordVerifierResult::OK)
+ {
+ aPassword = rPassword;
+ if (pbIsDefaultPassword)
+ *pbIsDefaultPassword = true;
+ }
+ if( eResult != DocPasswordVerifierResult::WrongPassword )
+ break;
+ }
+ }
+ }
+
+ // try media encryption data (skip, if result is OK or ABORT)
+ if( eResult == DocPasswordVerifierResult::WrongPassword )
+ {
+ if (nMediaEncDataCount)
+ {
+ eResult = rVerifier.verifyEncryptionData( rMediaEncData );
+ if( eResult == DocPasswordVerifierResult::OK )
+ aEncData = rMediaEncData;
+ }
+ }
+
+ // try media password (skip, if result is OK or ABORT)
+ if( eResult == DocPasswordVerifierResult::WrongPassword )
+ {
+ if( !rMediaPassword.isEmpty() )
+ {
+ eResult = rVerifier.verifyPassword( rMediaPassword, aEncData );
+ if (eResult == DocPasswordVerifierResult::OK)
+ aPassword = rMediaPassword;
+ }
+ }
+
+ // request a password (skip, if result is OK or ABORT)
+ if( (eResult == DocPasswordVerifierResult::WrongPassword) && rxInteractHandler.is() ) try
+ {
+ PasswordRequestMode eRequestMode = PasswordRequestMode_PASSWORD_ENTER;
+ while( eResult == DocPasswordVerifierResult::WrongPassword )
+ {
+ rtl::Reference<DocPasswordRequest> pRequest = new DocPasswordRequest( eRequestType, eRequestMode, rDocumentUrl );
+ rxInteractHandler->handle( pRequest );
+ if( pRequest->isPassword() )
+ {
+ if( !pRequest->getPassword().isEmpty() )
+ eResult = rVerifier.verifyPassword( pRequest->getPassword(), aEncData );
+ if (eResult == DocPasswordVerifierResult::OK)
+ aPassword = pRequest->getPassword();
+ }
+ else
+ {
+ eResult = DocPasswordVerifierResult::Abort;
+ }
+ eRequestMode = PasswordRequestMode_PASSWORD_REENTER;
+ }
+ }
+ catch( Exception& )
+ {
+ }
+
+ if (eResult == DocPasswordVerifierResult::OK && !aPassword.isEmpty())
+ {
+ if (std::none_of(std::cbegin(aEncData), std::cend(aEncData),
+ [](const css::beans::NamedValue& val) {
+ return val.Name == PACKAGE_ENCRYPTIONDATA_SHA256UTF8;
+ }))
+ {
+ // tdf#118639: We need ODF encryption data for autorecovery, where password
+ // will already be unavailable, so generate and append it here
+ aEncData = comphelper::concatSequences(
+ aEncData, OStorageHelper::CreatePackageEncryptionData(aPassword));
+ }
+
+ if (bForSalvage)
+ {
+ // TODO: add individual methods for different target filter, and only call what's needed
+
+ // 1. Prepare binary MS formats encryption data
+ auto aUniqueID = GenerateRandomByteSequence(16);
+ auto aEnc97Key = GenerateStd97Key(aPassword, aUniqueID);
+ // 2. Add MS binary and OOXML encryption data to result
+ aEncData = comphelper::concatSequences(
+ aEncData, std::initializer_list<beans::NamedValue>{
+ { "STD97EncryptionKey", css::uno::Any(aEnc97Key) },
+ { "STD97UniqueID", css::uno::Any(aUniqueID) },
+ { "OOXPassword", css::uno::Any(aPassword) },
+ });
+ }
+ }
+
+ return (eResult == DocPasswordVerifierResult::OK) ? aEncData : uno::Sequence< beans::NamedValue >();
+}
+
+/*static*/ uno::Sequence< css::beans::NamedValue >
+ DocPasswordHelper::decryptGpgSession(
+ const uno::Sequence< uno::Sequence< beans::NamedValue > >& rGpgProperties )
+{
+#if HAVE_FEATURE_GPGME
+ if ( !rGpgProperties.hasElements() )
+ return uno::Sequence< beans::NamedValue >();
+
+ uno::Sequence< beans::NamedValue > aEncryptionData;
+ std::unique_ptr<GpgME::Context> ctx;
+ GpgME::initializeLibrary();
+ GpgME::Error err = GpgME::checkEngine(GpgME::OpenPGP);
+ if (err)
+ throw uno::RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
+
+ ctx.reset( GpgME::Context::createForProtocol(GpgME::OpenPGP) );
+ if (ctx == nullptr)
+ throw uno::RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
+ ctx->setArmor(false);
+
+ const uno::Sequence < beans::NamedValue > *pSequence = rGpgProperties.getConstArray();
+ const sal_Int32 nLength = rGpgProperties.getLength();
+ for ( sal_Int32 i = 0; i < nLength ; i++, pSequence++ )
+ {
+ const beans::NamedValue *pValues = pSequence->getConstArray();
+ if ( pSequence->getLength() == 3 )
+ {
+ // take CipherValue and try to decrypt that - stop after
+ // the first successful decryption
+
+ // ctx is setup now, let's decrypt the lot!
+ uno::Sequence < sal_Int8 > aVector;
+ pValues[2].Value >>= aVector;
+
+ GpgME::Data cipher(
+ reinterpret_cast<const char*>(aVector.getConstArray()),
+ size_t(aVector.getLength()), false);
+ GpgME::Data plain;
+
+ GpgME::DecryptionResult crypt_res = ctx->decrypt(
+ cipher, plain);
+
+ // NO_SECKEY -> skip
+ // BAD_PASSPHRASE -> retry?
+
+ off_t result = plain.seek(0,SEEK_SET);
+ (void) result;
+ assert(result == 0);
+ int len=0, curr=0; char buf;
+ while( (curr=plain.read(&buf, 1)) )
+ len += curr;
+
+ if(crypt_res.error() || !len)
+ continue; // can't use this key, take next one
+
+ uno::Sequence < sal_Int8 > aKeyValue(len);
+ result = plain.seek(0,SEEK_SET);
+ assert(result == 0);
+ if( plain.read(aKeyValue.getArray(), len) != len )
+ throw uno::RuntimeException("The GpgME library failed to read the encrypted value.");
+
+ SAL_INFO("comphelper.crypto", "Extracted gpg session key of length: " << len);
+
+ aEncryptionData = { { PACKAGE_ENCRYPTIONDATA_SHA256UTF8, uno::Any(aKeyValue) } };
+ break;
+ }
+ }
+
+ if ( aEncryptionData.hasElements() )
+ {
+ uno::Sequence< beans::NamedValue > aContainer{
+ { "GpgInfos", uno::Any(rGpgProperties) }, { "EncryptionKey", uno::Any(aEncryptionData) }
+ };
+
+ return aContainer;
+ }
+#else
+ (void)rGpgProperties;
+#endif
+ return uno::Sequence< beans::NamedValue >();
+}
+
+} // namespace comphelper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/docpasswordrequest.cxx b/comphelper/source/misc/docpasswordrequest.cxx
new file mode 100644
index 0000000000..6f644336e1
--- /dev/null
+++ b/comphelper/source/misc/docpasswordrequest.cxx
@@ -0,0 +1,179 @@
+/* -*- 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 <comphelper/docpasswordrequest.hxx>
+#include <com/sun/star/task/DocumentMSPasswordRequest2.hpp>
+#include <com/sun/star/task/DocumentPasswordRequest2.hpp>
+#include <com/sun/star/task/PasswordRequest.hpp>
+#include <com/sun/star/task/XInteractionAbort.hpp>
+#include <com/sun/star/task/XInteractionPassword2.hpp>
+#include <cppuhelper/implbase.hxx>
+
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::XInterface;
+using ::com::sun::star::task::InteractionClassification_QUERY;
+using ::com::sun::star::task::DocumentMSPasswordRequest2;
+using ::com::sun::star::task::DocumentPasswordRequest2;
+using ::com::sun::star::task::PasswordRequest;
+using ::com::sun::star::task::PasswordRequestMode;
+using ::com::sun::star::task::XInteractionAbort;
+using ::com::sun::star::task::XInteractionContinuation;
+using ::com::sun::star::task::XInteractionPassword2;
+
+namespace comphelper {
+
+namespace {
+
+class AbortContinuation : public ::cppu::WeakImplHelper< XInteractionAbort >
+{
+public:
+ virtual void SAL_CALL select() override {}
+};
+
+}
+
+class PasswordContinuation : public ::cppu::WeakImplHelper< XInteractionPassword2 >
+{
+public:
+ explicit PasswordContinuation() : mbReadOnly( false ), mbSelected( false ) {}
+
+ bool isSelected() const { return mbSelected; }
+
+ virtual void SAL_CALL select() override { mbSelected = true; }
+
+ virtual void SAL_CALL setPassword( const OUString& rPass ) override { maPassword = rPass; }
+ virtual OUString SAL_CALL getPassword() override { return maPassword; }
+
+ virtual void SAL_CALL setPasswordToModify( const OUString& rPass ) override { maModifyPassword = rPass; }
+ virtual OUString SAL_CALL getPasswordToModify() override { return maModifyPassword; }
+
+ virtual void SAL_CALL setRecommendReadOnly( sal_Bool bReadOnly ) override { mbReadOnly = bReadOnly; }
+ virtual sal_Bool SAL_CALL getRecommendReadOnly() override { return mbReadOnly; }
+
+private:
+ OUString maPassword;
+ OUString maModifyPassword;
+ bool mbReadOnly;
+ bool mbSelected;
+};
+
+
+SimplePasswordRequest::SimplePasswordRequest()
+{
+ PasswordRequest aRequest( OUString(), Reference< XInterface >(),
+ InteractionClassification_QUERY, css::task::PasswordRequestMode_PASSWORD_CREATE );
+ maRequest <<= aRequest;
+
+ mxAbort = new AbortContinuation;
+ mxPassword = new PasswordContinuation;
+}
+
+SimplePasswordRequest::~SimplePasswordRequest()
+{
+}
+
+bool SimplePasswordRequest::isPassword() const
+{
+ return mxPassword->isSelected();
+}
+
+OUString SimplePasswordRequest::getPassword() const
+{
+ return mxPassword->getPassword();
+}
+
+Any SAL_CALL SimplePasswordRequest::getRequest()
+{
+ return maRequest;
+}
+
+Sequence< Reference< XInteractionContinuation > > SAL_CALL SimplePasswordRequest::getContinuations()
+{
+ return { mxAbort, mxPassword };
+}
+
+
+DocPasswordRequest::DocPasswordRequest( DocPasswordRequestType eType,
+ PasswordRequestMode eMode, const OUString& rDocumentUrl, bool bPasswordToModify )
+{
+ switch( eType )
+ {
+ case DocPasswordRequestType::Standard:
+ {
+ DocumentPasswordRequest2 aRequest( OUString(), Reference< XInterface >(),
+ InteractionClassification_QUERY, eMode, rDocumentUrl, bPasswordToModify );
+ maRequest <<= aRequest;
+ }
+ break;
+ case DocPasswordRequestType::MS:
+ {
+ DocumentMSPasswordRequest2 aRequest( OUString(), Reference< XInterface >(),
+ InteractionClassification_QUERY, eMode, rDocumentUrl, bPasswordToModify );
+ maRequest <<= aRequest;
+ }
+ break;
+ /* no 'default', so compilers will complain about missing
+ implementation of a new enum value. */
+ }
+
+ mxAbort = new AbortContinuation;
+ mxPassword = new PasswordContinuation;
+}
+
+DocPasswordRequest::~DocPasswordRequest()
+{
+}
+
+bool DocPasswordRequest::isPassword() const
+{
+ return mxPassword->isSelected();
+}
+
+OUString DocPasswordRequest::getPassword() const
+{
+ return mxPassword->getPassword();
+}
+
+OUString DocPasswordRequest::getPasswordToModify() const
+{
+ return mxPassword->getPasswordToModify();
+}
+
+bool DocPasswordRequest::getRecommendReadOnly() const
+{
+ return mxPassword->getRecommendReadOnly();
+}
+
+Any SAL_CALL DocPasswordRequest::getRequest()
+{
+ return maRequest;
+}
+
+Sequence< Reference< XInteractionContinuation > > SAL_CALL DocPasswordRequest::getContinuations()
+{
+ return { mxAbort, mxPassword };
+}
+
+
+} // namespace comphelper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/documentinfo.cxx b/comphelper/source/misc/documentinfo.cxx
new file mode 100644
index 0000000000..21425524e6
--- /dev/null
+++ b/comphelper/source/misc/documentinfo.cxx
@@ -0,0 +1,177 @@
+/* -*- 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 <comphelper/documentinfo.hxx>
+#include <comphelper/namedvaluecollection.hxx>
+
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/frame/XStorable.hpp>
+#include <com/sun/star/frame/XTitle.hpp>
+
+#include <cppuhelper/exc_hlp.hxx>
+
+#include <sal/log.hxx>
+
+namespace comphelper {
+
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::UNO_QUERY;
+ using ::com::sun::star::uno::UNO_QUERY_THROW;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::frame::XModel;
+ using ::com::sun::star::frame::XTitle;
+ using ::com::sun::star::frame::XController;
+ using ::com::sun::star::document::XDocumentPropertiesSupplier;
+ using ::com::sun::star::document::XDocumentProperties;
+ using ::com::sun::star::frame::XStorable;
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::frame::XFrame;
+
+ namespace
+ {
+ OUString lcl_getTitle( const Reference< XInterface >& _rxComponent )
+ {
+ Reference< XTitle > xTitle( _rxComponent, UNO_QUERY );
+ if ( xTitle.is() )
+ return xTitle->getTitle();
+ return OUString();
+ }
+ }
+
+ OUString DocumentInfo::getDocumentTitle( const Reference< XModel >& _rxDocument )
+ {
+ OUString sTitle;
+
+ if ( !_rxDocument.is() )
+ return sTitle;
+
+ try
+ {
+ // 1. ask the model and the controller for their XTitle::getTitle
+ sTitle = lcl_getTitle( _rxDocument );
+ if ( !sTitle.isEmpty() )
+ return sTitle;
+
+ Reference< XController > xController( _rxDocument->getCurrentController() );
+ sTitle = lcl_getTitle( xController );
+ if ( !sTitle.isEmpty() )
+ return sTitle;
+
+ // work around a problem with embedded objects, which sometimes return
+ // private:object as URL
+ OUString sDocURL = _rxDocument->getURL();
+ if ( sDocURL.startsWithIgnoreAsciiCase( "private:" ) )
+ sDocURL.clear();
+
+ // 2. if the document is not saved, yet, check the frame title
+ if ( sDocURL.isEmpty() )
+ {
+ Reference< XFrame > xFrame;
+ if ( xController.is() )
+ xFrame.set( xController->getFrame() );
+ sTitle = lcl_getTitle( xFrame );
+ if ( !sTitle.isEmpty() )
+ return sTitle;
+ }
+
+ // 3. try the UNO XDocumentProperties
+ Reference< XDocumentPropertiesSupplier > xDPS( _rxDocument, UNO_QUERY );
+ if ( xDPS.is() )
+ {
+ Reference< XDocumentProperties > xDocProps (
+ xDPS->getDocumentProperties(), css::uno::UNO_SET_THROW );
+ sTitle = xDocProps->getTitle();
+ if ( !sTitle.isEmpty() )
+ return sTitle;
+ }
+
+ // 4. try model arguments
+ sTitle = NamedValueCollection::getOrDefault( _rxDocument->getArgs(), u"Title", sTitle );
+ if ( !sTitle.isEmpty() )
+ return sTitle;
+
+ // 5. try the last segment of the document URL
+ // this formerly was an INetURLObject::getName( LAST_SEGMENT, true, DecodeMechanism::WithCharset ),
+ // but since we moved this code to comphelper, we do not have access to an INetURLObject anymore
+ // This heuristics here should be sufficient - finally, we will get a UNO title API in a not
+ // too distant future (hopefully), then this complete class is superfluous)
+ if ( sDocURL.isEmpty() )
+ {
+ Reference< XStorable > xDocStorable( _rxDocument, UNO_QUERY_THROW );
+ sDocURL = xDocStorable->getLocation();
+ }
+ sal_Int32 nLastSepPos = sDocURL.lastIndexOf( '/' );
+ if ( ( nLastSepPos != -1 ) && ( nLastSepPos == sDocURL.getLength() - 1 ) )
+ {
+ sDocURL = sDocURL.copy( 0, nLastSepPos );
+ nLastSepPos = sDocURL.lastIndexOf( '/' );
+ }
+ sTitle = sDocURL.copy( nLastSepPos + 1 );
+
+ if ( !sTitle.isEmpty() )
+ return sTitle;
+
+ // 5.
+ // <-- #i88104# (05-16-08) TKR: use the new XTitle Interface to get the Title -->
+
+ Reference< XTitle > xTitle( _rxDocument, UNO_QUERY );
+ if ( xTitle.is() )
+ {
+ if ( !xTitle->getTitle().isEmpty() )
+ return xTitle->getTitle();
+ }
+ }
+ catch ( const Exception& )
+ {
+ // Cannot use tools::exceptionToString here, because the tools module depends on the comphelper module
+ css::uno::Any caught( ::cppu::getCaughtException() );
+ css::uno::Exception exception;
+ caught >>= exception;
+ SAL_WARN( "comphelper", "caught an exception!\ntype : " << caught.getValueTypeName()
+ << "\nmessage: " << exception
+ << "\nin function:\n" << __func__);
+ }
+
+ return sTitle;
+ }
+
+ void DocumentInfo::notifyMacroEventRead(const css::uno::Reference<css::frame::XModel>& rModel)
+ {
+ if (!rModel.is())
+ return;
+
+ // like BreakMacroSignature of XMLScriptContext use XModel::attachResource
+ // to propagate this notification
+ css::uno::Sequence<css::beans::PropertyValue> aMedDescr = rModel->getArgs();
+ sal_Int32 nNewLen = aMedDescr.getLength() + 1;
+ aMedDescr.realloc(nNewLen);
+ auto pMedDescr = aMedDescr.getArray();
+ pMedDescr[nNewLen-1].Name = "MacroEventRead";
+ pMedDescr[nNewLen-1].Value <<= true;
+ rModel->attachResource(rModel->getURL(), aMedDescr);
+ }
+
+} // namespace comphelper
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/errcode.cxx b/comphelper/source/misc/errcode.cxx
new file mode 100644
index 0000000000..e7b6677458
--- /dev/null
+++ b/comphelper/source/misc/errcode.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 <comphelper/errcode.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <o3tl/runtimetooustring.hxx>
+
+COMPHELPER_DLLPUBLIC OUString ErrCode::toString() const
+{
+ std::u16string_view pWarningError;
+ if (IsWarning())
+ pWarningError = u"Warning";
+ else
+ pWarningError = u"Error";
+
+ std::u16string_view pArea;
+ switch (GetArea())
+ {
+ case ErrCodeArea::Io:
+ pArea = u"Io";
+ break;
+ case ErrCodeArea::Sfx:
+ pArea = u"Sfx";
+ break;
+ case ErrCodeArea::Inet:
+ pArea = u"Inet";
+ break;
+ case ErrCodeArea::Vcl:
+ pArea = u"Vcl";
+ break;
+ case ErrCodeArea::Svx:
+ pArea = u"Svx";
+ break;
+ case ErrCodeArea::So:
+ pArea = u"So";
+ break;
+ case ErrCodeArea::Sbx:
+ pArea = u"Sbx";
+ break;
+ case ErrCodeArea::Uui:
+ pArea = u"Uui";
+ break;
+ case ErrCodeArea::Sc:
+ pArea = u"Sc";
+ break;
+ case ErrCodeArea::Sd:
+ pArea = u"Sd";
+ break;
+ case ErrCodeArea::Sw:
+ pArea = u"Sw";
+ break;
+ }
+
+ std::u16string_view pClass;
+ switch (GetClass())
+ {
+ case ErrCodeClass::NONE:
+ pClass = u"NONE";
+ break;
+ case ErrCodeClass::Abort:
+ pClass = u"Abort";
+ break;
+ case ErrCodeClass::General:
+ pClass = u"General";
+ break;
+ case ErrCodeClass::NotExists:
+ pClass = u"NotExists";
+ break;
+ case ErrCodeClass::AlreadyExists:
+ pClass = u"AlreadyExists";
+ break;
+ case ErrCodeClass::Access:
+ pClass = u"Access";
+ break;
+ case ErrCodeClass::Path:
+ pClass = u"Path";
+ break;
+ case ErrCodeClass::Locking:
+ pClass = u"Locking";
+ break;
+ case ErrCodeClass::Parameter:
+ pClass = u"Parameter";
+ break;
+ case ErrCodeClass::Space:
+ pClass = u"Space";
+ break;
+ case ErrCodeClass::NotSupported:
+ pClass = u"NotSupported";
+ break;
+ case ErrCodeClass::Read:
+ pClass = u"Read";
+ break;
+ case ErrCodeClass::Write:
+ pClass = u"Write";
+ break;
+ case ErrCodeClass::Unknown:
+ pClass = u"Unknown";
+ break;
+ case ErrCodeClass::Version:
+ pClass = u"Version";
+ break;
+ case ErrCodeClass::Format:
+ pClass = u"Format";
+ break;
+ case ErrCodeClass::Create:
+ pClass = u"Create";
+ break;
+ case ErrCodeClass::Import:
+ pClass = u"Import";
+ break;
+ case ErrCodeClass::Export:
+ pClass = u"Export";
+ break;
+ case ErrCodeClass::So:
+ pClass = u"So";
+ break;
+ case ErrCodeClass::Sbx:
+ pClass = u"Sbx";
+ break;
+ case ErrCodeClass::Runtime:
+ pClass = u"Runtime";
+ break;
+ case ErrCodeClass::Compiler:
+ pClass = u"Compiler";
+ break;
+ }
+ return toHexString() + "(" + pWarningError + " Area:" + pArea + " Class:" + pClass
+ + " Code:" + OUString::number(GetCode()) + ")";
+}
+
+COMPHELPER_DLLPUBLIC std::ostream& operator<<(std::ostream& os, const ErrCode& err)
+{
+ os << err.toString();
+ return os;
+}
+
+COMPHELPER_DLLPUBLIC OUString ErrCodeMsg::toString() const
+{
+ OUString s = mnCode.toString();
+ if (!maArg1.isEmpty())
+ s += " arg1=" + maArg1;
+ if (!maArg2.isEmpty())
+ s += " arg2=" + maArg2;
+#ifdef LIBO_ERRMSG_USE_SOURCE_LOCATION
+ if (!moLoc)
+ s += OUString::Concat(" func=") + o3tl::runtimeToOUString(moLoc->function_name()) + " src="
+ + o3tl::runtimeToOUString(moLoc->file_name()) + ":" + OUString::number(moLoc->line());
+#endif
+ return s;
+}
+
+COMPHELPER_DLLPUBLIC std::ostream& operator<<(std::ostream& os, const ErrCodeMsg& err)
+{
+ os << err.toString();
+ return os;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/evtlistenerhlp.cxx b/comphelper/source/misc/evtlistenerhlp.cxx
new file mode 100644
index 0000000000..2eac315808
--- /dev/null
+++ b/comphelper/source/misc/evtlistenerhlp.cxx
@@ -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/.
+ *
+ * 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 <comphelper/evtlistenerhlp.hxx>
+
+namespace comphelper
+{
+ OEventListenerHelper::OEventListenerHelper(const css::uno::Reference< css::lang::XEventListener>&
+ _rxListener) : m_xListener(_rxListener)
+ {
+ }
+ void SAL_CALL OEventListenerHelper::disposing( const css::lang::EventObject& Source )
+ {
+ css::uno::Reference< css::lang::XEventListener> xRef = m_xListener;
+ if(xRef.is())
+ xRef->disposing(Source);
+ }
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/evtmethodhelper.cxx b/comphelper/source/misc/evtmethodhelper.cxx
new file mode 100644
index 0000000000..5f60d92d64
--- /dev/null
+++ b/comphelper/source/misc/evtmethodhelper.cxx
@@ -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 .
+ */
+
+#include <comphelper/evtmethodhelper.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::Type;
+
+namespace comphelper
+{
+
+ Sequence< OUString> getEventMethodsForType(const Type& type)
+ {
+ typelib_InterfaceTypeDescription *pType=nullptr;
+ type.getDescription(reinterpret_cast<typelib_TypeDescription**>(&pType));
+
+ if(!pType)
+ return Sequence< OUString>();
+
+ Sequence< OUString> aNames(pType->nMembers);
+ OUString* pNames = aNames.getArray();
+ for(sal_Int32 i=0;i<pType->nMembers;i++,++pNames)
+ {
+ // the description reference
+ typelib_TypeDescriptionReference* pMemberDescriptionReference = pType->ppMembers[i];
+ // the description for the reference
+ typelib_TypeDescription* pMemberDescription = nullptr;
+ typelib_typedescriptionreference_getDescription(&pMemberDescription, pMemberDescriptionReference);
+ if (pMemberDescription)
+ {
+ typelib_InterfaceMemberTypeDescription* pRealMemberDescription =
+ reinterpret_cast<typelib_InterfaceMemberTypeDescription*>(pMemberDescription);
+ *pNames = pRealMemberDescription->pMemberName;
+ }
+ }
+ typelib_typedescription_release( &pType->aBase );
+ return aNames;
+ }
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/fileurl.cxx b/comphelper/source/misc/fileurl.cxx
new file mode 100644
index 0000000000..2515b28c5b
--- /dev/null
+++ b/comphelper/source/misc/fileurl.cxx
@@ -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 .
+ */
+
+#include <sal/config.h>
+
+#include <comphelper/fileurl.hxx>
+#include <rtl/ustring.hxx>
+#include <o3tl/string_view.hxx>
+
+bool comphelper::isFileUrl(std::u16string_view url)
+{
+ return o3tl::matchIgnoreAsciiCase(url, "file:");
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/getexpandeduri.cxx b/comphelper/source/misc/getexpandeduri.cxx
new file mode 100644
index 0000000000..853e5dbd3c
--- /dev/null
+++ b/comphelper/source/misc/getexpandeduri.cxx
@@ -0,0 +1,32 @@
+/* -*- 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/uno/Reference.hxx>
+#include <com/sun/star/uri/UriReferenceFactory.hpp>
+#include <com/sun/star/uri/XVndSunStarExpandUrlReference.hpp>
+#include <com/sun/star/util/theMacroExpander.hpp>
+#include <comphelper/getexpandeduri.hxx>
+#include <rtl/ustring.hxx>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+OUString comphelper::getExpandedUri(
+ css::uno::Reference<css::uno::XComponentContext> const & context,
+ OUString const & uri)
+{
+ css::uno::Reference<css::uri::XVndSunStarExpandUrlReference> ref(
+ css::uri::UriReferenceFactory::create(context)->parse(uri),
+ css::uno::UNO_QUERY);
+ return ref.is()
+ ? ref->expand(css::util::theMacroExpander::get(context)) : uri;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/graphicmimetype.cxx b/comphelper/source/misc/graphicmimetype.cxx
new file mode 100644
index 0000000000..8ae3dad561
--- /dev/null
+++ b/comphelper/source/misc/graphicmimetype.cxx
@@ -0,0 +1,239 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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 <comphelper/graphicmimetype.hxx>
+#include <comphelper/mediamimetype.hxx>
+
+#include <map>
+#include <set>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/graphic/GraphicProvider.hpp>
+#include <com/sun/star/graphic/XGraphicProvider.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+
+using namespace css;
+using namespace css::beans;
+using namespace css::graphic;
+using namespace css::io;
+using namespace css::uno;
+
+namespace comphelper
+{
+OUString GraphicMimeTypeHelper::GetMimeTypeForExtension(std::string_view rExt)
+{
+ struct XMLGraphicMimeTypeMapper
+ {
+ const char* pExt;
+ const char* pMimeType;
+ };
+
+ static const XMLGraphicMimeTypeMapper aMapper[]
+ = { { "gif", "image/gif" }, { "png", "image/png" }, { "jpg", "image/jpeg" },
+ { "tif", "image/tiff" }, { "svg", "image/svg+xml" }, { "pdf", "application/pdf" },
+ { "wmf", "image/x-wmf" }, { "emf", "image/x-emf" }, { "eps", "image/x-eps" },
+ { "bmp", "image/bmp" }, { "pct", "image/x-pict" }, { "svm", "image/x-svm" } };
+
+ OUString aMimeType;
+
+ size_t const nCount = std::size(aMapper);
+ for (size_t i = 0; (i < nCount) && aMimeType.isEmpty(); ++i)
+ {
+ if (rExt == aMapper[i].pExt)
+ aMimeType = OUString(aMapper[i].pMimeType, strlen(aMapper[i].pMimeType),
+ RTL_TEXTENCODING_ASCII_US);
+ }
+
+ return aMimeType;
+}
+
+OUString GraphicMimeTypeHelper::GetMimeTypeForXGraphic(const Reference<XGraphic>& xGraphic)
+{
+ OUString aSourceMimeType;
+ Reference<XPropertySet> const xGraphicPropertySet(xGraphic, UNO_QUERY);
+ if (xGraphicPropertySet.is() && // it's null if it's an external link
+ (xGraphicPropertySet->getPropertyValue("MimeType") >>= aSourceMimeType))
+ {
+ return aSourceMimeType;
+ }
+ return "";
+}
+
+OUString
+GraphicMimeTypeHelper::GetMimeTypeForImageStream(const Reference<XInputStream>& xInputStream)
+{
+ // Create the graphic to retrieve the mimetype from it
+ Reference<XGraphicProvider> xProvider
+ = css::graphic::GraphicProvider::create(comphelper::getProcessComponentContext());
+ Sequence<PropertyValue> aMediaProperties{ comphelper::makePropertyValue("InputStream",
+ xInputStream) };
+ Reference<XGraphic> xGraphic(xProvider->queryGraphic(aMediaProperties));
+
+ return GetMimeTypeForXGraphic(xGraphic);
+}
+
+OUString GraphicMimeTypeHelper::GetMimeTypeForConvertDataFormat(ConvertDataFormat convertDataFormat)
+{
+ switch (convertDataFormat)
+ {
+ case ConvertDataFormat::BMP:
+ return "image/bmp";
+ case ConvertDataFormat::GIF:
+ return "image/gif";
+ case ConvertDataFormat::JPG:
+ return "image/jpeg";
+ case ConvertDataFormat::PCT:
+ return "image/x-pict";
+ case ConvertDataFormat::PNG:
+ return "image/png";
+ case ConvertDataFormat::SVM:
+ return "image/x-svm";
+ case ConvertDataFormat::TIF:
+ return "image/tiff";
+ case ConvertDataFormat::WMF:
+ return "image/x-wmf";
+ case ConvertDataFormat::EMF:
+ return "image/x-emf";
+ case ConvertDataFormat::SVG:
+ return "image/svg+xml";
+ case ConvertDataFormat::MET: // What is this?
+ case ConvertDataFormat::Unknown:
+ default:
+ return "";
+ }
+}
+
+char const* GraphicMimeTypeHelper::GetExtensionForConvertDataFormat(ConvertDataFormat nFormat)
+{
+ char const* pExt = nullptr;
+ // create extension
+ if (nFormat != ConvertDataFormat::Unknown)
+ {
+ switch (nFormat)
+ {
+ case ConvertDataFormat::BMP:
+ pExt = ".bmp";
+ break;
+ case ConvertDataFormat::GIF:
+ pExt = ".gif";
+ break;
+ case ConvertDataFormat::JPG:
+ pExt = ".jpg";
+ break;
+ case ConvertDataFormat::MET:
+ pExt = ".met";
+ break;
+ case ConvertDataFormat::PCT:
+ pExt = ".pct";
+ break;
+ case ConvertDataFormat::PNG:
+ pExt = ".png";
+ break;
+ case ConvertDataFormat::SVM:
+ pExt = ".svm";
+ break;
+ case ConvertDataFormat::TIF:
+ pExt = ".tif";
+ break;
+ case ConvertDataFormat::WMF:
+ pExt = ".wmf";
+ break;
+ case ConvertDataFormat::EMF:
+ pExt = ".emf";
+ break;
+
+ default:
+ pExt = ".grf";
+ break;
+ }
+ }
+ return pExt;
+}
+
+static auto GetMediaMimes() -> std::map<OString, OString> const&
+{
+ static std::map<OString, OString> const mimes = {
+ { "mp4", "video/mp4" },
+ { "ts", "video/MP2T" },
+ { "mpeg", "video/mpeg" },
+ { "mpg", "video/mpeg" },
+ { "mkv", "video/x-matroska" },
+ { "webm", "video/webm" },
+ { "ogv", "video/ogg" },
+ { "mov", "video/quicktime" },
+ { "wmv", "video/x-ms-wmv" },
+ { "avi", "video/x-msvideo" },
+ { "m4a", "audio/mp4" },
+ { "aac", "audio/aac" },
+ { "mp3", "audio/mpeg" }, // https://bugs.chromium.org/p/chromium/issues/detail?id=227004
+ { "ogg", "audio/ogg" },
+ { "oga", "audio/ogg" },
+ { "opus", "audio/ogg" },
+ { "flac", "audio/flac" }, // missing at IANA?
+ // note there is RFC 2631 but i got the impression that vnd.wave
+ // requires specifying the codec in the container; also this page
+ // says "Historic" whatever that means:
+ // https://www.iana.org/assignments/wave-avi-codec-registry/wave-avi-codec-registry.xhtml
+ { "wav", "audio/x-wav" },
+ };
+ return mimes;
+}
+
+auto IsMediaMimeType(::std::string_view const rMimeType) -> bool
+{
+ return IsMediaMimeType(OStringToOUString(rMimeType, RTL_TEXTENCODING_UTF8));
+}
+
+auto IsMediaMimeType(OUString const& rMimeType) -> bool
+{
+ static std::set<OUString> mimes;
+ if (mimes.empty())
+ {
+ auto const& rMap(GetMediaMimes());
+ for (auto const& it : rMap)
+ {
+ mimes.insert(OStringToOUString(it.second, RTL_TEXTENCODING_UTF8));
+ }
+ }
+ return rMimeType == AVMEDIA_MIMETYPE_COMMON || mimes.find(rMimeType) != mimes.end();
+}
+
+auto GuessMediaMimeType(::std::u16string_view rFileName) -> OUString
+{
+ if (auto const i = rFileName.rfind('.'); i != ::std::string_view::npos)
+ {
+ OString const ext(OUStringToOString(rFileName.substr(i + 1), RTL_TEXTENCODING_UTF8));
+ auto const& rMap(GetMediaMimes());
+ auto const it(rMap.find(ext));
+ if (it != rMap.end())
+ {
+ return OStringToOUString(it->second, RTL_TEXTENCODING_ASCII_US);
+ }
+ }
+ return OUString();
+}
+
+} // namespace comphelper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/hash.cxx b/comphelper/source/misc/hash.cxx
new file mode 100644
index 0000000000..25b93ad87e
--- /dev/null
+++ b/comphelper/source/misc/hash.cxx
@@ -0,0 +1,270 @@
+/* -*- 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/uno/RuntimeException.hpp>
+#include <comphelper/hash.hxx>
+#include <rtl/ustring.hxx>
+#include <rtl/alloc.h>
+#include <osl/endian.h>
+#include <config_oox.h>
+
+#if USE_TLS_NSS
+#include <nss.h>
+#include <nspr.h>
+#include <sechash.h>
+#elif USE_TLS_OPENSSL
+#include <openssl/evp.h>
+#include <openssl/sha.h>
+#endif // USE_TLS_OPENSSL
+
+namespace comphelper {
+
+struct HashImpl
+{
+
+#if USE_TLS_NSS
+ HASHContext* mpContext;
+
+ HASH_HashType getNSSType() const
+ {
+ switch (meType)
+ {
+ case HashType::MD5:
+ return HASH_AlgMD5;
+ case HashType::SHA1:
+ return HASH_AlgSHA1;
+ case HashType::SHA256:
+ return HASH_AlgSHA256;
+ case HashType::SHA384:
+ return HASH_AlgSHA384;
+ case HashType::SHA512:
+ return HASH_AlgSHA512;
+ }
+
+ return HASH_AlgNULL;
+ }
+#elif USE_TLS_OPENSSL
+ EVP_MD_CTX* mpContext;
+
+ const EVP_MD* getOpenSSLType() const
+ {
+ switch (meType)
+ {
+ case HashType::MD5:
+ return EVP_md5();
+ case HashType::SHA1:
+ return EVP_sha1();
+ case HashType::SHA256:
+ return EVP_sha256();
+ case HashType::SHA384:
+ return EVP_sha384();
+ case HashType::SHA512:
+ return EVP_sha512();
+ }
+
+ return nullptr;
+ }
+#endif
+
+ HashType const meType;
+
+ HashImpl(HashType eType):
+ meType(eType)
+ {
+
+#if USE_TLS_NSS
+ if (!NSS_IsInitialized())
+ {
+ auto const e = NSS_NoDB_Init(nullptr);
+ if (e != SECSuccess)
+ {
+ PRErrorCode error = PR_GetError();
+ const char* errorText = PR_ErrorToName(error);
+ throw css::uno::RuntimeException("NSS_NoDB_Init failed with " + OUString(errorText, strlen(errorText), RTL_TEXTENCODING_UTF8) + " (" + OUString::number(static_cast<int>(error)) + ")");
+ }
+ }
+ mpContext = HASH_Create(getNSSType());
+ HASH_Begin(mpContext);
+#elif USE_TLS_OPENSSL
+ mpContext = EVP_MD_CTX_create();
+ EVP_DigestInit_ex(mpContext, getOpenSSLType(), nullptr);
+#endif
+ }
+
+ ~HashImpl()
+ {
+#if USE_TLS_NSS
+ HASH_Destroy(mpContext);
+#elif USE_TLS_OPENSSL
+ EVP_MD_CTX_destroy(mpContext);
+#endif
+ }
+};
+
+Hash::Hash(HashType eType):
+ mpImpl(new HashImpl(eType))
+{
+}
+
+Hash::~Hash()
+{
+}
+
+void Hash::update(const unsigned char* pInput, size_t length)
+{
+#if USE_TLS_NSS
+ HASH_Update(mpImpl->mpContext, pInput, length);
+#elif USE_TLS_OPENSSL
+ EVP_DigestUpdate(mpImpl->mpContext, pInput, length);
+#else
+ (void)pInput;
+ (void)length;
+#endif
+}
+
+std::vector<unsigned char> Hash::finalize()
+{
+ std::vector<unsigned char> hash(getLength(), 0);
+ unsigned int digestWrittenLength;
+#if USE_TLS_NSS
+ HASH_End(mpImpl->mpContext, hash.data(), &digestWrittenLength, getLength());
+#elif USE_TLS_OPENSSL
+ EVP_DigestFinal_ex(mpImpl->mpContext, hash.data(), &digestWrittenLength);
+#else
+ (void)digestWrittenLength;
+#endif
+
+ return hash;
+}
+
+size_t Hash::getLength() const
+{
+ switch (mpImpl->meType)
+ {
+ case HashType::MD5:
+ return MD5_HASH_LENGTH;
+ case HashType::SHA1:
+ return SHA1_HASH_LENGTH;
+ case HashType::SHA256:
+ return SHA256_HASH_LENGTH;
+ case HashType::SHA384:
+ return SHA384_HASH_LENGTH;
+ case HashType::SHA512:
+ return SHA512_HASH_LENGTH;
+ }
+
+ return 0;
+}
+
+std::vector<unsigned char> Hash::calculateHash(const unsigned char* pInput, size_t length, HashType eType)
+{
+ Hash aHash(eType);
+ aHash.update(pInput, length);
+ return aHash.finalize();
+}
+
+std::vector<unsigned char> Hash::calculateHash(
+ const unsigned char* pInput, size_t nLength,
+ const unsigned char* pSalt, size_t nSaltLen,
+ sal_uInt32 nSpinCount,
+ IterCount eIterCount,
+ HashType eType)
+{
+ if (!pSalt)
+ nSaltLen = 0;
+
+ if (!nSaltLen && !nSpinCount)
+ return calculateHash( pInput, nLength, eType);
+
+ Hash aHash(eType);
+ if (nSaltLen)
+ {
+ std::vector<unsigned char> initialData( nSaltLen + nLength);
+ std::copy( pSalt, pSalt + nSaltLen, initialData.begin());
+ std::copy( pInput, pInput + nLength, initialData.begin() + nSaltLen);
+ aHash.update( initialData.data(), initialData.size());
+ rtl_secureZeroMemory( initialData.data(), initialData.size());
+ }
+ else
+ {
+ aHash.update( pInput, nLength);
+ }
+ std::vector<unsigned char> hash( aHash.finalize());
+
+ if (nSpinCount)
+ {
+ // https://msdn.microsoft.com/en-us/library/dd920692
+ // says the iteration is concatenated after the hash.
+ // https://msdn.microsoft.com/en-us/library/dd924776 and
+ // https://msdn.microsoft.com/en-us/library/dd925430
+ // say the iteration is prepended to the hash.
+ const size_t nAddIter = (eIterCount == IterCount::NONE ? 0 : 4);
+ const size_t nIterPos = (eIterCount == IterCount::APPEND ? hash.size() : 0);
+ const size_t nHashPos = (eIterCount == IterCount::PREPEND ? nAddIter : 0);
+ std::vector<unsigned char> data( hash.size() + nAddIter, 0);
+ for (sal_uInt32 i = 0; i < nSpinCount; ++i)
+ {
+ std::copy( hash.begin(), hash.end(), data.begin() + nHashPos);
+ if (nAddIter)
+ {
+#ifdef OSL_BIGENDIAN
+ sal_uInt32 be = i;
+ sal_uInt8* p = reinterpret_cast<sal_uInt8*>(&be);
+ std::swap( p[0], p[3] );
+ std::swap( p[1], p[2] );
+ memcpy( data.data() + nIterPos, &be, nAddIter);
+#else
+ memcpy( data.data() + nIterPos, &i, nAddIter);
+#endif
+ }
+ /* TODO: isn't there something better than
+ * creating/finalizing/destroying on each iteration? */
+ Hash aReHash(eType);
+ aReHash.update( data.data(), data.size());
+ hash = aReHash.finalize();
+ }
+ }
+
+ return hash;
+}
+
+std::vector<unsigned char> Hash::calculateHash(
+ const OUString& rPassword,
+ const std::vector<unsigned char>& rSaltValue,
+ sal_uInt32 nSpinCount,
+ IterCount eIterCount,
+ HashType eType)
+{
+ const unsigned char* pPassBytes = reinterpret_cast<const unsigned char*>(rPassword.getStr());
+ const size_t nPassBytesLen = rPassword.getLength() * 2;
+#ifdef OSL_BIGENDIAN
+ // Swap UTF16-BE to UTF16-LE
+ std::vector<unsigned char> vPass;
+ if (nPassBytesLen)
+ {
+ vPass.resize( nPassBytesLen);
+ std::copy( pPassBytes, pPassBytes + nPassBytesLen, vPass.begin());
+ unsigned char* p = vPass.data();
+ unsigned char const * const pEnd = p + nPassBytesLen;
+ for ( ; p < pEnd; p += 2 )
+ {
+ std::swap( p[0], p[1] );
+ }
+ pPassBytes = vPass.data();
+ }
+#endif
+ return calculateHash( pPassBytes, nPassBytesLen, rSaltValue.data(), rSaltValue.size(), nSpinCount,
+ eIterCount, eType);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/instancelocker.cxx b/comphelper/source/misc/instancelocker.cxx
new file mode 100644
index 0000000000..465cc51858
--- /dev/null
+++ b/comphelper/source/misc/instancelocker.cxx
@@ -0,0 +1,446 @@
+/* -*- 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 <cppuhelper/supportsservice.hxx>
+
+#include <com/sun/star/util/CloseVetoException.hpp>
+#include <com/sun/star/util/XCloseBroadcaster.hpp>
+#include <com/sun/star/util/XCloseable.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/frame/XDesktop.hpp>
+#include <com/sun/star/frame/TerminationVetoException.hpp>
+#include <com/sun/star/frame/DoubleInitializationException.hpp>
+#include <com/sun/star/embed/Actions.hpp>
+#include <com/sun/star/embed/XActionsApproval.hpp>
+#include <utility>
+
+#include "instancelocker.hxx"
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+using namespace ::com::sun::star;
+
+
+// OInstanceLocker
+
+
+OInstanceLocker::OInstanceLocker()
+: m_bDisposed( false )
+, m_bInitialized( false )
+{
+}
+
+
+OInstanceLocker::~OInstanceLocker()
+{
+ if ( !m_bDisposed )
+ {
+ osl_atomic_increment(&m_refCount); // to call dispose
+ try {
+ dispose();
+ }
+ catch ( uno::RuntimeException& )
+ {}
+ }
+}
+
+// XComponent
+
+void SAL_CALL OInstanceLocker::dispose()
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ if ( m_bDisposed )
+ throw lang::DisposedException();
+
+ lang::EventObject aSource( static_cast< ::cppu::OWeakObject* >(this) );
+ m_aListenersContainer.disposeAndClear( aGuard, aSource );
+ if ( m_xLockListener.is() )
+ {
+ auto tmp = std::move(m_xLockListener);
+ aGuard.unlock();
+ tmp->Dispose();
+ aGuard.lock();
+ }
+
+ m_bDisposed = true;
+}
+
+
+void SAL_CALL OInstanceLocker::addEventListener( const uno::Reference< lang::XEventListener >& xListener )
+{
+ std::unique_lock aGuard( m_aMutex );
+ if ( m_bDisposed )
+ throw lang::DisposedException(); // TODO
+
+ m_aListenersContainer.addInterface( aGuard, xListener );
+}
+
+
+void SAL_CALL OInstanceLocker::removeEventListener( const uno::Reference< lang::XEventListener >& xListener )
+{
+ std::unique_lock aGuard( m_aMutex );
+ m_aListenersContainer.removeInterface( aGuard, xListener );
+}
+
+// XInitialization
+
+void SAL_CALL OInstanceLocker::initialize( const uno::Sequence< uno::Any >& aArguments )
+{
+ std::unique_lock aGuard( m_aMutex );
+ if ( m_bInitialized )
+ throw frame::DoubleInitializationException();
+
+ if ( m_bDisposed )
+ throw lang::DisposedException(); // TODO
+
+ if ( !m_refCount )
+ throw uno::RuntimeException(); // the object must be refcounted already!
+
+ uno::Reference< uno::XInterface > xInstance;
+ uno::Reference< embed::XActionsApproval > xApproval;
+
+ try
+ {
+ sal_Int32 nLen = aArguments.getLength();
+ if ( nLen < 2 || nLen > 3 )
+ throw lang::IllegalArgumentException(
+ "Wrong count of parameters!",
+ uno::Reference< uno::XInterface >(),
+ 0 );
+
+ if ( !( aArguments[0] >>= xInstance ) || !xInstance.is() )
+ throw lang::IllegalArgumentException(
+ "Nonempty reference is expected as the first argument!",
+ uno::Reference< uno::XInterface >(),
+ 0 );
+
+ sal_Int32 nModes = 0;
+ if (
+ !( aArguments[1] >>= nModes ) ||
+ (
+ !( nModes & embed::Actions::PREVENT_CLOSE ) &&
+ !( nModes & embed::Actions::PREVENT_TERMINATION )
+ )
+ )
+ {
+ throw lang::IllegalArgumentException(
+ "The correct modes set is expected as the second argument!",
+ uno::Reference< uno::XInterface >(),
+ 0 );
+ }
+
+ if ( nLen == 3 && !( aArguments[2] >>= xApproval ) )
+ throw lang::IllegalArgumentException(
+ "If the third argument is provided, it must be XActionsApproval implementation!",
+ uno::Reference< uno::XInterface >(),
+ 0 );
+
+ m_xLockListener = new OLockListener( uno::Reference< lang::XComponent > ( static_cast< lang::XComponent* >( this ) ),
+ xInstance,
+ nModes,
+ xApproval );
+ m_xLockListener->Init();
+ }
+ catch( uno::Exception& )
+ {
+ aGuard.unlock();
+ dispose();
+ throw;
+ }
+
+ m_bInitialized = true;
+}
+
+// XServiceInfo
+OUString SAL_CALL OInstanceLocker::getImplementationName( )
+{
+ return "com.sun.star.comp.embed.InstanceLocker";
+}
+
+sal_Bool SAL_CALL OInstanceLocker::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL OInstanceLocker::getSupportedServiceNames()
+{
+ return { "com.sun.star.embed.InstanceLocker" };
+}
+
+// OLockListener
+
+
+OLockListener::OLockListener( uno::WeakReference< lang::XComponent > xWrapper,
+ uno::Reference< uno::XInterface > xInstance,
+ sal_Int32 nMode,
+ uno::Reference< embed::XActionsApproval > xApproval )
+: m_xInstance(std::move( xInstance ))
+, m_xApproval(std::move( xApproval ))
+, m_xWrapper(std::move( xWrapper ))
+, m_bDisposed( false )
+, m_bInitialized( false )
+, m_nMode( nMode )
+{
+}
+
+
+OLockListener::~OLockListener()
+{
+}
+
+
+void OLockListener::Dispose()
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ if ( m_bDisposed )
+ return;
+
+ auto xInstance = std::move(m_xInstance);
+ auto xApproval = std::move(m_xApproval);
+ auto nMode = m_nMode;
+ m_bDisposed = true;
+ aGuard.unlock();
+
+ if ( nMode & embed::Actions::PREVENT_CLOSE )
+ {
+ try
+ {
+ uno::Reference< util::XCloseBroadcaster > xCloseBroadcaster( xInstance, uno::UNO_QUERY );
+ if ( xCloseBroadcaster.is() )
+ xCloseBroadcaster->removeCloseListener( static_cast< util::XCloseListener* >( this ) );
+
+ uno::Reference< util::XCloseable > xCloseable( xInstance, uno::UNO_QUERY );
+ if ( xCloseable.is() )
+ xCloseable->close( true );
+ }
+ catch( uno::Exception& )
+ {}
+ }
+
+ if ( nMode & embed::Actions::PREVENT_TERMINATION )
+ {
+ try
+ {
+ uno::Reference< frame::XDesktop > xDesktop( xInstance, uno::UNO_QUERY_THROW );
+ xDesktop->removeTerminateListener( static_cast< frame::XTerminateListener* >( this ) );
+ }
+ catch( uno::Exception& )
+ {}
+ }
+}
+
+// XEventListener
+
+void SAL_CALL OLockListener::disposing( const lang::EventObject& aEvent )
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ // object is disposed
+ if ( aEvent.Source != m_xInstance )
+ return;
+
+ // the object does not listen for anything any more
+ m_nMode = 0;
+
+ // dispose the wrapper;
+ uno::Reference< lang::XComponent > xComponent( m_xWrapper.get(), uno::UNO_QUERY );
+ aGuard.unlock();
+ if ( xComponent.is() )
+ {
+ try { xComponent->dispose(); }
+ catch( uno::Exception& ){}
+ }
+}
+
+
+// XCloseListener
+
+void SAL_CALL OLockListener::queryClosing( const lang::EventObject& aEvent, sal_Bool )
+{
+ // GetsOwnership parameter is always ignored, the user of the service must close the object always
+ std::unique_lock aGuard( m_aMutex );
+ if ( !(!m_bDisposed && aEvent.Source == m_xInstance && ( m_nMode & embed::Actions::PREVENT_CLOSE )) )
+ return;
+
+ try
+ {
+ uno::Reference< embed::XActionsApproval > xApprove = m_xApproval;
+
+ // unlock the mutex here
+ aGuard.unlock();
+
+ if ( xApprove.is() && xApprove->approveAction( embed::Actions::PREVENT_CLOSE ) )
+ throw util::CloseVetoException();
+ }
+ catch( util::CloseVetoException& )
+ {
+ // rethrow this exception
+ throw;
+ }
+ catch( uno::Exception& )
+ {
+ // no action should be done
+ }
+}
+
+
+void SAL_CALL OLockListener::notifyClosing( const lang::EventObject& aEvent )
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ // object is closed, no reason to listen
+ if ( aEvent.Source != m_xInstance )
+ return;
+
+ uno::Reference< util::XCloseBroadcaster > xCloseBroadcaster( aEvent.Source, uno::UNO_QUERY );
+ if ( !xCloseBroadcaster.is() )
+ return;
+
+ xCloseBroadcaster->removeCloseListener( static_cast< util::XCloseListener* >( this ) );
+ m_nMode &= ~embed::Actions::PREVENT_CLOSE;
+ if ( !m_nMode )
+ {
+ // dispose the wrapper;
+ uno::Reference< lang::XComponent > xComponent( m_xWrapper.get(), uno::UNO_QUERY );
+ aGuard.unlock();
+ if ( xComponent.is() )
+ {
+ try { xComponent->dispose(); }
+ catch( uno::Exception& ){}
+ }
+ }
+}
+
+
+// XTerminateListener
+
+void SAL_CALL OLockListener::queryTermination( const lang::EventObject& aEvent )
+{
+ std::unique_lock aGuard( m_aMutex );
+ if ( !(aEvent.Source == m_xInstance && ( m_nMode & embed::Actions::PREVENT_TERMINATION )) )
+ return;
+
+ try
+ {
+ uno::Reference< embed::XActionsApproval > xApprove = m_xApproval;
+
+ // unlock the mutex here
+ aGuard.unlock();
+
+ if ( xApprove.is() && xApprove->approveAction( embed::Actions::PREVENT_TERMINATION ) )
+ throw frame::TerminationVetoException();
+ }
+ catch( frame::TerminationVetoException& )
+ {
+ // rethrow this exception
+ throw;
+ }
+ catch( uno::Exception& )
+ {
+ // no action should be done
+ }
+}
+
+
+void SAL_CALL OLockListener::notifyTermination( const lang::EventObject& aEvent )
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ // object is terminated, no reason to listen
+ if ( aEvent.Source != m_xInstance )
+ return;
+
+ uno::Reference< frame::XDesktop > xDesktop( aEvent.Source, uno::UNO_QUERY );
+ if ( !xDesktop.is() )
+ return;
+
+ try
+ {
+ xDesktop->removeTerminateListener( static_cast< frame::XTerminateListener* >( this ) );
+ m_nMode &= ~embed::Actions::PREVENT_TERMINATION;
+ if ( !m_nMode )
+ {
+ // dispose the wrapper;
+ uno::Reference< lang::XComponent > xComponent( m_xWrapper.get(), uno::UNO_QUERY );
+ aGuard.unlock();
+ if ( xComponent.is() )
+ {
+ try { xComponent->dispose(); }
+ catch( uno::Exception& ){}
+ }
+ }
+ }
+ catch( uno::Exception& )
+ {}
+}
+
+
+// XInitialization
+
+void OLockListener::Init()
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ if ( m_bDisposed || m_bInitialized )
+ return;
+
+ try
+ {
+ if ( m_nMode & embed::Actions::PREVENT_CLOSE )
+ {
+ uno::Reference< util::XCloseBroadcaster > xCloseBroadcaster( m_xInstance, uno::UNO_QUERY_THROW );
+ xCloseBroadcaster->addCloseListener( static_cast< util::XCloseListener* >( this ) );
+ }
+
+ if ( m_nMode & embed::Actions::PREVENT_TERMINATION )
+ {
+ uno::Reference< frame::XDesktop > xDesktop( m_xInstance, uno::UNO_QUERY_THROW );
+ xDesktop->addTerminateListener( static_cast< frame::XTerminateListener* >( this ) );
+ }
+ }
+ catch( uno::Exception& )
+ {
+ // dispose the wrapper;
+ uno::Reference< lang::XComponent > xComponent( m_xWrapper.get(), uno::UNO_QUERY );
+ aGuard.unlock();
+ if ( xComponent.is() )
+ {
+ try { xComponent->dispose(); }
+ catch( uno::Exception& ){}
+ }
+
+ throw;
+ }
+
+ m_bInitialized = true;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_embed_InstanceLocker(
+ css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new OInstanceLocker());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/instancelocker.hxx b/comphelper/source/misc/instancelocker.hxx
new file mode 100644
index 0000000000..6a050c7f43
--- /dev/null
+++ b/comphelper/source/misc/instancelocker.hxx
@@ -0,0 +1,111 @@
+/* -*- 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 <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/util/XCloseListener.hpp>
+#include <com/sun/star/frame/XTerminateListener.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <comphelper/interfacecontainer4.hxx>
+#include <cppuhelper/weakref.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <rtl/ref.hxx>
+#include <mutex>
+
+namespace com::sun::star::embed { class XActionsApproval; }
+
+
+class OLockListener;
+
+// the service is implemented as a wrapper to be able to die by refcount
+// the disposing mechanics is required for java related scenarios
+class OInstanceLocker : public ::cppu::WeakImplHelper< css::lang::XComponent,
+ css::lang::XInitialization,
+ css::lang::XServiceInfo >
+{
+ std::mutex m_aMutex;
+
+ rtl::Reference< OLockListener > m_xLockListener;
+
+ comphelper::OInterfaceContainerHelper4<css::lang::XEventListener> m_aListenersContainer; // list of listeners
+
+ bool m_bDisposed;
+ bool m_bInitialized;
+
+public:
+ explicit OInstanceLocker();
+ virtual ~OInstanceLocker() override;
+
+// XComponent
+ virtual void SAL_CALL dispose() override;
+ virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override;
+ virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override;
+
+// XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+// XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+};
+
+
+class OLockListener : public ::cppu::WeakImplHelper< css::util::XCloseListener,
+ css::frame::XTerminateListener >
+{
+ std::mutex m_aMutex;
+ css::uno::Reference< css::uno::XInterface > m_xInstance;
+ css::uno::Reference< css::embed::XActionsApproval > m_xApproval;
+
+ css::uno::WeakReference< css::lang::XComponent > m_xWrapper;
+
+ bool m_bDisposed;
+ bool m_bInitialized;
+
+ sal_Int32 m_nMode;
+
+public:
+ OLockListener( css::uno::WeakReference< css::lang::XComponent > xWrapper,
+ css::uno::Reference< css::uno::XInterface > xInstance,
+ sal_Int32 nMode,
+ css::uno::Reference< css::embed::XActionsApproval > xApproval );
+
+ virtual ~OLockListener() override;
+
+ void Init();
+ void Dispose();
+
+// XEventListener
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+// XCloseListener
+ virtual void SAL_CALL queryClosing( const css::lang::EventObject& Source, sal_Bool GetsOwnership ) override;
+ virtual void SAL_CALL notifyClosing( const css::lang::EventObject& Source ) override;
+
+// XTerminateListener
+ virtual void SAL_CALL queryTermination( const css::lang::EventObject& Event ) override;
+ virtual void SAL_CALL notifyTermination( const css::lang::EventObject& Event ) override;
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/interaction.cxx b/comphelper/source/misc/interaction.cxx
new file mode 100644
index 0000000000..9e7b1706d1
--- /dev/null
+++ b/comphelper/source/misc/interaction.cxx
@@ -0,0 +1,71 @@
+/* -*- 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 <comphelper/interaction.hxx>
+
+#include <comphelper/sequence.hxx>
+#include <utility>
+#include <osl/diagnose.h>
+
+
+namespace comphelper
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::task;
+
+ OInteractionRequest::OInteractionRequest(Any _aRequestDescription)
+ :m_aRequest(std::move(_aRequestDescription))
+ {
+ }
+
+ OInteractionRequest::OInteractionRequest(Any aRequestDescription,
+ std::vector<Reference<XInteractionContinuation>>&& rContinuations)
+ : m_aRequest(std::move(aRequestDescription))
+ , m_aContinuations(std::move(rContinuations))
+ {
+ }
+
+ void OInteractionRequest::addContinuation(const Reference< XInteractionContinuation >& _rxContinuation)
+ {
+ OSL_ENSURE(_rxContinuation.is(), "OInteractionRequest::addContinuation: invalid argument!");
+ if (_rxContinuation.is())
+ {
+ m_aContinuations.push_back(_rxContinuation);
+ }
+ }
+
+
+ Any SAL_CALL OInteractionRequest::getRequest( )
+ {
+ return m_aRequest;
+ }
+
+
+ Sequence< Reference< XInteractionContinuation > > SAL_CALL OInteractionRequest::getContinuations( )
+ {
+ return comphelper::containerToSequence(m_aContinuations);
+ }
+
+
+} // namespace comphelper
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/logging.cxx b/comphelper/source/misc/logging.cxx
new file mode 100644
index 0000000000..3bce820a82
--- /dev/null
+++ b/comphelper/source/misc/logging.cxx
@@ -0,0 +1,161 @@
+/* -*- 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 <comphelper/logging.hxx>
+
+#include <com/sun/star/logging/LoggerPool.hpp>
+
+#include <comphelper/diagnose_ex.hxx>
+#include <osl/diagnose.h>
+
+
+namespace comphelper
+{
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::XComponentContext;
+ using ::com::sun::star::logging::XLoggerPool;
+ using ::com::sun::star::logging::LoggerPool;
+ using ::com::sun::star::logging::XLogger;
+ using ::com::sun::star::uno::Exception;
+
+ class EventLogger_Impl
+ {
+ private:
+ Reference< XComponentContext > m_aContext;
+ Reference< XLogger > m_xLogger;
+
+ public:
+ EventLogger_Impl( const Reference< XComponentContext >& _rxContext, const OUString& _rLoggerName );
+
+ bool isValid() const { return m_xLogger.is(); }
+ const Reference< XLogger >& getLogger() const { return m_xLogger; }
+ };
+
+ EventLogger_Impl::EventLogger_Impl( const Reference< XComponentContext >& _rxContext, const OUString& _rLoggerName )
+ :m_aContext( _rxContext )
+ {
+ try
+ {
+ Reference< XLoggerPool > xPool( LoggerPool::get( m_aContext ) );
+ if ( !_rLoggerName.isEmpty() )
+ m_xLogger = xPool->getNamedLogger( _rLoggerName );
+ else
+ m_xLogger = xPool->getDefaultLogger();
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION(
+ "comphelper", "EventLogger_Impl::impl_createLogger_nothrow: caught an exception!" );
+ }
+ }
+
+ EventLogger::EventLogger( const Reference< XComponentContext >& _rxContext, const char* _pAsciiLoggerName )
+ :m_pImpl( std::make_shared<EventLogger_Impl>( _rxContext, OUString::createFromAscii( _pAsciiLoggerName ) ) )
+ {
+ }
+
+ bool EventLogger::isLoggable( const sal_Int32 _nLogLevel ) const
+ {
+ if ( !m_pImpl->isValid() )
+ return false;
+
+ try
+ {
+ return m_pImpl->getLogger()->isLoggable( _nLogLevel );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "comphelper", "EventLogger::isLoggable: caught an exception!" );
+ }
+
+ return false;
+ }
+
+ const css::uno::Reference<css::logging::XLogger> & EventLogger::getLogger() const
+ {
+ return m_pImpl->getLogger();
+ }
+
+
+ namespace
+ {
+ void lcl_replaceParameter( OUString& _inout_Message, const char* _rPlaceHolder, std::u16string_view _rReplacement )
+ {
+ sal_Int32 nPlaceholderPosition = _inout_Message.indexOfAsciiL( _rPlaceHolder, strlen(_rPlaceHolder) );
+ OSL_ENSURE( nPlaceholderPosition >= 0, "lcl_replaceParameter: placeholder not found!" );
+ if ( nPlaceholderPosition < 0 )
+ return;
+
+ _inout_Message = _inout_Message.replaceAt( nPlaceholderPosition, strlen(_rPlaceHolder), _rReplacement );
+ }
+ }
+
+
+ void EventLogger::impl_log( const sal_Int32 _nLogLevel,
+ const char* _pSourceClass, const char* _pSourceMethod, const OUString& _rMessage,
+ const OptionalString& _rArgument1, const OptionalString& _rArgument2,
+ const OptionalString& _rArgument3, const OptionalString& _rArgument4,
+ const OptionalString& _rArgument5, const OptionalString& _rArgument6 ) const
+ {
+ OUString sMessage( _rMessage );
+ if ( !!_rArgument1 )
+ lcl_replaceParameter( sMessage, "$1$", *_rArgument1 );
+
+ if ( !!_rArgument2 )
+ lcl_replaceParameter( sMessage, "$2$", *_rArgument2 );
+
+ if ( !!_rArgument3 )
+ lcl_replaceParameter( sMessage, "$3$", *_rArgument3 );
+
+ if ( !!_rArgument4 )
+ lcl_replaceParameter( sMessage, "$4$", *_rArgument4 );
+
+ if ( !!_rArgument5 )
+ lcl_replaceParameter( sMessage, "$5$", *_rArgument5 );
+
+ if ( !!_rArgument6 )
+ lcl_replaceParameter( sMessage, "$6$", *_rArgument6 );
+
+ try
+ {
+ Reference< XLogger > xLogger( m_pImpl->getLogger() );
+ OSL_PRECOND( xLogger.is(), "EventLogger::impl_log: should never be called without a logger!" );
+ if ( _pSourceClass && _pSourceMethod )
+ {
+ xLogger->logp(
+ _nLogLevel,
+ OUString::createFromAscii( _pSourceClass ),
+ OUString::createFromAscii( _pSourceMethod ),
+ sMessage
+ );
+ }
+ else
+ {
+ xLogger->log( _nLogLevel, sMessage );
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "comphelper", "EventLogger::impl_log: caught an exception!" );
+ }
+ }
+} // namespace comphelper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/lok.cxx b/comphelper/source/misc/lok.cxx
new file mode 100644
index 0000000000..b5cbcc74cb
--- /dev/null
+++ b/comphelper/source/misc/lok.cxx
@@ -0,0 +1,308 @@
+/* -*- 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 <comphelper/lok.hxx>
+#include <osl/process.h>
+#include <i18nlangtag/languagetag.hxx>
+#include <sal/log.hxx>
+
+#include <iostream>
+
+namespace comphelper::LibreOfficeKit
+{
+
+static bool g_bActive(false);
+
+static bool g_bPartInInvalidation(false);
+
+static bool g_bTiledPainting(false);
+
+static bool g_bDialogPainting(false);
+
+static bool g_bTiledAnnotations(true);
+
+static bool g_bRangeHeaders(false);
+
+static bool g_bViewIdForVisCursorInvalidation(false);
+
+static bool g_bLocalRendering(false);
+
+static Compat g_eCompatFlags(Compat::none);
+
+namespace
+{
+
+class LanguageAndLocale
+{
+private:
+ LanguageTag maLanguageTag;
+ LanguageTag maLocaleLanguageTag;
+
+public:
+
+ LanguageAndLocale()
+ : maLanguageTag(LANGUAGE_NONE)
+ , maLocaleLanguageTag(LANGUAGE_NONE)
+ {}
+
+ const LanguageTag& getLanguage() const
+ {
+ return maLanguageTag;
+ }
+
+ void setLanguage(const LanguageTag& rLanguageTag)
+ {
+ if (maLanguageTag != rLanguageTag)
+ {
+ SAL_INFO("comphelper.lok", "Setting language from " << maLanguageTag.getBcp47() << " to " << rLanguageTag.getBcp47());
+ maLanguageTag = rLanguageTag;
+ }
+ }
+
+ const LanguageTag& getLocale() const
+ {
+ return maLocaleLanguageTag;
+ }
+
+ void setLocale(const LanguageTag& rLocaleLanguageTag)
+ {
+ if (maLocaleLanguageTag != rLocaleLanguageTag)
+ {
+ SAL_INFO("comphelper.lok", "Setting locale from " << maLocaleLanguageTag.getBcp47() << " to " << rLocaleLanguageTag.getBcp47());
+ maLocaleLanguageTag = rLocaleLanguageTag;
+ }
+ }
+
+};
+
+}
+
+static LanguageAndLocale g_aLanguageAndLocale;
+
+/// Scaling of the cairo canvas painting for hi-dpi
+static double g_fDPIScale(1.0);
+
+void setActive(bool bActive)
+{
+ g_bActive = bActive;
+}
+
+bool isActive()
+{
+ return g_bActive;
+}
+
+void setPartInInvalidation(bool bPartInInvalidation)
+{
+ g_bPartInInvalidation = bPartInInvalidation;
+}
+
+bool isPartInInvalidation()
+{
+ return g_bPartInInvalidation;
+}
+
+void setTiledPainting(bool bTiledPainting)
+{
+ g_bTiledPainting = bTiledPainting;
+}
+
+bool isTiledPainting()
+{
+ return g_bTiledPainting;
+}
+
+void setDialogPainting(bool bDialogPainting)
+{
+ g_bDialogPainting = bDialogPainting;
+}
+
+bool isDialogPainting()
+{
+ return g_bDialogPainting;
+}
+
+void setDPIScale(double fDPIScale)
+{
+ g_fDPIScale = fDPIScale;
+}
+
+double getDPIScale()
+{
+ return g_fDPIScale;
+}
+
+void setTiledAnnotations(bool bTiledAnnotations)
+{
+ g_bTiledAnnotations = bTiledAnnotations;
+}
+
+bool isTiledAnnotations()
+{
+ return g_bTiledAnnotations;
+}
+
+void setRangeHeaders(bool bRangeHeaders)
+{
+ g_bRangeHeaders = bRangeHeaders;
+}
+
+void setViewIdForVisCursorInvalidation(bool bViewIdForVisCursorInvalidation)
+{
+ g_bViewIdForVisCursorInvalidation = bViewIdForVisCursorInvalidation;
+}
+
+bool isViewIdForVisCursorInvalidation()
+{
+ return g_bViewIdForVisCursorInvalidation;
+}
+
+bool isRangeHeaders()
+{
+ return g_bRangeHeaders;
+}
+
+void setLocalRendering(bool bLocalRendering)
+{
+ g_bLocalRendering = bLocalRendering;
+}
+
+bool isLocalRendering()
+{
+ return g_bLocalRendering;
+}
+
+void setCompatFlag(Compat flag) { g_eCompatFlags = static_cast<Compat>(g_eCompatFlags | flag); }
+
+bool isCompatFlagSet(Compat flag) { return (g_eCompatFlags & flag) == flag; }
+
+void resetCompatFlag() { g_eCompatFlags = Compat::none; }
+
+void setLocale(const LanguageTag& rLanguageTag)
+{
+ g_aLanguageAndLocale.setLocale(rLanguageTag);
+}
+
+const LanguageTag& getLocale()
+{
+ const LanguageTag& rLocale = g_aLanguageAndLocale.getLocale();
+ SAL_INFO_IF(rLocale.getLanguageType() == LANGUAGE_NONE, "comphelper.lok", "Locale not set");
+ return rLocale;
+}
+
+void setLanguageTag(const LanguageTag& rLanguageTag)
+{
+ g_aLanguageAndLocale.setLanguage(rLanguageTag);
+}
+
+const LanguageTag& getLanguageTag()
+{
+ const LanguageTag& rLanguage = g_aLanguageAndLocale.getLanguage();
+ SAL_INFO_IF(rLanguage.getLanguageType() == LANGUAGE_NONE, "comphelper.lok", "Language not set");
+ return rLanguage;
+}
+
+bool isAllowlistedLanguage(const OUString& lang)
+{
+ if (!isActive())
+ return true;
+
+#if defined ANDROID || defined IOS
+ (void) lang;
+ return true;
+#else
+ static const std::vector<OUString> aAllowlist = [] {
+ std::vector<OUString> aList;
+ // coverity[tainted_data] - we trust the contents of this variable
+ const char* pAllowlist = getenv("LOK_ALLOWLIST_LANGUAGES");
+ if (pAllowlist)
+ {
+ std::stringstream stream(pAllowlist);
+ std::string s;
+
+ std::cerr << "Allowlisted languages: ";
+ while (getline(stream, s, ' ')) {
+ if (s.length() == 0)
+ continue;
+
+ std::cerr << s << " ";
+ aList.emplace_back(OStringToOUString(s, RTL_TEXTENCODING_UTF8));
+ }
+ std::cerr << std::endl;
+ }
+
+ if (aList.empty())
+ std::cerr << "No language allowlisted, turning off the language support." << std::endl;
+
+ return aList;
+ }();
+
+ if (aAllowlist.empty())
+ return false;
+
+ for (const auto& entry : aAllowlist)
+ {
+ if (lang.startsWith(entry))
+ return true;
+ if (lang.startsWith(entry.replace('_', '-')))
+ return true;
+ }
+
+ return false;
+#endif
+}
+
+void setTimezone(bool isSet, const OUString& rTimezone)
+{
+ if (isSet)
+ {
+ // Set the given timezone, even if empty.
+ osl_setEnvironment(OUString("TZ").pData, rTimezone.pData);
+ }
+ else
+ {
+ // Unset and empty aren't the same.
+ // When unset, it means default to the system configured timezone.
+ osl_clearEnvironment(OUString("TZ").pData);
+ }
+
+ // Update the timezone data.
+ ::tzset();
+}
+
+static void (*pStatusIndicatorCallback)(void *data, statusIndicatorCallbackType type, int percent, const char* pText)(nullptr);
+static void *pStatusIndicatorCallbackData(nullptr);
+
+void setStatusIndicatorCallback(void (*callback)(void *data, statusIndicatorCallbackType type, int percent, const char* pText), void *data)
+{
+ pStatusIndicatorCallback = callback;
+ pStatusIndicatorCallbackData = data;
+}
+
+void statusIndicatorStart(const OUString& sText)
+{
+ if (pStatusIndicatorCallback)
+ pStatusIndicatorCallback(pStatusIndicatorCallbackData, statusIndicatorCallbackType::Start, 0, sText.toUtf8().getStr());
+}
+
+void statusIndicatorSetValue(int percent)
+{
+ if (pStatusIndicatorCallback)
+ pStatusIndicatorCallback(pStatusIndicatorCallbackData, statusIndicatorCallbackType::SetValue, percent, nullptr);
+}
+
+void statusIndicatorFinish()
+{
+ if (pStatusIndicatorCallback)
+ pStatusIndicatorCallback(pStatusIndicatorCallbackData, statusIndicatorCallbackType::Finish, 0, nullptr);
+}
+
+} // namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/mimeconfighelper.cxx b/comphelper/source/misc/mimeconfighelper.cxx
new file mode 100644
index 0000000000..7f402b6351
--- /dev/null
+++ b/comphelper/source/misc/mimeconfighelper.cxx
@@ -0,0 +1,909 @@
+/* -*- 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 <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/container/XContainerQuery.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/embed/VerbDescriptor.hpp>
+#include <com/sun/star/document/XTypeDetection.hpp>
+
+#include <osl/diagnose.h>
+
+#include <comphelper/fileformat.h>
+#include <comphelper/mimeconfighelper.hxx>
+#include <comphelper/classids.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <comphelper/documentconstants.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <utility>
+
+
+using namespace ::com::sun::star;
+using namespace comphelper;
+
+
+MimeConfigurationHelper::MimeConfigurationHelper( uno::Reference< uno::XComponentContext > xContext )
+: m_xContext(std::move( xContext ))
+{
+ if ( !m_xContext.is() )
+ throw uno::RuntimeException();
+}
+
+
+OUString MimeConfigurationHelper::GetStringClassIDRepresentation( const uno::Sequence< sal_Int8 >& aClassID )
+{
+ OUStringBuffer aResult;
+
+ if ( aClassID.getLength() == 16 )
+ {
+ for ( sal_Int32 nInd = 0; nInd < aClassID.getLength(); nInd++ )
+ {
+ if ( nInd == 4 || nInd == 6 || nInd == 8 || nInd == 10 )
+ aResult.append("-");
+
+ sal_Int32 nDigit1 = static_cast<sal_Int32>( static_cast<sal_uInt8>(aClassID[nInd]) / 16 );
+ sal_Int32 nDigit2 = static_cast<sal_uInt8>(aClassID[nInd]) % 16;
+ aResult.append( OUString::number(nDigit1, 16) + OUString::number( nDigit2, 16 ) );
+ }
+ }
+
+ return aResult.makeStringAndClear();
+}
+
+
+static sal_uInt8 GetDigit_Impl( char aChar )
+{
+ if ( aChar >= '0' && aChar <= '9' )
+ return aChar - '0';
+ else if ( aChar >= 'a' && aChar <= 'f' )
+ return aChar - 'a' + 10;
+ else if ( aChar >= 'A' && aChar <= 'F' )
+ return aChar - 'A' + 10;
+ else
+ return 16;
+}
+
+
+uno::Sequence< sal_Int8 > MimeConfigurationHelper::GetSequenceClassIDRepresentation( std::u16string_view aClassID )
+{
+ size_t nLength = aClassID.size();
+ if ( nLength == 36 )
+ {
+ OString aCharClassID = OUStringToOString( aClassID, RTL_TEXTENCODING_ASCII_US );
+ uno::Sequence< sal_Int8 > aResult( 16 );
+ auto pResult = aResult.getArray();
+
+ size_t nStrPointer = 0;
+ sal_Int32 nSeqInd = 0;
+ while( nSeqInd < 16 && nStrPointer + 1U < nLength )
+ {
+ sal_uInt8 nDigit1 = GetDigit_Impl( aCharClassID[nStrPointer++] );
+ sal_uInt8 nDigit2 = GetDigit_Impl( aCharClassID[nStrPointer++] );
+
+ if ( nDigit1 > 15 || nDigit2 > 15 )
+ break;
+
+ pResult[nSeqInd++] = static_cast<sal_Int8>( nDigit1 * 16 + nDigit2 );
+
+ if ( nStrPointer < nLength && aCharClassID[nStrPointer] == '-' )
+ nStrPointer++;
+ }
+
+ if ( nSeqInd == 16 && nStrPointer == nLength )
+ return aResult;
+ }
+
+ return uno::Sequence< sal_Int8 >();
+}
+
+
+uno::Reference< container::XNameAccess > MimeConfigurationHelper::GetConfigurationByPathImpl( const OUString& aPath )
+{
+ uno::Reference< container::XNameAccess > xConfig;
+
+ try
+ {
+ if ( !m_xConfigProvider.is() )
+ m_xConfigProvider = configuration::theDefaultProvider::get( m_xContext );
+
+ uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
+ {
+ {"nodepath", uno::Any(aPath)}
+ }));
+ xConfig.set( m_xConfigProvider->createInstanceWithArguments(
+ "com.sun.star.configuration.ConfigurationAccess",
+ aArgs ),
+ uno::UNO_QUERY );
+ }
+ catch( uno::Exception& )
+ {}
+
+ return xConfig;
+}
+
+
+uno::Reference< container::XNameAccess > MimeConfigurationHelper::GetObjConfiguration()
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ if ( !m_xObjectConfig.is() )
+ m_xObjectConfig = GetConfigurationByPathImpl(
+ "/org.openoffice.Office.Embedding/Objects" );
+
+ return m_xObjectConfig;
+}
+
+
+uno::Reference< container::XNameAccess > MimeConfigurationHelper::GetVerbsConfiguration()
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ if ( !m_xVerbsConfig.is() )
+ m_xVerbsConfig = GetConfigurationByPathImpl(
+ "/org.openoffice.Office.Embedding/Verbs");
+
+ return m_xVerbsConfig;
+}
+
+
+uno::Reference< container::XNameAccess > MimeConfigurationHelper::GetMediaTypeConfiguration()
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ if ( !m_xMediaTypeConfig.is() )
+ m_xMediaTypeConfig = GetConfigurationByPathImpl(
+ "/org.openoffice.Office.Embedding/MimeTypeClassIDRelations");
+
+ return m_xMediaTypeConfig;
+}
+
+
+uno::Reference< container::XNameAccess > MimeConfigurationHelper::GetFilterFactory()
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ if ( !m_xFilterFactory.is() )
+ m_xFilterFactory.set(
+ m_xContext->getServiceManager()->createInstanceWithContext("com.sun.star.document.FilterFactory", m_xContext),
+ uno::UNO_QUERY );
+
+ return m_xFilterFactory;
+}
+
+
+OUString MimeConfigurationHelper::GetDocServiceNameFromFilter( const OUString& aFilterName )
+{
+ OUString aDocServiceName;
+
+ try
+ {
+ uno::Reference< container::XNameAccess > xFilterFactory(
+ GetFilterFactory(),
+ uno::UNO_SET_THROW );
+
+ uno::Any aFilterAnyData = xFilterFactory->getByName( aFilterName );
+ uno::Sequence< beans::PropertyValue > aFilterData;
+ if ( aFilterAnyData >>= aFilterData )
+ {
+ for ( const auto & prop : std::as_const(aFilterData) )
+ if ( prop.Name == "DocumentService" )
+ prop.Value >>= aDocServiceName;
+ }
+ }
+ catch( uno::Exception& )
+ {}
+
+ return aDocServiceName;
+}
+
+
+OUString MimeConfigurationHelper::GetDocServiceNameFromMediaType( const OUString& aMediaType )
+{
+ uno::Reference< container::XContainerQuery > xTypeCFG(
+ m_xContext->getServiceManager()->createInstanceWithContext("com.sun.star.document.TypeDetection", m_xContext),
+ uno::UNO_QUERY );
+
+ if ( xTypeCFG.is() )
+ {
+ try
+ {
+ // make query for all types matching the properties
+ uno::Sequence < beans::NamedValue > aSeq { { "MediaType", css::uno::Any(aMediaType) } };
+
+ uno::Reference < container::XEnumeration > xEnum = xTypeCFG->createSubSetEnumerationByProperties( aSeq );
+ while ( xEnum->hasMoreElements() )
+ {
+ uno::Sequence< beans::PropertyValue > aType;
+ if ( xEnum->nextElement() >>= aType )
+ {
+ for ( const auto & prop : std::as_const(aType) )
+ {
+ OUString aFilterName;
+ if ( prop.Name == "PreferredFilter"
+ && ( prop.Value >>= aFilterName ) && !aFilterName.isEmpty() )
+ {
+ OUString aDocumentName = GetDocServiceNameFromFilter( aFilterName );
+ if ( !aDocumentName.isEmpty() )
+ return aDocumentName;
+ }
+ }
+ }
+ }
+ }
+ catch( uno::Exception& )
+ {}
+ }
+
+ return OUString();
+}
+
+
+bool MimeConfigurationHelper::GetVerbByShortcut( const OUString& aVerbShortcut,
+ embed::VerbDescriptor& aDescriptor )
+{
+ bool bResult = false;
+
+ uno::Reference< container::XNameAccess > xVerbsConfig = GetVerbsConfiguration();
+ uno::Reference< container::XNameAccess > xVerbsProps;
+ try
+ {
+ if ( xVerbsConfig.is() && ( xVerbsConfig->getByName( aVerbShortcut ) >>= xVerbsProps ) && xVerbsProps.is() )
+ {
+ embed::VerbDescriptor aTempDescr;
+ static constexpr OUStringLiteral sVerbID = u"VerbID";
+ static constexpr OUStringLiteral sVerbUIName = u"VerbUIName";
+ static constexpr OUStringLiteral sVerbFlags = u"VerbFlags";
+ static constexpr OUStringLiteral sVerbAttributes = u"VerbAttributes";
+ if ( ( xVerbsProps->getByName(sVerbID) >>= aTempDescr.VerbID )
+ && ( xVerbsProps->getByName(sVerbUIName) >>= aTempDescr.VerbName )
+ && ( xVerbsProps->getByName(sVerbFlags) >>= aTempDescr.VerbFlags )
+ && ( xVerbsProps->getByName(sVerbAttributes) >>= aTempDescr.VerbAttributes ) )
+ {
+ aDescriptor = aTempDescr;
+ bResult = true;
+ }
+ }
+ }
+ catch( uno::Exception& )
+ {
+ }
+
+ return bResult;
+}
+
+
+uno::Sequence< beans::NamedValue > MimeConfigurationHelper::GetObjPropsFromConfigEntry(
+ const uno::Sequence< sal_Int8 >& aClassID,
+ const uno::Reference< container::XNameAccess >& xObjectProps )
+{
+ uno::Sequence< beans::NamedValue > aResult;
+
+ if ( aClassID.getLength() == 16 )
+ {
+ try
+ {
+ const uno::Sequence< OUString > aObjPropNames = xObjectProps->getElementNames();
+
+ aResult.realloc( aObjPropNames.getLength() + 1 );
+ auto pResult = aResult.getArray();
+ pResult[0].Name = "ClassID";
+ pResult[0].Value <<= aClassID;
+
+ for ( sal_Int32 nInd = 0; nInd < aObjPropNames.getLength(); nInd++ )
+ {
+ pResult[nInd + 1].Name = aObjPropNames[nInd];
+
+ if ( aObjPropNames[nInd] == "ObjectVerbs" )
+ {
+ uno::Sequence< OUString > aVerbShortcuts;
+ if ( !(xObjectProps->getByName( aObjPropNames[nInd] ) >>= aVerbShortcuts) )
+ throw uno::RuntimeException();
+ uno::Sequence< embed::VerbDescriptor > aVerbDescriptors( aVerbShortcuts.getLength() );
+ auto aVerbDescriptorsRange = asNonConstRange(aVerbDescriptors);
+ for ( sal_Int32 nVerbI = 0; nVerbI < aVerbShortcuts.getLength(); nVerbI++ )
+ if ( !GetVerbByShortcut( aVerbShortcuts[nVerbI], aVerbDescriptorsRange[nVerbI] ) )
+ throw uno::RuntimeException();
+
+ pResult[nInd+1].Value <<= aVerbDescriptors;
+ }
+ else
+ pResult[nInd+1].Value = xObjectProps->getByName( aObjPropNames[nInd] );
+ }
+ }
+ catch( uno::Exception& )
+ {
+ aResult.realloc( 0 );
+ }
+ }
+
+ return aResult;
+}
+
+
+OUString MimeConfigurationHelper::GetExplicitlyRegisteredObjClassID( const OUString& aMediaType )
+{
+ OUString aStringClassID;
+
+ uno::Reference< container::XNameAccess > xMediaTypeConfig = GetMediaTypeConfiguration();
+ try
+ {
+ if ( xMediaTypeConfig.is() )
+ xMediaTypeConfig->getByName( aMediaType ) >>= aStringClassID;
+ }
+ catch( uno::Exception& )
+ {
+ }
+
+ return aStringClassID;
+
+}
+
+
+uno::Sequence< beans::NamedValue > MimeConfigurationHelper::GetObjectPropsByStringClassID(
+ const OUString& aStringClassID )
+{
+ uno::Sequence< beans::NamedValue > aObjProps;
+
+ uno::Sequence< sal_Int8 > aClassID = GetSequenceClassIDRepresentation( aStringClassID );
+ if ( ClassIDsEqual( aClassID, GetSequenceClassID( SO3_DUMMY_CLASSID ) ) )
+ {
+ aObjProps = { { "ObjectFactory",
+ uno::Any(OUString("com.sun.star.embed.OOoSpecialEmbeddedObjectFactory")) },
+ { "ClassID", uno::Any(aClassID) } };
+ return aObjProps;
+ }
+
+ if ( aClassID.getLength() == 16 )
+ {
+ uno::Reference< container::XNameAccess > xObjConfig = GetObjConfiguration();
+ uno::Reference< container::XNameAccess > xObjectProps;
+ try
+ {
+ // TODO/LATER: allow to provide ClassID string in any format, only digits are counted
+ if ( xObjConfig.is() && ( xObjConfig->getByName( aStringClassID.toAsciiUpperCase() ) >>= xObjectProps ) && xObjectProps.is() )
+ aObjProps = GetObjPropsFromConfigEntry( aClassID, xObjectProps );
+ }
+ catch( uno::Exception& )
+ {
+ }
+ }
+
+ return aObjProps;
+}
+
+
+uno::Sequence< beans::NamedValue > MimeConfigurationHelper::GetObjectPropsByClassID(
+ const uno::Sequence< sal_Int8 >& aClassID )
+{
+ uno::Sequence< beans::NamedValue > aObjProps;
+ if ( ClassIDsEqual( aClassID, GetSequenceClassID( SO3_DUMMY_CLASSID ) ) )
+ {
+ aObjProps = { { "ObjectFactory",
+ uno::Any(OUString("com.sun.star.embed.OOoSpecialEmbeddedObjectFactory")) },
+ { "ClassID", uno::Any(aClassID) } };
+ }
+
+ OUString aStringClassID = GetStringClassIDRepresentation( aClassID );
+ if ( !aStringClassID.isEmpty() )
+ {
+ uno::Reference< container::XNameAccess > xObjConfig = GetObjConfiguration();
+ uno::Reference< container::XNameAccess > xObjectProps;
+ try
+ {
+ if ( xObjConfig.is() && ( xObjConfig->getByName( aStringClassID.toAsciiUpperCase() ) >>= xObjectProps ) && xObjectProps.is() )
+ aObjProps = GetObjPropsFromConfigEntry( aClassID, xObjectProps );
+ }
+ catch( uno::Exception& )
+ {
+ }
+ }
+
+ return aObjProps;
+}
+
+
+uno::Sequence< beans::NamedValue > MimeConfigurationHelper::GetObjectPropsByMediaType( const OUString& aMediaType )
+{
+ uno::Sequence< beans::NamedValue > aObject =
+ GetObjectPropsByStringClassID( GetExplicitlyRegisteredObjClassID( aMediaType ) );
+ if ( aObject.hasElements() )
+ return aObject;
+
+ OUString aDocumentName = GetDocServiceNameFromMediaType( aMediaType );
+ if ( !aDocumentName.isEmpty() )
+ return GetObjectPropsByDocumentName( aDocumentName );
+
+ return uno::Sequence< beans::NamedValue >();
+}
+
+
+uno::Sequence< beans::NamedValue > MimeConfigurationHelper::GetObjectPropsByFilter( const OUString& aFilterName )
+{
+ OUString aDocumentName = GetDocServiceNameFromFilter( aFilterName );
+ if ( !aDocumentName.isEmpty() )
+ return GetObjectPropsByDocumentName( aDocumentName );
+
+ return uno::Sequence< beans::NamedValue >();
+}
+
+
+uno::Sequence< beans::NamedValue > MimeConfigurationHelper::GetObjectPropsByDocumentName( std::u16string_view aDocName )
+{
+ if ( !aDocName.empty() )
+ {
+ uno::Reference< container::XNameAccess > xObjConfig = GetObjConfiguration();
+ if ( xObjConfig.is() )
+ {
+ try
+ {
+ const uno::Sequence< OUString > aClassIDs = xObjConfig->getElementNames();
+ for ( const OUString & id : aClassIDs )
+ {
+ uno::Reference< container::XNameAccess > xObjectProps;
+ OUString aEntryDocName;
+
+ if ( ( xObjConfig->getByName( id ) >>= xObjectProps ) && xObjectProps.is()
+ && ( xObjectProps->getByName("ObjectDocumentServiceName") >>= aEntryDocName )
+ && aEntryDocName == aDocName )
+ {
+ return GetObjPropsFromConfigEntry( GetSequenceClassIDRepresentation( id ),
+ xObjectProps );
+ }
+ }
+ }
+ catch( uno::Exception& )
+ {}
+ }
+ }
+
+ return uno::Sequence< beans::NamedValue >();
+}
+
+
+OUString MimeConfigurationHelper::GetFactoryNameByClassID( const uno::Sequence< sal_Int8 >& aClassID )
+{
+ return GetFactoryNameByStringClassID( GetStringClassIDRepresentation( aClassID ) );
+}
+
+
+OUString MimeConfigurationHelper::GetFactoryNameByStringClassID( const OUString& aStringClassID )
+{
+ OUString aResult;
+
+ if ( !aStringClassID.isEmpty() )
+ {
+ uno::Reference< container::XNameAccess > xObjConfig = GetObjConfiguration();
+ uno::Reference< container::XNameAccess > xObjectProps;
+ try
+ {
+ if ( xObjConfig.is() && ( xObjConfig->getByName( aStringClassID.toAsciiUpperCase() ) >>= xObjectProps ) && xObjectProps.is() )
+ xObjectProps->getByName("ObjectFactory") >>= aResult;
+ }
+ catch( uno::Exception& )
+ {
+ uno::Sequence< sal_Int8 > aClassID = GetSequenceClassIDRepresentation( aStringClassID );
+ if ( ClassIDsEqual( aClassID, GetSequenceClassID( SO3_DUMMY_CLASSID ) ) )
+ return "com.sun.star.embed.OOoSpecialEmbeddedObjectFactory";
+ }
+ }
+
+ return aResult;
+}
+
+
+OUString MimeConfigurationHelper::GetFactoryNameByDocumentName( std::u16string_view aDocName )
+{
+ OUString aResult;
+
+ if ( !aDocName.empty() )
+ {
+ uno::Reference< container::XNameAccess > xObjConfig = GetObjConfiguration();
+ if ( xObjConfig.is() )
+ {
+ try
+ {
+ const uno::Sequence< OUString > aClassIDs = xObjConfig->getElementNames();
+ for ( const OUString & id : aClassIDs )
+ {
+ uno::Reference< container::XNameAccess > xObjectProps;
+ OUString aEntryDocName;
+
+ if ( ( xObjConfig->getByName( id ) >>= xObjectProps ) && xObjectProps.is()
+ && ( xObjectProps->getByName( "ObjectDocumentServiceName" ) >>= aEntryDocName )
+ && aEntryDocName == aDocName )
+ {
+ xObjectProps->getByName("ObjectFactory") >>= aResult;
+ break;
+ }
+ }
+ }
+ catch( uno::Exception& )
+ {}
+ }
+ }
+
+ return aResult;
+}
+
+
+OUString MimeConfigurationHelper::GetFactoryNameByMediaType( const OUString& aMediaType )
+{
+ OUString aResult = GetFactoryNameByStringClassID( GetExplicitlyRegisteredObjClassID( aMediaType ) );
+
+ if ( aResult.isEmpty() )
+ {
+ OUString aDocumentName = GetDocServiceNameFromMediaType( aMediaType );
+ if ( !aDocumentName.isEmpty() )
+ aResult = GetFactoryNameByDocumentName( aDocumentName );
+ }
+
+ return aResult;
+}
+
+
+OUString MimeConfigurationHelper::UpdateMediaDescriptorWithFilterName(
+ uno::Sequence< beans::PropertyValue >& aMediaDescr,
+ bool bIgnoreType )
+{
+ OUString aFilterName;
+
+ for ( const auto & prop : std::as_const(aMediaDescr) )
+ if ( prop.Name == "FilterName" )
+ prop.Value >>= aFilterName;
+
+ if ( aFilterName.isEmpty() )
+ {
+ // filter name is not specified, so type detection should be done
+
+ uno::Reference< document::XTypeDetection > xTypeDetection(
+ m_xContext->getServiceManager()->createInstanceWithContext("com.sun.star.document.TypeDetection", m_xContext),
+ uno::UNO_QUERY_THROW );
+
+ // typedetection can change the mode, add a stream and so on, thus a copy should be used
+ uno::Sequence< beans::PropertyValue > aTempMD( aMediaDescr );
+
+ // get TypeName
+ OUString aTypeName = xTypeDetection->queryTypeByDescriptor( aTempMD, true );
+
+ // get FilterName
+ for ( const auto & prop : std::as_const(aTempMD) )
+ if ( prop.Name == "FilterName" )
+ prop.Value >>= aFilterName;
+
+ if ( !aFilterName.isEmpty() )
+ {
+ sal_Int32 nOldLen = aMediaDescr.getLength();
+ aMediaDescr.realloc( nOldLen + 1 );
+ auto pMediaDescr = aMediaDescr.getArray();
+ pMediaDescr[nOldLen].Name = "FilterName";
+ pMediaDescr[ nOldLen ].Value <<= aFilterName;
+
+ }
+ else if ( !aTypeName.isEmpty() && !bIgnoreType )
+ {
+ uno::Reference< container::XNameAccess > xNameAccess( xTypeDetection, uno::UNO_QUERY );
+ uno::Sequence< beans::PropertyValue > aTypes;
+
+ if ( xNameAccess.is() && ( xNameAccess->getByName( aTypeName ) >>= aTypes ) )
+ {
+ for ( const auto & prop : std::as_const(aTypes) )
+ {
+ if ( prop.Name == "PreferredFilter" && ( prop.Value >>= aFilterName ) )
+ {
+ sal_Int32 nOldLen = aMediaDescr.getLength();
+ aMediaDescr.realloc( nOldLen + 1 );
+ auto pMediaDescr = aMediaDescr.getArray();
+ pMediaDescr[nOldLen].Name = "FilterName";
+ pMediaDescr[ nOldLen ].Value = prop.Value;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return aFilterName;
+}
+
+OUString MimeConfigurationHelper::UpdateMediaDescriptorWithFilterName(
+ uno::Sequence< beans::PropertyValue >& aMediaDescr,
+ uno::Sequence< beans::NamedValue >& aObject )
+{
+ OUString aDocName;
+ for ( const auto & nv : std::as_const(aObject) )
+ if ( nv.Name == "ObjectDocumentServiceName" )
+ {
+ nv.Value >>= aDocName;
+ break;
+ }
+
+ OSL_ENSURE( !aDocName.isEmpty(), "The name must exist at this point!" );
+
+
+ bool bNeedsAddition = true;
+ for ( sal_Int32 nMedInd = 0; nMedInd < aMediaDescr.getLength(); nMedInd++ )
+ if ( aMediaDescr[nMedInd].Name == "DocumentService" )
+ {
+ aMediaDescr.getArray()[nMedInd].Value <<= aDocName;
+ bNeedsAddition = false;
+ break;
+ }
+
+ if ( bNeedsAddition )
+ {
+ sal_Int32 nOldLen = aMediaDescr.getLength();
+ aMediaDescr.realloc( nOldLen + 1 );
+ auto pMediaDescr = aMediaDescr.getArray();
+ pMediaDescr[nOldLen].Name = "DocumentService";
+ pMediaDescr[nOldLen].Value <<= aDocName;
+ }
+
+ return UpdateMediaDescriptorWithFilterName( aMediaDescr, true );
+}
+
+#ifdef _WIN32
+
+SfxFilterFlags MimeConfigurationHelper::GetFilterFlags( const OUString& aFilterName )
+{
+ SfxFilterFlags nFlags = SfxFilterFlags::NONE;
+ try
+ {
+ if ( !aFilterName.isEmpty() )
+ {
+ uno::Reference< container::XNameAccess > xFilterFactory(
+ GetFilterFactory(),
+ uno::UNO_SET_THROW );
+
+ uno::Any aFilterAny = xFilterFactory->getByName( aFilterName );
+ uno::Sequence< beans::PropertyValue > aData;
+ if ( aFilterAny >>= aData )
+ {
+ SequenceAsHashMap aFilterHM( aData );
+ nFlags = static_cast<SfxFilterFlags>(aFilterHM.getUnpackedValueOrDefault( "Flags", sal_Int32(0) ));
+ }
+ }
+ } catch( uno::Exception& )
+ {}
+
+ return nFlags;
+}
+
+bool MimeConfigurationHelper::AddFilterNameCheckOwnFile(
+ uno::Sequence< beans::PropertyValue >& aMediaDescr )
+{
+ OUString aFilterName = UpdateMediaDescriptorWithFilterName( aMediaDescr, false );
+ if ( !aFilterName.isEmpty() )
+ {
+ SfxFilterFlags nFlags = GetFilterFlags( aFilterName );
+ // check the OWN flag
+ return bool(nFlags & SfxFilterFlags::OWN);
+ }
+
+ return false;
+}
+
+#endif
+
+OUString MimeConfigurationHelper::GetDefaultFilterFromServiceName( const OUString& aServiceName, sal_Int32 nVersion )
+{
+ OUString aResult;
+
+ if ( !aServiceName.isEmpty() && nVersion )
+ try
+ {
+ uno::Reference< container::XContainerQuery > xFilterQuery(
+ GetFilterFactory(),
+ uno::UNO_QUERY_THROW );
+
+ uno::Sequence< beans::NamedValue > aSearchRequest
+ {
+ { "DocumentService", css::uno::Any(aServiceName) },
+ { "FileFormatVersion", css::uno::Any(nVersion) }
+ };
+
+ uno::Reference< container::XEnumeration > xFilterEnum =
+ xFilterQuery->createSubSetEnumerationByProperties( aSearchRequest );
+
+ // use the first filter that is found
+ if ( xFilterEnum.is() )
+ while ( xFilterEnum->hasMoreElements() )
+ {
+ uno::Sequence< beans::PropertyValue > aProps;
+ if ( xFilterEnum->nextElement() >>= aProps )
+ {
+ SfxFilterFlags nFlags = SfxFilterFlags::NONE;
+ OUString sName;
+ for (const auto & rPropVal : aProps)
+ {
+ if (rPropVal.Name == "Flags")
+ {
+ sal_Int32 nTmp(0);
+ if (rPropVal.Value >>= nTmp)
+ nFlags = static_cast<SfxFilterFlags>(nTmp);
+ }
+ else if (rPropVal.Name == "Name")
+ rPropVal.Value >>= sName;
+ }
+
+ // that should be import, export, own filter and not a template filter ( TemplatePath flag )
+ SfxFilterFlags const nRequired = SfxFilterFlags::OWN
+ // fdo#78159 for OOoXML, there is code to convert
+ // to ODF in OCommonEmbeddedObject::store*
+ // so accept it even though there's no export
+ | (SOFFICE_FILEFORMAT_60 == nVersion ? SfxFilterFlags::NONE : SfxFilterFlags::EXPORT)
+ | SfxFilterFlags::IMPORT;
+ if ( ( ( nFlags & nRequired ) == nRequired ) && !( nFlags & SfxFilterFlags::TEMPLATEPATH ) )
+ {
+ // if there are more than one filter the preferred one should be used
+ // if there is no preferred filter the first one will be used
+ if ( aResult.isEmpty() || ( nFlags & SfxFilterFlags::PREFERED ) )
+ aResult = sName;
+ if ( nFlags & SfxFilterFlags::PREFERED )
+ break; // the preferred filter was found
+ }
+ }
+ }
+ }
+ catch( uno::Exception& )
+ {}
+
+ return aResult;
+}
+
+
+OUString MimeConfigurationHelper::GetExportFilterFromImportFilter( const OUString& aImportFilterName )
+{
+ OUString aExportFilterName;
+
+ try
+ {
+ if ( !aImportFilterName.isEmpty() )
+ {
+ uno::Reference< container::XNameAccess > xFilterFactory(
+ GetFilterFactory(),
+ uno::UNO_SET_THROW );
+
+ uno::Any aImpFilterAny = xFilterFactory->getByName( aImportFilterName );
+ uno::Sequence< beans::PropertyValue > aImpData;
+ if ( aImpFilterAny >>= aImpData )
+ {
+ SequenceAsHashMap aImpFilterHM( aImpData );
+ SfxFilterFlags nFlags = static_cast<SfxFilterFlags>(aImpFilterHM.getUnpackedValueOrDefault( "Flags", sal_Int32(0) ));
+
+ if ( !( nFlags & SfxFilterFlags::IMPORT ) )
+ {
+ OSL_FAIL( "This is no import filter!" );
+ throw uno::Exception("this is no import filter", nullptr);
+ }
+
+ if ( nFlags & SfxFilterFlags::EXPORT )
+ {
+ aExportFilterName = aImportFilterName;
+ }
+ else
+ {
+ OUString aDocumentServiceName = aImpFilterHM.getUnpackedValueOrDefault( "DocumentService", OUString() );
+ OUString aTypeName = aImpFilterHM.getUnpackedValueOrDefault( "Type", OUString() );
+
+ OSL_ENSURE( !aDocumentServiceName.isEmpty() && !aTypeName.isEmpty(), "Incomplete filter data!" );
+ if ( !(aDocumentServiceName.isEmpty() || aTypeName.isEmpty()) )
+ {
+ uno::Sequence< beans::NamedValue > aSearchRequest
+ {
+ { "Type", css::uno::Any(aTypeName) },
+ { "DocumentService", css::uno::Any(aDocumentServiceName) }
+ };
+
+ uno::Sequence< beans::PropertyValue > aExportFilterProps = SearchForFilter(
+ uno::Reference< container::XContainerQuery >( xFilterFactory, uno::UNO_QUERY_THROW ),
+ aSearchRequest,
+ SfxFilterFlags::EXPORT,
+ SfxFilterFlags::INTERNAL );
+
+ if ( aExportFilterProps.hasElements() )
+ {
+ SequenceAsHashMap aExpPropsHM( aExportFilterProps );
+ aExportFilterName = aExpPropsHM.getUnpackedValueOrDefault( "Name", OUString() );
+ }
+ }
+ }
+ }
+ }
+ }
+ catch( uno::Exception& )
+ {}
+
+ return aExportFilterName;
+}
+
+
+// static
+uno::Sequence< beans::PropertyValue > MimeConfigurationHelper::SearchForFilter(
+ const uno::Reference< container::XContainerQuery >& xFilterQuery,
+ const uno::Sequence< beans::NamedValue >& aSearchRequest,
+ SfxFilterFlags nMustFlags,
+ SfxFilterFlags nDontFlags )
+{
+ uno::Sequence< beans::PropertyValue > aFilterProps;
+ uno::Reference< container::XEnumeration > xFilterEnum =
+ xFilterQuery->createSubSetEnumerationByProperties( aSearchRequest );
+
+ // the first default filter will be taken,
+ // if there is no filter with flag default the first acceptable filter will be taken
+ if ( xFilterEnum.is() )
+ {
+ while ( xFilterEnum->hasMoreElements() )
+ {
+ uno::Sequence< beans::PropertyValue > aProps;
+ if ( xFilterEnum->nextElement() >>= aProps )
+ {
+ SequenceAsHashMap aPropsHM( aProps );
+ SfxFilterFlags nFlags = static_cast<SfxFilterFlags>(aPropsHM.getUnpackedValueOrDefault("Flags",
+ sal_Int32(0) ));
+ if ( ( ( nFlags & nMustFlags ) == nMustFlags ) && !( nFlags & nDontFlags ) )
+ {
+ if ( ( nFlags & SfxFilterFlags::DEFAULT ) == SfxFilterFlags::DEFAULT )
+ {
+ aFilterProps = aProps;
+ break;
+ }
+ else if ( !aFilterProps.hasElements() )
+ aFilterProps = aProps;
+ }
+ }
+ }
+ }
+
+ return aFilterProps;
+}
+
+
+bool MimeConfigurationHelper::ClassIDsEqual( const uno::Sequence< sal_Int8 >& aClassID1, const uno::Sequence< sal_Int8 >& aClassID2 )
+{
+ return aClassID1 == aClassID2;
+}
+
+
+uno::Sequence< sal_Int8 > MimeConfigurationHelper::GetSequenceClassID( sal_uInt32 n1, sal_uInt16 n2, sal_uInt16 n3,
+ sal_uInt8 b8, sal_uInt8 b9, sal_uInt8 b10, sal_uInt8 b11,
+ sal_uInt8 b12, sal_uInt8 b13, sal_uInt8 b14, sal_uInt8 b15 )
+{
+ uno::Sequence< sal_Int8 > aResult{ /* [ 0] */ static_cast<sal_Int8>( n1 >> 24 ),
+ /* [ 1] */ static_cast<sal_Int8>( ( n1 << 8 ) >> 24 ),
+ /* [ 2] */ static_cast<sal_Int8>( ( n1 << 16 ) >> 24 ),
+ /* [ 3] */ static_cast<sal_Int8>( ( n1 << 24 ) >> 24 ),
+ /* [ 4] */ static_cast<sal_Int8>( n2 >> 8 ),
+ /* [ 5] */ static_cast<sal_Int8>( ( n2 << 8 ) >> 8 ),
+ /* [ 6] */ static_cast<sal_Int8>( n3 >> 8 ),
+ /* [ 7] */ static_cast<sal_Int8>( ( n3 << 8 ) >> 8 ),
+ /* [ 8] */ static_cast<sal_Int8>( b8 ),
+ /* [ 9] */ static_cast<sal_Int8>( b9 ),
+ /* [10] */ static_cast<sal_Int8>( b10 ),
+ /* [11] */ static_cast<sal_Int8>( b11 ),
+ /* [12] */ static_cast<sal_Int8>( b12 ),
+ /* [13] */ static_cast<sal_Int8>( b13 ),
+ /* [14] */ static_cast<sal_Int8>( b14 ),
+ /* [15] */ static_cast<sal_Int8>( b15 ) };
+
+ return aResult;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/namedvaluecollection.cxx b/comphelper/source/misc/namedvaluecollection.cxx
new file mode 100644
index 0000000000..11ef15b308
--- /dev/null
+++ b/comphelper/source/misc/namedvaluecollection.cxx
@@ -0,0 +1,308 @@
+/* -*- 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 <comphelper/namedvaluecollection.hxx>
+
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/beans/PropertyState.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+#include <sal/log.hxx>
+
+#include <algorithm>
+#include <unordered_map>
+
+namespace comphelper
+{
+
+
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::beans::PropertyValue;
+ using ::com::sun::star::beans::NamedValue;
+ using ::com::sun::star::uno::Type;
+ using ::com::sun::star::uno::cpp_acquire;
+ using ::com::sun::star::uno::cpp_release;
+ using ::com::sun::star::uno::cpp_queryInterface;
+ using ::com::sun::star::lang::IllegalArgumentException;
+ using ::com::sun::star::beans::PropertyState_DIRECT_VALUE;
+
+ NamedValueCollection::NamedValueCollection( const Any& _rElements )
+ {
+ impl_assign( _rElements );
+ }
+
+
+ NamedValueCollection::NamedValueCollection( const Sequence< Any >& _rArguments )
+ {
+ impl_assign( _rArguments );
+ }
+
+
+ NamedValueCollection::NamedValueCollection( const Sequence< PropertyValue >& _rArguments )
+ {
+ impl_assign( _rArguments );
+ }
+
+
+ NamedValueCollection::NamedValueCollection( const Sequence< NamedValue >& _rArguments )
+ {
+ impl_assign( _rArguments );
+ }
+
+
+ bool NamedValueCollection::canExtractFrom( css::uno::Any const & i_value )
+ {
+ Type const & aValueType = i_value.getValueType();
+ return aValueType.equals( ::cppu::UnoType< PropertyValue >::get() )
+ || aValueType.equals( ::cppu::UnoType< NamedValue >::get() )
+ || aValueType.equals( ::cppu::UnoType< Sequence< PropertyValue > >::get() )
+ || aValueType.equals( ::cppu::UnoType< Sequence< NamedValue > >::get() );
+ }
+
+
+ NamedValueCollection& NamedValueCollection::merge( const NamedValueCollection& _rAdditionalValues, bool _bOverwriteExisting )
+ {
+ for (auto const& value : _rAdditionalValues.maValues)
+ {
+ if ( _bOverwriteExisting || !impl_has( value.first ) )
+ impl_put( value.first, value.second );
+ }
+
+ return *this;
+ }
+
+
+ size_t NamedValueCollection::size() const
+ {
+ return maValues.size();
+ }
+
+
+ bool NamedValueCollection::empty() const
+ {
+ return maValues.empty();
+ }
+
+
+ std::vector< OUString > NamedValueCollection::getNames() const
+ {
+ std::vector< OUString > aNames;
+ for (auto const& value : maValues)
+ {
+ aNames.push_back( value.first );
+ }
+ return aNames;
+ }
+
+
+ void NamedValueCollection::impl_assign( const Any& i_rWrappedElements )
+ {
+ Sequence< NamedValue > aNamedValues;
+ Sequence< PropertyValue > aPropertyValues;
+ NamedValue aNamedValue;
+ PropertyValue aPropertyValue;
+
+ if ( i_rWrappedElements >>= aNamedValues )
+ impl_assign( aNamedValues );
+ else if ( i_rWrappedElements >>= aPropertyValues )
+ impl_assign( aPropertyValues );
+ else if ( i_rWrappedElements >>= aNamedValue )
+ impl_assign( Sequence< NamedValue >( &aNamedValue, 1 ) );
+ else if ( i_rWrappedElements >>= aPropertyValue )
+ impl_assign( Sequence< PropertyValue >( &aPropertyValue, 1 ) );
+ else
+ SAL_WARN_IF( i_rWrappedElements.hasValue(), "comphelper", "NamedValueCollection::impl_assign(Any): unsupported type!" );
+ }
+
+
+ void NamedValueCollection::impl_assign( const Sequence< Any >& _rArguments )
+ {
+ maValues.clear();
+
+ PropertyValue aPropertyValue;
+ NamedValue aNamedValue;
+
+ for ( auto const & argument : _rArguments )
+ {
+ if ( argument >>= aPropertyValue )
+ maValues[ aPropertyValue.Name ] = aPropertyValue.Value;
+ else if ( argument >>= aNamedValue )
+ maValues[ aNamedValue.Name ] = aNamedValue.Value;
+ else
+ {
+ SAL_WARN_IF(
+ argument.hasValue(), "comphelper",
+ ("NamedValueCollection::impl_assign: encountered a value"
+ " type which I cannot handle: "
+ + argument.getValueTypeName()));
+ }
+ }
+ }
+
+
+ void NamedValueCollection::impl_assign( const Sequence< PropertyValue >& _rArguments )
+ {
+ maValues.clear();
+
+ for ( auto const & argument : _rArguments )
+ maValues[ argument.Name ] = argument.Value;
+ }
+
+
+ void NamedValueCollection::impl_assign( const Sequence< NamedValue >& _rArguments )
+ {
+ maValues.clear();
+
+ for ( auto const & argument : _rArguments )
+ maValues[ argument.Name ] = argument.Value;
+ }
+
+
+ bool NamedValueCollection::get_ensureType( const OUString& _rValueName, void* _pValueLocation, const Type& _rExpectedValueType ) const
+ {
+ auto pos = maValues.find( _rValueName );
+ if ( pos == maValues.end() )
+ // argument does not exist
+ return false;
+
+ if ( uno_type_assignData(
+ _pValueLocation, _rExpectedValueType.getTypeLibType(),
+ const_cast< void* >( pos->second.getValue() ), pos->second.getValueType().getTypeLibType(),
+ reinterpret_cast< uno_QueryInterfaceFunc >( cpp_queryInterface ),
+ reinterpret_cast< uno_AcquireFunc >( cpp_acquire ),
+ reinterpret_cast< uno_ReleaseFunc >( cpp_release )
+ ) )
+ // argument exists, and could be extracted
+ return true;
+
+ // argument exists, but is of wrong type
+ throw IllegalArgumentException(
+ "Invalid value type for '" + _rValueName
+ + "'.\nExpected: " + _rExpectedValueType.getTypeName()
+ + "\nFound: " + pos->second.getValueType().getTypeName(),
+ nullptr, 0 );
+ }
+
+ // static
+ bool NamedValueCollection::get_ensureType( const css::uno::Sequence<css::beans::PropertyValue>& rPropSeq,
+ std::u16string_view _rValueName, void* _pValueLocation, const Type& _rExpectedValueType )
+ {
+ for (const css::beans::PropertyValue& rPropVal : rPropSeq)
+ {
+ if (rPropVal.Name == _rValueName)
+ {
+ if ( uno_type_assignData(
+ _pValueLocation, _rExpectedValueType.getTypeLibType(),
+ const_cast< void* >( rPropVal.Value.getValue() ), rPropVal.Value.getValueType().getTypeLibType(),
+ reinterpret_cast< uno_QueryInterfaceFunc >( cpp_queryInterface ),
+ reinterpret_cast< uno_AcquireFunc >( cpp_acquire ),
+ reinterpret_cast< uno_ReleaseFunc >( cpp_release )
+ ) )
+ // argument exists, and could be extracted
+ return true;
+
+ // argument exists, but is of wrong type
+ throw IllegalArgumentException(
+ OUString::Concat("Invalid value type for '") + _rValueName
+ + "'.\nExpected: " + _rExpectedValueType.getTypeName()
+ + "\nFound: " + rPropVal.Value.getValueType().getTypeName(),
+ nullptr, 0 );
+ }
+ }
+ // argument does not exist
+ return false;
+ }
+
+ // static
+ const css::uno::Any& NamedValueCollection::get( const css::uno::Sequence<css::beans::PropertyValue>& rPropSeq,
+ std::u16string_view _rValueName )
+ {
+ static const Any theEmptyDefault;
+ for (const css::beans::PropertyValue& rPropVal : rPropSeq)
+ {
+ if (rPropVal.Name == _rValueName)
+ {
+ return rPropVal.Value;
+ }
+ }
+ return theEmptyDefault;
+ }
+
+ const Any& NamedValueCollection::impl_get( const OUString& _rValueName ) const
+ {
+ static const Any theEmptyDefault;
+ auto pos = maValues.find( _rValueName );
+ if ( pos != maValues.end() )
+ return pos->second;
+
+ return theEmptyDefault;
+ }
+
+
+ bool NamedValueCollection::impl_has( const OUString& _rValueName ) const
+ {
+ auto pos = maValues.find( _rValueName );
+ return ( pos != maValues.end() );
+ }
+
+
+ bool NamedValueCollection::impl_put( const OUString& _rValueName, const Any& _rValue )
+ {
+ bool bHas = impl_has( _rValueName );
+ maValues[ _rValueName ] = _rValue;
+ return bHas;
+ }
+
+
+ bool NamedValueCollection::impl_remove( const OUString& _rValueName )
+ {
+ auto pos = maValues.find( _rValueName );
+ if ( pos == maValues.end() )
+ return false;
+ maValues.erase( pos );
+ return true;
+ }
+
+
+ sal_Int32 NamedValueCollection::operator >>= ( Sequence< PropertyValue >& _out_rValues ) const
+ {
+ _out_rValues.realloc( maValues.size() );
+ std::transform( maValues.begin(), maValues.end(), _out_rValues.getArray(),
+ [](const std::pair< OUString, css::uno::Any >& _rValue)
+ { return PropertyValue( _rValue.first, 0, _rValue.second, PropertyState_DIRECT_VALUE ); } );
+ return _out_rValues.getLength();
+ }
+
+
+ sal_Int32 NamedValueCollection::operator >>= ( Sequence< NamedValue >& _out_rValues ) const
+ {
+ _out_rValues.realloc( maValues.size() );
+ std::transform( maValues.begin(), maValues.end(), _out_rValues.getArray(),
+ [](const std::pair< OUString, css::uno::Any >& _rValue)
+ { return NamedValue( _rValue.first, _rValue.second ); } );
+ return _out_rValues.getLength();
+ }
+
+
+} // namespace comphelper
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/numberedcollection.cxx b/comphelper/source/misc/numberedcollection.cxx
new file mode 100644
index 0000000000..9dec18e66a
--- /dev/null
+++ b/comphelper/source/misc/numberedcollection.cxx
@@ -0,0 +1,213 @@
+/* -*- 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 <algorithm>
+#include <comphelper/numberedcollection.hxx>
+#include <com/sun/star/frame/UntitledNumbersConst.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+
+namespace comphelper{
+
+constexpr OUString ERRMSG_INVALID_COMPONENT_PARAM = u"NULL as component reference not allowed."_ustr;
+
+
+NumberedCollection::NumberedCollection()
+{
+}
+
+
+NumberedCollection::~NumberedCollection()
+{
+}
+
+
+void NumberedCollection::setOwner(const css::uno::Reference< css::uno::XInterface >& xOwner)
+{
+ // SYNCHRONIZED ->
+ std::scoped_lock aLock(m_aMutex);
+
+ m_xOwner = xOwner;
+
+ // <- SYNCHRONIZED
+}
+
+
+void NumberedCollection::setUntitledPrefix(const OUString& sPrefix)
+{
+ // SYNCHRONIZED ->
+ std::scoped_lock aLock(m_aMutex);
+
+ m_sUntitledPrefix = sPrefix;
+
+ // <- SYNCHRONIZED
+}
+
+
+::sal_Int32 SAL_CALL NumberedCollection::leaseNumber(const css::uno::Reference< css::uno::XInterface >& xComponent)
+{
+ // SYNCHRONIZED ->
+ std::scoped_lock aLock(m_aMutex);
+
+ if ( ! xComponent.is ())
+ throw css::lang::IllegalArgumentException(ERRMSG_INVALID_COMPONENT_PARAM, m_xOwner.get(), 1);
+
+ sal_IntPtr pComponent = reinterpret_cast<sal_IntPtr>( xComponent.get() );
+ TNumberedItemHash::const_iterator pIt = m_lComponents.find (pComponent);
+
+ // a) component already exists - return its number directly
+ if (pIt != m_lComponents.end())
+ return pIt->second.nNumber;
+
+ // b) component must be added new to this container
+
+ // b1) collection is full - no further components possible
+ // -> return INVALID_NUMBER
+ ::sal_Int32 nFreeNumber = impl_searchFreeNumber();
+ if (nFreeNumber == css::frame::UntitledNumbersConst::INVALID_NUMBER)
+ return css::frame::UntitledNumbersConst::INVALID_NUMBER;
+
+ // b2) add component to collection and return its number
+ TNumberedItem aItem;
+ aItem.xItem = css::uno::WeakReference< css::uno::XInterface >(xComponent);
+ aItem.nNumber = nFreeNumber;
+ m_lComponents[pComponent] = aItem;
+
+ return nFreeNumber;
+
+ // <- SYNCHRONIZED
+}
+
+
+void SAL_CALL NumberedCollection::releaseNumber(::sal_Int32 nNumber)
+{
+ // SYNCHRONIZED ->
+ std::scoped_lock aLock(m_aMutex);
+
+ if (nNumber == css::frame::UntitledNumbersConst::INVALID_NUMBER)
+ throw css::lang::IllegalArgumentException ("Special value INVALID_NUMBER not allowed as input parameter.", m_xOwner.get(), 1);
+
+ TDeadItemList lDeadItems;
+ TNumberedItemHash::iterator pComponent;
+
+ for ( pComponent = m_lComponents.begin ();
+ pComponent != m_lComponents.end ();
+ ++pComponent )
+ {
+ const TNumberedItem& rItem = pComponent->second;
+ const css::uno::Reference< css::uno::XInterface > xItem = rItem.xItem.get();
+
+ if ( ! xItem.is ())
+ {
+ lDeadItems.push_back(pComponent->first);
+ continue;
+ }
+
+ if (rItem.nNumber == nNumber)
+ {
+ m_lComponents.erase (pComponent);
+ break;
+ }
+ }
+
+ impl_cleanUpDeadItems(m_lComponents, lDeadItems);
+
+ // <- SYNCHRONIZED
+}
+
+
+void SAL_CALL NumberedCollection::releaseNumberForComponent(const css::uno::Reference< css::uno::XInterface >& xComponent)
+{
+ // SYNCHRONIZED ->
+ std::scoped_lock aLock(m_aMutex);
+
+ if ( ! xComponent.is ())
+ throw css::lang::IllegalArgumentException(ERRMSG_INVALID_COMPONENT_PARAM, m_xOwner.get(), 1);
+
+ sal_IntPtr pComponent = reinterpret_cast<sal_IntPtr>( xComponent.get() );
+ TNumberedItemHash::iterator pIt = m_lComponents.find (pComponent);
+
+ // a) component exists and will be removed
+ if (pIt != m_lComponents.end())
+ m_lComponents.erase(pIt);
+
+ // else
+ // b) component does not exists - nothing todo here (ignore request!)
+
+ // <- SYNCHRONIZED
+}
+
+
+OUString SAL_CALL NumberedCollection::getUntitledPrefix()
+{
+ // SYNCHRONIZED ->
+ std::scoped_lock aLock(m_aMutex);
+
+ return m_sUntitledPrefix;
+
+ // <- SYNCHRONIZED
+}
+
+
+/** create an ordered list of all possible numbers ...
+ e.g. {1,2,3,...,N} Max size of these list will be
+ current size of component list + 1 .
+
+ "+1" ... because in case all numbers in range 1..n
+ are in use we need a new number n+1 :-)
+
+ Every item which is already used as unique number
+ will be removed. At the end a list of e.g. {3,6,...,M}
+ exists where the first item represent the lowest free
+ number (in this example 3).
+ */
+::sal_Int32 NumberedCollection::impl_searchFreeNumber ()
+{
+ // create bitset, where each position represents one possible number.
+ std::vector<bool> aUsedNumbers((m_lComponents.size() * 2) + 1, false);
+
+ for (const auto& rPair : m_lComponents)
+ {
+ // numbers start at 1
+ sal_Int32 pos = rPair.second.nNumber - 1;
+ if (pos >= static_cast<sal_Int32>(aUsedNumbers.size()))
+ aUsedNumbers.resize(pos * 2, false); // should be rare
+ aUsedNumbers[pos] = true;
+ }
+
+ // a) non free numbers ... return INVALID_NUMBER
+ auto it = std::find(aUsedNumbers.begin(), aUsedNumbers.end(), false);
+ if (it == aUsedNumbers.end())
+ return css::frame::UntitledNumbersConst::INVALID_NUMBER;
+
+ // b) return first free number
+ return it - aUsedNumbers.begin() + 1;
+}
+
+void NumberedCollection::impl_cleanUpDeadItems ( TNumberedItemHash& lItems ,
+ const TDeadItemList& lDeadItems)
+{
+ for (const sal_IntPtr& rDeadItem : lDeadItems)
+ {
+ lItems.erase(rDeadItem);
+ }
+}
+
+} // namespace comphelper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/numbers.cxx b/comphelper/source/misc/numbers.cxx
new file mode 100644
index 0000000000..f3b609392c
--- /dev/null
+++ b/comphelper/source/misc/numbers.cxx
@@ -0,0 +1,120 @@
+/* -*- 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 <comphelper/numbers.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <com/sun/star/util/NumberFormat.hpp>
+#include <com/sun/star/util/XNumberFormatter.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+
+
+namespace comphelper
+{
+
+sal_Int16 getNumberFormatType(const css::uno::Reference<css::util::XNumberFormats>& xFormats, sal_Int32 nKey)
+{
+ sal_Int16 nReturn(css::util::NumberFormat::UNDEFINED);
+ if (xFormats.is())
+ {
+ try
+ {
+ css::uno::Reference<css::beans::XPropertySet> xFormat(xFormats->getByKey(nKey));
+ if (xFormat.is())
+ xFormat->getPropertyValue("Type") >>= nReturn;
+ }
+ catch(...)
+ {
+ SAL_WARN("comphelper", "getNumberFormatType : invalid key! (maybe created with another formatter ?)");
+ }
+ }
+ return nReturn;
+}
+
+
+sal_Int16 getNumberFormatType(const css::uno::Reference<css::util::XNumberFormatter>& xFormatter, sal_Int32 nKey)
+{
+ OSL_ENSURE(xFormatter.is(), "getNumberFormatType : the formatter isn't valid !");
+ css::uno::Reference<css::util::XNumberFormatsSupplier> xSupplier( xFormatter->getNumberFormatsSupplier());
+ OSL_ENSURE(xSupplier.is(), "getNumberFormatType : the formatter doesn't implement a supplier !");
+ css::uno::Reference<css::util::XNumberFormats> xFormats( xSupplier->getNumberFormats());
+ return getNumberFormatType(xFormats, nKey);
+}
+
+
+css::uno::Any getNumberFormatDecimals(const css::uno::Reference<css::util::XNumberFormats>& xFormats, sal_Int32 nKey)
+{
+ if (xFormats.is())
+ {
+ try
+ {
+ css::uno::Reference<css::beans::XPropertySet> xFormat( xFormats->getByKey(nKey));
+ if (xFormat.is())
+ {
+ return xFormat->getPropertyValue( "Decimals" );
+ }
+ }
+ catch(...)
+ {
+ SAL_WARN("comphelper", "getNumberFormatDecimals : invalid key! (may be created with another formatter ?)");
+ }
+ }
+ return css::uno::Any(sal_Int16(0));
+}
+
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::beans;
+
+
+Any getNumberFormatProperty( const Reference< XNumberFormatter >& _rxFormatter, sal_Int32 _nKey, const OUString& _rPropertyName )
+{
+ Any aReturn;
+
+ OSL_ENSURE( _rxFormatter.is() && !_rPropertyName.isEmpty(), "getNumberFormatProperty: invalid arguments!" );
+ try
+ {
+ Reference< XNumberFormatsSupplier > xSupplier;
+ Reference< XNumberFormats > xFormats;
+ Reference< XPropertySet > xFormatProperties;
+
+ if ( _rxFormatter.is() )
+ xSupplier = _rxFormatter->getNumberFormatsSupplier();
+ if ( xSupplier.is() )
+ xFormats = xSupplier->getNumberFormats();
+ if ( xFormats.is() )
+ xFormatProperties = xFormats->getByKey( _nKey );
+
+ if ( xFormatProperties.is() )
+ aReturn = xFormatProperties->getPropertyValue( _rPropertyName );
+ }
+ catch( const Exception& )
+ {
+ OSL_FAIL( "::getNumberFormatProperty: caught an exception (did you create the key with another formatter?)!" );
+ }
+
+ return aReturn;
+}
+
+
+} // namespace comphelper
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/officerestartmanager.cxx b/comphelper/source/misc/officerestartmanager.cxx
new file mode 100644
index 0000000000..86eb18f623
--- /dev/null
+++ b/comphelper/source/misc/officerestartmanager.cxx
@@ -0,0 +1,157 @@
+/* -*- 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 <com/sun/star/lang/XMultiComponentFactory.hpp>
+#include <com/sun/star/awt/XRequestCallback.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+
+#include <cppuhelper/supportsservice.hxx>
+#include "officerestartmanager.hxx"
+
+using namespace ::com::sun::star;
+
+namespace comphelper
+{
+
+// XRestartManager
+
+void SAL_CALL OOfficeRestartManager::requestRestart( const uno::Reference< task::XInteractionHandler >& /* xInteractionHandler */ )
+{
+ if ( !m_xContext.is() )
+ throw uno::RuntimeException();
+
+ {
+ std::unique_lock aGuard( m_aMutex );
+
+ // if the restart already running there is no need to trigger it again
+ if ( m_bRestartRequested )
+ return;
+
+ m_bRestartRequested = true;
+
+ // the office is still not initialized, no need to terminate, changing the state is enough
+ if ( !m_bOfficeInitialized )
+ return;
+ }
+
+ // TODO: use InteractionHandler to report errors
+ try
+ {
+ // register itself as a job that should be executed asynchronously
+ uno::Reference< lang::XMultiComponentFactory > xFactory( m_xContext->getServiceManager(), uno::UNO_SET_THROW );
+
+ uno::Reference< awt::XRequestCallback > xRequestCallback(
+ xFactory->createInstanceWithContext(
+ "com.sun.star.awt.AsyncCallback",
+ m_xContext ),
+ uno::UNO_QUERY_THROW );
+
+ xRequestCallback->addCallback( this, uno::Any() );
+ }
+ catch ( uno::Exception& )
+ {
+ // the try to request restart has failed
+ m_bRestartRequested = false;
+ }
+}
+
+
+sal_Bool SAL_CALL OOfficeRestartManager::isRestartRequested( sal_Bool bOfficeInitialized )
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ if ( bOfficeInitialized && !m_bOfficeInitialized )
+ m_bOfficeInitialized = bOfficeInitialized;
+
+ return m_bRestartRequested;
+}
+
+// XCallback
+
+void SAL_CALL OOfficeRestartManager::notify( const uno::Any& /* aData */ )
+{
+ try
+ {
+ bool bSuccess = false;
+
+ if ( m_xContext.is() )
+ {
+ uno::Reference< frame::XDesktop2 > xDesktop = frame::Desktop::create(m_xContext);
+
+ // Turn Quickstarter veto off
+ uno::Reference< beans::XPropertySet > xPropertySet( xDesktop, uno::UNO_QUERY_THROW );
+ OUString aVetoPropName( "SuspendQuickstartVeto" );
+ uno::Any aValue;
+ aValue <<= true;
+ xPropertySet->setPropertyValue( aVetoPropName, aValue );
+
+ try
+ {
+ bSuccess = xDesktop->terminate();
+ } catch( uno::Exception& )
+ {}
+
+ if ( !bSuccess )
+ {
+ aValue <<= false;
+ xPropertySet->setPropertyValue( aVetoPropName, aValue );
+ }
+ }
+
+ if ( !bSuccess )
+ m_bRestartRequested = false;
+ }
+ catch( uno::Exception& )
+ {
+ // the try to restart has failed
+ m_bRestartRequested = false;
+ }
+}
+
+// XServiceInfo
+
+OUString SAL_CALL OOfficeRestartManager::getImplementationName()
+{
+ return "com.sun.star.comp.task.OfficeRestartManager";
+}
+
+sal_Bool SAL_CALL OOfficeRestartManager::supportsService( const OUString& aServiceName )
+{
+ return cppu::supportsService(this, aServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL OOfficeRestartManager::getSupportedServiceNames()
+{
+ return { "com.sun.star.comp.task.OfficeRestartManager" };
+}
+
+} // namespace comphelper
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_task_OfficeRestartManager(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new comphelper::OOfficeRestartManager(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/officerestartmanager.hxx b/comphelper/source/misc/officerestartmanager.hxx
new file mode 100644
index 0000000000..ecf59c5432
--- /dev/null
+++ b/comphelper/source/misc/officerestartmanager.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 <com/sun/star/task/XRestartManager.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/awt/XCallback.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include <mutex>
+#include <cppuhelper/implbase.hxx>
+#include <utility>
+
+namespace comphelper
+{
+
+class OOfficeRestartManager : public ::cppu::WeakImplHelper< css::task::XRestartManager
+ , css::awt::XCallback
+ , css::lang::XServiceInfo >
+{
+ std::mutex m_aMutex;
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+
+ bool m_bOfficeInitialized;
+ bool m_bRestartRequested;
+
+public:
+ explicit OOfficeRestartManager( css::uno::Reference< css::uno::XComponentContext > xContext )
+ : m_xContext(std::move( xContext ))
+ , m_bOfficeInitialized( false )
+ , m_bRestartRequested( false )
+ {}
+
+// XRestartManager
+ virtual void SAL_CALL requestRestart( const css::uno::Reference< css::task::XInteractionHandler >& xInteractionHandler ) override;
+ virtual sal_Bool SAL_CALL isRestartRequested( sal_Bool bInitialized ) override;
+
+// XCallback
+ virtual void SAL_CALL notify( const css::uno::Any& aData ) override;
+
+// XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+};
+
+} // namespace comphelper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/proxyaggregation.cxx b/comphelper/source/misc/proxyaggregation.cxx
new file mode 100644
index 0000000000..db580bea49
--- /dev/null
+++ b/comphelper/source/misc/proxyaggregation.cxx
@@ -0,0 +1,243 @@
+/* -*- 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 <comphelper/proxyaggregation.hxx>
+#include <com/sun/star/reflection/ProxyFactory.hpp>
+
+
+namespace comphelper
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::reflection;
+
+ OProxyAggregation::OProxyAggregation( const Reference< XComponentContext >& _rxContext )
+ :m_xContext( _rxContext )
+ {
+ }
+
+
+ void OProxyAggregation::baseAggregateProxyFor( const Reference< XInterface >& _rxComponent, oslInterlockedCount& _rRefCount,
+ ::cppu::OWeakObject& _rDelegator )
+ {
+ // first a factory for the proxy
+ Reference< XProxyFactory > xFactory = ProxyFactory::create( m_xContext );
+
+ // then the proxy itself
+ { // i36686 OJ: achieve the destruction of the temporary -> otherwise it leads to _rRefCount -= 2
+ m_xProxyAggregate = xFactory->createProxy( _rxComponent );
+ }
+ if ( m_xProxyAggregate.is() )
+ m_xProxyAggregate->queryAggregation( cppu::UnoType<decltype(m_xProxyTypeAccess)>::get() ) >>= m_xProxyTypeAccess;
+
+ // aggregate the proxy
+ osl_atomic_increment( &_rRefCount );
+ if ( m_xProxyAggregate.is() )
+ {
+ // At this point in time, the proxy has a ref count of exactly two - in m_xControlContextProxy,
+ // and in m_xProxyTypeAccess.
+ // Remember to _not_ reset these members unless the delegator of the proxy has been reset, too!
+ m_xProxyAggregate->setDelegator( _rDelegator );
+ }
+ osl_atomic_decrement( &_rRefCount );
+ }
+
+
+ Any SAL_CALL OProxyAggregation::queryAggregation( const Type& _rType )
+ {
+ return m_xProxyAggregate.is() ? m_xProxyAggregate->queryAggregation( _rType ) : Any();
+ }
+
+
+ Sequence< Type > SAL_CALL OProxyAggregation::getTypes( )
+ {
+ Sequence< Type > aTypes;
+ if ( m_xProxyAggregate.is() )
+ {
+ if ( m_xProxyTypeAccess.is() )
+ aTypes = m_xProxyTypeAccess->getTypes();
+ }
+ return aTypes;
+ }
+
+
+ OProxyAggregation::~OProxyAggregation()
+ {
+ if ( m_xProxyAggregate.is() )
+ m_xProxyAggregate->setDelegator( nullptr );
+ m_xProxyAggregate.clear();
+ m_xProxyTypeAccess.clear();
+ // this should remove the _two_only_ "real" references (means not delegated to
+ // ourself) to this proxy, and thus delete it
+ }
+
+ OComponentProxyAggregationHelper::OComponentProxyAggregationHelper( const Reference< XComponentContext >& _rxContext,
+ ::cppu::OBroadcastHelper& _rBHelper )
+ :OProxyAggregation( _rxContext )
+ ,m_rBHelper( _rBHelper )
+ {
+ OSL_ENSURE( _rxContext.is(), "OComponentProxyAggregationHelper::OComponentProxyAggregationHelper: invalid arguments!" );
+ }
+
+
+ void OComponentProxyAggregationHelper::componentAggregateProxyFor(
+ const Reference< XComponent >& _rxComponent, oslInterlockedCount& _rRefCount,
+ ::cppu::OWeakObject& _rDelegator )
+ {
+ OSL_ENSURE( _rxComponent.is(), "OComponentProxyAggregationHelper::componentAggregateProxyFor: invalid inner component!" );
+ m_xInner = _rxComponent;
+
+ // aggregate a proxy for the object
+ baseAggregateProxyFor( m_xInner, _rRefCount, _rDelegator );
+
+ // add as event listener to the inner context, because we want to be notified of disposals
+ osl_atomic_increment( &_rRefCount );
+ {
+ if ( m_xInner.is() )
+ m_xInner->addEventListener( this );
+ }
+ osl_atomic_decrement( &_rRefCount );
+ }
+
+
+ Any SAL_CALL OComponentProxyAggregationHelper::queryInterface( const Type& _rType )
+ {
+ Any aReturn( BASE::queryInterface( _rType ) );
+ if ( !aReturn.hasValue() )
+ aReturn = OProxyAggregation::queryAggregation( _rType );
+ return aReturn;
+ }
+
+
+ IMPLEMENT_FORWARD_XTYPEPROVIDER2( OComponentProxyAggregationHelper, BASE, OProxyAggregation )
+
+
+ OComponentProxyAggregationHelper::~OComponentProxyAggregationHelper( )
+ {
+ OSL_ENSURE( m_rBHelper.bDisposed, "OComponentProxyAggregationHelper::~OComponentProxyAggregationHelper: you should dispose your derived class in the dtor, if necessary!" );
+ // if this asserts, add the following to your derived class dtor:
+
+ // if ( !m_rBHelper.bDisposed )
+ // {
+ // acquire(); // to prevent duplicate dtor calls
+ // dispose();
+ // }
+
+ m_xInner.clear();
+ }
+
+
+ void SAL_CALL OComponentProxyAggregationHelper::disposing( const EventObject& _rSource )
+ {
+ if ( _rSource.Source == m_xInner )
+ { // it's our inner context which is dying -> dispose ourself
+ if ( !m_rBHelper.bDisposed && !m_rBHelper.bInDispose )
+ { // (if necessary only, of course)
+ dispose();
+ }
+ }
+ }
+
+
+ void SAL_CALL OComponentProxyAggregationHelper::dispose()
+ {
+ ::osl::MutexGuard aGuard( m_rBHelper.rMutex );
+
+ // dispose our inner context
+ // before we do this, remove ourself as listener - else in disposing( EventObject ), we
+ // would dispose ourself a second time
+ if ( m_xInner.is() )
+ {
+ m_xInner->removeEventListener( this );
+ m_xInner->dispose();
+ m_xInner.clear();
+ }
+ }
+
+ OComponentProxyAggregation::OComponentProxyAggregation( const Reference< XComponentContext >& _rxContext,
+ const Reference< XComponent >& _rxComponent )
+ :WeakComponentImplHelperBase( m_aMutex )
+ ,OComponentProxyAggregationHelper( _rxContext, rBHelper )
+ {
+ OSL_ENSURE( _rxComponent.is(), "OComponentProxyAggregation::OComponentProxyAggregation: accessible is no XComponent!" );
+ if ( _rxComponent.is() )
+ componentAggregateProxyFor( _rxComponent, m_refCount, *this );
+ }
+
+
+ OComponentProxyAggregation::~OComponentProxyAggregation()
+ {
+ if ( !rBHelper.bDisposed )
+ {
+ acquire(); // to prevent duplicate dtor calls
+ dispose();
+ }
+ }
+
+
+ IMPLEMENT_FORWARD_XINTERFACE2( OComponentProxyAggregation, WeakComponentImplHelperBase, OComponentProxyAggregationHelper )
+
+
+ IMPLEMENT_GET_IMPLEMENTATION_ID( OComponentProxyAggregation )
+
+
+ Sequence< Type > SAL_CALL OComponentProxyAggregation::getTypes( )
+ {
+ return comphelper::concatSequences(
+ OComponentProxyAggregationHelper::getTypes(),
+ // append XComponent, coming from WeakComponentImplHelperBase
+ Sequence { cppu::UnoType<XComponent>::get() });
+ }
+
+
+ void SAL_CALL OComponentProxyAggregation::disposing( const EventObject& _rSource )
+ {
+ // Simply disambiguate---this is necessary for MSVC to distinguish
+ // "disposing(EventObject)" from "disposing()"; but it is also a good
+ // place to check for recursive calls that would be caused by an object
+ // being registered as an XEventListener at itself (cf. rhbz#928568):
+ assert(_rSource.Source != static_cast< cppu::OWeakObject * >(this));
+ OComponentProxyAggregationHelper::disposing( _rSource );
+ }
+
+
+ void SAL_CALL OComponentProxyAggregation::disposing()
+ {
+ // call the dispose-functionality of the base, which will dispose our aggregated component
+ OComponentProxyAggregationHelper::dispose();
+ }
+
+
+ void SAL_CALL OComponentProxyAggregation::dispose()
+ {
+ // simply disambiguate
+ WeakComponentImplHelperBase::dispose();
+ }
+
+
+} // namespace comphelper
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/random.cxx b/comphelper/source/misc/random.cxx
new file mode 100644
index 0000000000..96d466641d
--- /dev/null
+++ b/comphelper/source/misc/random.cxx
@@ -0,0 +1,123 @@
+/* -*- 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/.
+ *
+ * Contributor(s):
+ * Copyright (C) 2012 Tino Kluge <tino.kluge@hrz.tu-chemnitz.de>
+ */
+
+#include <comphelper/random.hxx>
+#include <sal/log.hxx>
+#include <assert.h>
+#include <time.h>
+#include <mutex>
+#include <random>
+#include <stdexcept>
+#if defined HAVE_VALGRIND_HEADERS
+#include <valgrind/memcheck.h>
+#endif
+
+// this is nothing but a simple wrapper around
+// the std::random generators
+
+namespace comphelper::rng
+{
+// underlying random number generator
+// std::mt19937 implements the Mersenne twister algorithm which
+// is fast and has good statistical properties, it produces integers
+// in the range of [0, 2^32-1] internally
+// memory requirement: 625*sizeof(uint32_t)
+// http://en.wikipedia.org/wiki/Mersenne_twister
+#define STD_RNG_ALGO std::mt19937
+
+namespace
+{
+struct RandomNumberGenerator
+{
+ std::mutex mutex;
+ STD_RNG_ALGO global_rng;
+ RandomNumberGenerator()
+ {
+ // make RR easier to use, breaks easily without the RNG being repeatable
+ bool bRepeatable = (getenv("SAL_RAND_REPEATABLE") != nullptr) || (getenv("RR") != nullptr);
+ // valgrind on some platforms (e.g.Ubuntu16.04) does not support the new Intel RDRAND instructions,
+ // which leads to "Illegal Opcode" errors, so just turn off randomness.
+#if defined HAVE_VALGRIND_HEADERS
+ if (RUNNING_ON_VALGRIND)
+ bRepeatable = true;
+#endif
+ if (bRepeatable)
+ {
+ global_rng.seed(42);
+ return;
+ }
+
+ try
+ {
+ std::random_device rd;
+ // initialises the state of the global random number generator
+ // should only be called once.
+ // (note, a few std::variate_generator<> (like normal) have their
+ // own state which would need a reset as well to guarantee identical
+ // sequence of numbers, e.g. via myrand.distribution().reset())
+ global_rng.seed(rd() ^ time(nullptr));
+ }
+ catch (std::runtime_error& e)
+ {
+ SAL_WARN("comphelper", "Using std::random_device failed: " << e.what());
+ global_rng.seed(time(nullptr));
+ }
+ }
+};
+
+RandomNumberGenerator& GetTheRandomNumberGenerator()
+{
+ static RandomNumberGenerator RANDOM;
+ return RANDOM;
+}
+}
+
+// uniform ints [a,b] distribution
+int uniform_int_distribution(int a, int b)
+{
+ std::uniform_int_distribution<int> dist(a, b);
+ auto& gen = GetTheRandomNumberGenerator();
+ std::scoped_lock<std::mutex> g(gen.mutex);
+ return dist(gen.global_rng);
+}
+
+// uniform ints [a,b] distribution
+unsigned int uniform_uint_distribution(unsigned int a, unsigned int b)
+{
+ std::uniform_int_distribution<unsigned int> dist(a, b);
+ auto& gen = GetTheRandomNumberGenerator();
+ std::scoped_lock<std::mutex> g(gen.mutex);
+ return dist(gen.global_rng);
+}
+
+// uniform size_t [a,b] distribution
+size_t uniform_size_distribution(size_t a, size_t b)
+{
+ std::uniform_int_distribution<size_t> dist(a, b);
+ auto& gen = GetTheRandomNumberGenerator();
+ std::scoped_lock<std::mutex> g(gen.mutex);
+ return dist(gen.global_rng);
+}
+
+// uniform size_t [a,b) distribution
+double uniform_real_distribution(double a, double b)
+{
+ assert(a < b);
+ std::uniform_real_distribution<double> dist(a, b);
+ auto& gen = GetTheRandomNumberGenerator();
+ std::scoped_lock<std::mutex> g(gen.mutex);
+ return dist(gen.global_rng);
+}
+
+} // namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/sequenceashashmap.cxx b/comphelper/source/misc/sequenceashashmap.cxx
new file mode 100644
index 0000000000..270c005db2
--- /dev/null
+++ b/comphelper/source/misc/sequenceashashmap.cxx
@@ -0,0 +1,400 @@
+/* -*- 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 <boost/property_tree/json_parser.hpp>
+
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/reflection/XIdlField.hpp>
+#include <com/sun/star/reflection/theCoreReflection.hpp>
+#include <comphelper/sequenceashashmap.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <sal/log.hxx>
+#include <o3tl/string_view.hxx>
+#include <comphelper/sequence.hxx>
+
+using namespace com::sun::star;
+
+namespace
+{
+uno::Any jsonToUnoAny(const boost::property_tree::ptree& aTree)
+{
+ uno::Any aAny;
+ uno::Any aValue;
+ sal_Int32 nFields;
+ uno::Reference<reflection::XIdlField> aField;
+ boost::property_tree::ptree aNodeNull, aNodeValue, aNodeField;
+ const std::string& rType = aTree.get<std::string>("type", "");
+ const std::string& rValue = aTree.get<std::string>("value", "");
+ uno::Sequence<uno::Reference<reflection::XIdlField>> aFields;
+ uno::Reference<reflection::XIdlClass> xIdlClass
+ = css::reflection::theCoreReflection::get(comphelper::getProcessComponentContext())
+ ->forName(OUString::fromUtf8(rType));
+ if (xIdlClass.is())
+ {
+ uno::TypeClass aTypeClass = xIdlClass->getTypeClass();
+ xIdlClass->createObject(aAny);
+ aFields = xIdlClass->getFields();
+ nFields = aFields.getLength();
+ aNodeValue = aTree.get_child("value", aNodeNull);
+ if (nFields > 0 && aNodeValue != aNodeNull)
+ {
+ for (sal_Int32 itField = 0; itField < nFields; ++itField)
+ {
+ aField = aFields[itField];
+ aNodeField = aNodeValue.get_child(aField->getName().toUtf8().getStr(), aNodeNull);
+ if (aNodeField != aNodeNull)
+ {
+ aValue = jsonToUnoAny(aNodeField);
+ aField->set(aAny, aValue);
+ }
+ }
+ }
+ else if (!rValue.empty())
+ {
+ if (aTypeClass == uno::TypeClass_VOID)
+ aAny.clear();
+ else if (aTypeClass == uno::TypeClass_BYTE)
+ aAny <<= static_cast<sal_Int8>(o3tl::toInt32(rValue));
+ else if (aTypeClass == uno::TypeClass_BOOLEAN)
+ aAny <<= OString(rValue).toBoolean();
+ else if (aTypeClass == uno::TypeClass_SHORT)
+ aAny <<= static_cast<sal_Int16>(o3tl::toInt32(rValue));
+ else if (aTypeClass == uno::TypeClass_UNSIGNED_SHORT)
+ aAny <<= static_cast<sal_uInt16>(o3tl::toUInt32(rValue));
+ else if (aTypeClass == uno::TypeClass_LONG)
+ aAny <<= o3tl::toInt32(rValue);
+ else if (aTypeClass == uno::TypeClass_UNSIGNED_LONG)
+ aAny <<= static_cast<sal_uInt32>(o3tl::toInt32(rValue));
+ else if (aTypeClass == uno::TypeClass_FLOAT)
+ aAny <<= OString(rValue).toFloat();
+ else if (aTypeClass == uno::TypeClass_DOUBLE)
+ aAny <<= o3tl::toDouble(rValue);
+ else if (aTypeClass == uno::TypeClass_STRING)
+ aAny <<= OUString::fromUtf8(rValue);
+ }
+ }
+ return aAny;
+}
+}
+
+namespace comphelper{
+
+SequenceAsHashMap::SequenceAsHashMap()
+{
+}
+
+SequenceAsHashMap::SequenceAsHashMap(const css::uno::Any& aSource)
+{
+ (*this) << aSource;
+}
+
+
+SequenceAsHashMap::SequenceAsHashMap(const css::uno::Sequence< css::uno::Any >& lSource)
+{
+ (*this) << lSource;
+}
+
+SequenceAsHashMap::SequenceAsHashMap(const css::uno::Sequence< css::beans::PropertyValue >& lSource)
+{
+ (*this) << lSource;
+}
+
+SequenceAsHashMap::SequenceAsHashMap(const css::uno::Sequence< css::beans::NamedValue >& lSource)
+{
+ (*this) << lSource;
+}
+
+void SequenceAsHashMap::operator<<(const css::uno::Any& aSource)
+{
+ // An empty Any reset this instance!
+ if (!aSource.hasValue())
+ {
+ clear();
+ return;
+ }
+
+ css::uno::Sequence< css::beans::NamedValue > lN;
+ if (aSource >>= lN)
+ {
+ (*this) << lN;
+ return;
+ }
+
+ css::uno::Sequence< css::beans::PropertyValue > lP;
+ if (aSource >>= lP)
+ {
+ (*this) << lP;
+ return;
+ }
+
+ throw css::lang::IllegalArgumentException(
+ "Any contains wrong type.", css::uno::Reference<css::uno::XInterface>(),
+ -1);
+}
+
+
+void SequenceAsHashMap::operator<<(const css::uno::Sequence< css::uno::Any >& lSource)
+{
+ sal_Int32 c = lSource.getLength();
+ sal_Int32 i = 0;
+
+ m_aMap.reserve(c);
+ for (i=0; i<c; ++i)
+ {
+ css::beans::PropertyValue lP;
+ if (lSource[i] >>= lP)
+ {
+ if (
+ (lP.Name.isEmpty()) ||
+ (!lP.Value.hasValue())
+ )
+ throw css::lang::IllegalArgumentException(
+ "PropertyValue struct contains no useful information.",
+ css::uno::Reference<css::uno::XInterface>(), -1);
+ (*this)[lP.Name] = lP.Value;
+ continue;
+ }
+
+ css::beans::NamedValue lN;
+ if (lSource[i] >>= lN)
+ {
+ if (
+ (lN.Name.isEmpty()) ||
+ (!lN.Value.hasValue())
+ )
+ throw css::lang::IllegalArgumentException(
+ "NamedValue struct contains no useful information.",
+ css::uno::Reference<css::uno::XInterface>(), -1);
+ (*this)[lN.Name] = lN.Value;
+ continue;
+ }
+
+ // ignore VOID Any ... but reject wrong filled ones!
+ if (lSource[i].hasValue())
+ throw css::lang::IllegalArgumentException(
+ "Any contains wrong type.",
+ css::uno::Reference<css::uno::XInterface>(), -1);
+ }
+}
+
+void SequenceAsHashMap::operator<<(const css::uno::Sequence< css::beans::PropertyValue >& lSource)
+{
+ clear();
+
+ sal_Int32 c = lSource.getLength();
+ const css::beans::PropertyValue* pSource = lSource.getConstArray();
+
+ m_aMap.reserve(c);
+ for (sal_Int32 i=0; i<c; ++i)
+ (*this)[pSource[i].Name] = pSource[i].Value;
+}
+
+void SequenceAsHashMap::operator<<(const css::uno::Sequence< css::beans::NamedValue >& lSource)
+{
+ clear();
+
+ sal_Int32 c = lSource.getLength();
+ const css::beans::NamedValue* pSource = lSource.getConstArray();
+
+ m_aMap.reserve(c);
+ for (sal_Int32 i=0; i<c; ++i)
+ (*this)[pSource[i].Name] = pSource[i].Value;
+}
+
+void SequenceAsHashMap::operator>>(css::uno::Sequence< css::beans::PropertyValue >& lDestination) const
+{
+ sal_Int32 c = static_cast<sal_Int32>(size());
+ lDestination.realloc(c);
+ css::beans::PropertyValue* pDestination = lDestination.getArray();
+
+ sal_Int32 i = 0;
+ for (const_iterator pThis = begin();
+ pThis != end() ;
+ ++pThis )
+ {
+ pDestination[i].Name = pThis->first.maString;
+ pDestination[i].Value = pThis->second;
+ ++i;
+ }
+}
+
+void SequenceAsHashMap::operator>>(css::uno::Sequence< css::beans::NamedValue >& lDestination) const
+{
+ sal_Int32 c = static_cast<sal_Int32>(size());
+ lDestination.realloc(c);
+ css::beans::NamedValue* pDestination = lDestination.getArray();
+
+ sal_Int32 i = 0;
+ for (const_iterator pThis = begin();
+ pThis != end() ;
+ ++pThis )
+ {
+ pDestination[i].Name = pThis->first.maString;
+ pDestination[i].Value = pThis->second;
+ ++i;
+ }
+}
+
+css::uno::Any SequenceAsHashMap::getAsConstAny(bool bAsPropertyValueList) const
+{
+ css::uno::Any aDestination;
+ if (bAsPropertyValueList)
+ aDestination <<= getAsConstPropertyValueList();
+ else
+ aDestination <<= getAsConstNamedValueList();
+ return aDestination;
+}
+
+css::uno::Sequence< css::beans::NamedValue > SequenceAsHashMap::getAsConstNamedValueList() const
+{
+ css::uno::Sequence< css::beans::NamedValue > lReturn;
+ (*this) >> lReturn;
+ return lReturn;
+}
+
+css::uno::Sequence< css::beans::PropertyValue > SequenceAsHashMap::getAsConstPropertyValueList() const
+{
+ css::uno::Sequence< css::beans::PropertyValue > lReturn;
+ (*this) >> lReturn;
+ return lReturn;
+}
+
+bool SequenceAsHashMap::match(const SequenceAsHashMap& rCheck) const
+{
+ for (auto const& elem : rCheck)
+ {
+ const OUString& sCheckName = elem.first.maString;
+ const css::uno::Any& aCheckValue = elem.second;
+ const_iterator pFound = find(sCheckName);
+
+ if (pFound == end())
+ return false;
+
+ const css::uno::Any& aFoundValue = pFound->second;
+ if (aFoundValue != aCheckValue)
+ return false;
+ }
+
+ return true;
+}
+
+void SequenceAsHashMap::update(const SequenceAsHashMap& rUpdate)
+{
+ m_aMap.reserve(std::max(size(), rUpdate.size()));
+ for (auto const& elem : rUpdate.m_aMap)
+ {
+ m_aMap[elem.first] = elem.second;
+ }
+}
+
+std::vector<css::beans::PropertyValue> JsonToPropertyValues(const OString& rJson)
+{
+ std::vector<beans::PropertyValue> aArguments;
+ boost::property_tree::ptree aTree, aNodeNull, aNodeValue;
+ std::stringstream aStream((std::string(rJson)));
+ boost::property_tree::read_json(aStream, aTree);
+
+ for (const auto& rPair : aTree)
+ {
+ const std::string& rType = rPair.second.get<std::string>("type", "");
+ const std::string& rValue = rPair.second.get<std::string>("value", "");
+
+ beans::PropertyValue aValue;
+ aValue.Name = OUString::fromUtf8(rPair.first);
+ if (rType == "string")
+ aValue.Value <<= OUString::fromUtf8(rValue);
+ else if (rType == "boolean")
+ aValue.Value <<= OString(rValue).toBoolean();
+ else if (rType == "float")
+ aValue.Value <<= OString(rValue).toFloat();
+ else if (rType == "long")
+ aValue.Value <<= o3tl::toInt32(rValue);
+ else if (rType == "short")
+ aValue.Value <<= sal_Int16(o3tl::toInt32(rValue));
+ else if (rType == "unsigned short")
+ aValue.Value <<= sal_uInt16(o3tl::toUInt32(rValue));
+ else if (rType == "int64")
+ aValue.Value <<= o3tl::toInt64(rValue);
+ else if (rType == "int32")
+ aValue.Value <<= o3tl::toInt32(rValue);
+ else if (rType == "int16")
+ aValue.Value <<= sal_Int16(o3tl::toInt32(rValue));
+ else if (rType == "uint64")
+ aValue.Value <<= OString(rValue).toUInt64();
+ else if (rType == "uint32")
+ aValue.Value <<= o3tl::toUInt32(rValue);
+ else if (rType == "uint16")
+ aValue.Value <<= sal_uInt16(o3tl::toUInt32(rValue));
+ else if (rType == "[]byte")
+ {
+ aNodeValue = rPair.second.get_child("value", aNodeNull);
+ if (aNodeValue != aNodeNull && aNodeValue.size() == 0)
+ {
+ uno::Sequence<sal_Int8> aSeqByte(reinterpret_cast<const sal_Int8*>(rValue.c_str()),
+ rValue.size());
+ aValue.Value <<= aSeqByte;
+ }
+ }
+ else if (rType == "[]any")
+ {
+ aNodeValue = rPair.second.get_child("value", aNodeNull);
+ if (aNodeValue != aNodeNull && !aNodeValue.empty())
+ {
+ uno::Sequence<uno::Any> aSeq(aNodeValue.size());
+ std::transform(aNodeValue.begin(), aNodeValue.end(), aSeq.getArray(),
+ [](const auto& rSeqPair) { return jsonToUnoAny(rSeqPair.second); });
+ aValue.Value <<= aSeq;
+ }
+ }
+ else if (rType == "[]com.sun.star.beans.PropertyValue")
+ {
+ aNodeValue = rPair.second.get_child("value", aNodeNull);
+ std::stringstream s;
+ boost::property_tree::write_json(s, aNodeValue);
+ std::vector<beans::PropertyValue> aPropertyValues = JsonToPropertyValues(OString(s.str()));
+ aValue.Value <<= comphelper::containerToSequence(aPropertyValues);
+ }
+ else if (rType == "[][]com.sun.star.beans.PropertyValue")
+ {
+ aNodeValue = rPair.second.get_child("value", aNodeNull);
+ std::vector<uno::Sequence<beans::PropertyValue>> aSeqs;
+ for (const auto& rItem : aNodeValue)
+ {
+ std::stringstream s;
+ boost::property_tree::write_json(s, rItem.second);
+ std::vector<beans::PropertyValue> aPropertyValues = JsonToPropertyValues(OString(s.str()));
+ aSeqs.push_back(comphelper::containerToSequence(aPropertyValues));
+ }
+ aValue.Value <<= comphelper::containerToSequence(aSeqs);
+ }
+ else
+ SAL_WARN("comphelper", "JsonToPropertyValues: unhandled type '" << rType << "'");
+ aArguments.push_back(aValue);
+ }
+ return aArguments;
+}
+
+} // namespace comphelper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/sharedmutex.cxx b/comphelper/source/misc/sharedmutex.cxx
new file mode 100644
index 0000000000..58ae35df59
--- /dev/null
+++ b/comphelper/source/misc/sharedmutex.cxx
@@ -0,0 +1,42 @@
+/* -*- 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 <comphelper/sharedmutex.hxx>
+#include <comphelper/refcountedmutex.hxx>
+
+
+namespace comphelper
+{
+
+ SharedMutex::SharedMutex()
+ :m_pMutexImpl( std::make_shared<::osl::Mutex >())
+ {
+ }
+
+
+ RefCountedMutex::~RefCountedMutex()
+ {
+ }
+
+
+} // namespace comphelper
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/simplefileaccessinteraction.cxx b/comphelper/source/misc/simplefileaccessinteraction.cxx
new file mode 100644
index 0000000000..8cd77af7d6
--- /dev/null
+++ b/comphelper/source/misc/simplefileaccessinteraction.cxx
@@ -0,0 +1,120 @@
+/* -*- 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 <comphelper/simplefileaccessinteraction.hxx>
+#include <com/sun/star/task/XInteractionAbort.hpp>
+#include <com/sun/star/task/XInteractionApprove.hpp>
+#include <com/sun/star/ucb/AuthenticationRequest.hpp>
+#include <com/sun/star/ucb/CertificateValidationRequest.hpp>
+#include <com/sun/star/ucb/InteractiveIOException.hpp>
+#include <com/sun/star/ucb/InteractiveNetworkException.hpp>
+#include <com/sun/star/ucb/UnsupportedDataSinkException.hpp>
+
+namespace comphelper
+{
+/// Will handle com::sun::star::ucb::InteractiveIOException and derived classes
+const sal_Int32 HANDLE_INTERACTIVEIOEXCEPTION = 0;
+/// Will handle com::sun::star::ucb::UnsupportedDataSinkException
+const sal_Int32 HANDLE_UNSUPPORTEDDATASINKEXCEPTION = 1;
+/// Will handle com::sun::star::ucb::InteractiveNetworkException
+const sal_Int32 HANDLE_INTERACTIVENETWORKEXCEPTION = 2;
+/// Will handle com::sun::star::ucb::CertificateValidationRequest
+const sal_Int32 HANDLE_CERTIFICATEREQUEST = 3;
+/// Will handle com::sun::star::ucb::AuthenticationRequest
+const sal_Int32 HANDLE_AUTHENTICATIONREQUEST = 4;
+
+SimpleFileAccessInteraction::SimpleFileAccessInteraction(
+ const css::uno::Reference<css::task::XInteractionHandler>& xHandler)
+{
+ std::vector<::ucbhelper::InterceptedInteraction::InterceptedRequest> lInterceptions{
+ { //intercept standard IO error exception (local file and WebDAV)
+ css::uno::Any(css::ucb::InteractiveIOException()),
+ cppu::UnoType<css::task::XInteractionAbort>::get(), HANDLE_INTERACTIVEIOEXCEPTION },
+ { //intercept internal error
+ css::uno::Any(css::ucb::UnsupportedDataSinkException()),
+ cppu::UnoType<css::task::XInteractionAbort>::get(), HANDLE_UNSUPPORTEDDATASINKEXCEPTION },
+ {
+ //intercept network error exception (WebDAV ucp provider)
+ css::uno::Any(css::ucb::InteractiveNetworkException()),
+ cppu::UnoType<css::task::XInteractionAbort>::get(),
+ HANDLE_INTERACTIVENETWORKEXCEPTION,
+ },
+ { //intercept certificate validation request (WebDAV ucp provider)
+ css::uno::Any(css::ucb::CertificateValidationRequest()),
+ cppu::UnoType<css::task::XInteractionAbort>::get(), HANDLE_CERTIFICATEREQUEST },
+ { //intercept authentication request (WebDAV ucp provider)
+ css::uno::Any(css::ucb::AuthenticationRequest()),
+ cppu::UnoType<css::task::XInteractionApprove>::get(), HANDLE_AUTHENTICATIONREQUEST }
+ };
+
+ setInterceptedHandler(xHandler);
+ setInterceptions(std::move(lInterceptions));
+}
+
+SimpleFileAccessInteraction::~SimpleFileAccessInteraction() {}
+
+ucbhelper::InterceptedInteraction::EInterceptionState SimpleFileAccessInteraction::intercepted(
+ const ::ucbhelper::InterceptedInteraction::InterceptedRequest& aRequest,
+ const css::uno::Reference<css::task::XInteractionRequest>& xRequest)
+{
+ bool bAbort = false;
+ switch (aRequest.Handle)
+ {
+ case HANDLE_UNSUPPORTEDDATASINKEXCEPTION:
+ case HANDLE_INTERACTIVENETWORKEXCEPTION:
+ case HANDLE_INTERACTIVEIOEXCEPTION:
+ {
+ bAbort = true;
+ }
+ break;
+
+ case HANDLE_CERTIFICATEREQUEST:
+ {
+ // use default internal handler.
+ if (m_xInterceptedHandler.is())
+ {
+ m_xInterceptedHandler->handle(xRequest);
+ return ::ucbhelper::InterceptedInteraction::E_INTERCEPTED;
+ }
+ else
+ bAbort = true;
+ break;
+ }
+
+ case HANDLE_AUTHENTICATIONREQUEST:
+ {
+ // use default internal handler.
+ if (m_xInterceptedHandler.is())
+ {
+ m_xInterceptedHandler->handle(xRequest);
+ return ::ucbhelper::InterceptedInteraction::E_INTERCEPTED;
+ }
+ else //simply abort
+ bAbort = true;
+ }
+ break;
+ }
+
+ // handle interaction by ourself, by not doing
+ // any selection...
+ if (bAbort)
+ {
+ css::uno::Reference<css::task::XInteractionContinuation> xAbort
+ = ::ucbhelper::InterceptedInteraction::extractContinuation(
+ xRequest->getContinuations(), cppu::UnoType<css::task::XInteractionAbort>::get());
+ if (!xAbort.is())
+ return ::ucbhelper::InterceptedInteraction::E_NO_CONTINUATION_FOUND;
+ return ::ucbhelper::InterceptedInteraction::E_INTERCEPTED;
+ }
+
+ return ::ucbhelper::InterceptedInteraction::E_INTERCEPTED;
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/solarmutex.cxx b/comphelper/source/misc/solarmutex.cxx
new file mode 100644
index 0000000000..3e45ae1458
--- /dev/null
+++ b/comphelper/source/misc/solarmutex.cxx
@@ -0,0 +1,102 @@
+/* -*- 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 <comphelper/solarmutex.hxx>
+#include <osl/thread.hxx>
+
+#include <assert.h>
+#include <cstdlib>
+
+namespace comphelper {
+
+namespace {
+ SolarMutex* g_pSolarMutex = nullptr;
+}
+
+SolarMutex *SolarMutex::get()
+{
+ return g_pSolarMutex;
+}
+
+SolarMutex::SolarMutex()
+ : m_nCount( 0 )
+ , m_aBeforeReleaseHandler( nullptr )
+{
+ assert(!g_pSolarMutex);
+ g_pSolarMutex = this;
+}
+
+SolarMutex::~SolarMutex()
+{
+ g_pSolarMutex = nullptr;
+}
+
+void SolarMutex::doAcquire( const sal_uInt32 nLockCount )
+{
+ for ( sal_uInt32 n = nLockCount; n ; --n )
+ m_aMutex.acquire();
+ m_nThreadId = std::this_thread::get_id();
+ m_nCount += nLockCount;
+}
+
+sal_uInt32 SolarMutex::doRelease( bool bUnlockAll )
+{
+ if ( !IsCurrentThread() )
+ std::abort();
+ if ( m_nCount == 0 )
+ std::abort();
+
+ const sal_uInt32 nCount = bUnlockAll ? m_nCount : 1;
+ m_nCount -= nCount;
+
+ if ( 0 == m_nCount )
+ {
+ if ( m_aBeforeReleaseHandler )
+ m_aBeforeReleaseHandler();
+ m_nThreadId = std::thread::id();
+ }
+
+ for ( sal_uInt32 n = nCount ; n ; --n )
+ m_aMutex.release();
+
+ return nCount;
+}
+
+bool SolarMutex::IsCurrentThread() const
+{
+ return m_nThreadId == std::this_thread::get_id();
+}
+
+bool SolarMutex::tryToAcquire()
+{
+ if ( m_aMutex.tryToAcquire() )
+ {
+ m_nThreadId = std::this_thread::get_id();
+ m_nCount++;
+ return true;
+ }
+ else
+ return false;
+}
+
+} // namespace comphelper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/stillreadwriteinteraction.cxx b/comphelper/source/misc/stillreadwriteinteraction.cxx
new file mode 100644
index 0000000000..88bc25bc46
--- /dev/null
+++ b/comphelper/source/misc/stillreadwriteinteraction.cxx
@@ -0,0 +1,156 @@
+/* -*- 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 <comphelper/stillreadwriteinteraction.hxx>
+
+#include <com/sun/star/ucb/InteractiveIOException.hpp>
+
+#include <com/sun/star/task/XInteractionAbort.hpp>
+
+#include <com/sun/star/task/XInteractionApprove.hpp>
+
+#include <com/sun/star/ucb/UnsupportedDataSinkException.hpp>
+
+#include <com/sun/star/ucb/AuthenticationRequest.hpp>
+
+#include <com/sun/star/ucb/CertificateValidationRequest.hpp>
+#include <utility>
+
+namespace comphelper{
+
+StillReadWriteInteraction::StillReadWriteInteraction(const css::uno::Reference< css::task::XInteractionHandler >& xHandler,
+ css::uno::Reference< css::task::XInteractionHandler > xAuxiliaryHandler)
+ : m_bUsed (false)
+ , m_bHandledByMySelf (false)
+ , m_xAuxiliaryHandler(std::move(xAuxiliaryHandler))
+{
+ std::vector< ::ucbhelper::InterceptedInteraction::InterceptedRequest > lInterceptions;
+ lInterceptions.reserve(4);
+ ::ucbhelper::InterceptedInteraction::InterceptedRequest aInterceptedRequest;
+
+ aInterceptedRequest.Handle = HANDLE_INTERACTIVEIOEXCEPTION;
+ aInterceptedRequest.Request <<= css::ucb::InteractiveIOException();
+ aInterceptedRequest.Continuation = cppu::UnoType<css::task::XInteractionAbort>::get();
+ lInterceptions.push_back(aInterceptedRequest);
+
+ aInterceptedRequest.Handle = HANDLE_UNSUPPORTEDDATASINKEXCEPTION;
+ aInterceptedRequest.Request <<= css::ucb::UnsupportedDataSinkException();
+ aInterceptedRequest.Continuation = cppu::UnoType<css::task::XInteractionAbort>::get();
+ lInterceptions.push_back(aInterceptedRequest);
+
+ aInterceptedRequest.Handle = HANDLE_AUTHENTICATIONREQUESTEXCEPTION;
+ aInterceptedRequest.Request <<= css::ucb::AuthenticationRequest();
+ aInterceptedRequest.Continuation = cppu::UnoType<css::task::XInteractionApprove>::get();
+ lInterceptions.push_back(aInterceptedRequest);
+
+ aInterceptedRequest.Handle = HANDLE_CERTIFICATEVALIDATIONREQUESTEXCEPTION;
+ aInterceptedRequest.Request <<= css::ucb::CertificateValidationRequest();
+ aInterceptedRequest.Continuation = cppu::UnoType<css::task::XInteractionApprove>::get();
+ lInterceptions.push_back(aInterceptedRequest);
+
+ setInterceptedHandler(xHandler);
+ setInterceptions(std::move(lInterceptions));
+}
+
+void StillReadWriteInteraction::resetInterceptions()
+{
+ setInterceptions(std::vector< ::ucbhelper::InterceptedInteraction::InterceptedRequest >());
+}
+
+void StillReadWriteInteraction::resetErrorStates()
+{
+ m_bUsed = false;
+ m_bHandledByMySelf = false;
+}
+
+
+ucbhelper::InterceptedInteraction::EInterceptionState StillReadWriteInteraction::intercepted(const ::ucbhelper::InterceptedInteraction::InterceptedRequest& aRequest,
+ const css::uno::Reference< css::task::XInteractionRequest >& xRequest)
+{
+ // we are used!
+ m_bUsed = true;
+
+ // check if it's a real interception - might some parameters are not the right ones ...
+ bool bAbort = false;
+ switch(aRequest.Handle)
+ {
+ case HANDLE_INTERACTIVEIOEXCEPTION:
+ {
+ css::ucb::InteractiveIOException exIO;
+ xRequest->getRequest() >>= exIO;
+ bAbort = (
+ (exIO.Code == css::ucb::IOErrorCode_ACCESS_DENIED )
+ || (exIO.Code == css::ucb::IOErrorCode_LOCKING_VIOLATION )
+ || (exIO.Code == css::ucb::IOErrorCode_NOT_EXISTING )
+ // At least on Linux, a request to open some fuse-mounted file O_RDWR may fail with
+ // EOPNOTSUPP (mapped to osl_File_E_NOSYS to IOErrorCode_NOT_SUPPORTED) when opening
+ // it O_RDONLY would succeed:
+ || (exIO.Code == css::ucb::IOErrorCode_NOT_SUPPORTED )
+#ifdef MACOSX
+ // this is a workaround for MAC, on this platform if the file is locked
+ // the returned error code looks to be wrong
+ || (exIO.Code == css::ucb::IOErrorCode_GENERAL )
+#endif
+ );
+ }
+ break;
+
+ case HANDLE_UNSUPPORTEDDATASINKEXCEPTION:
+ {
+ bAbort = true;
+ }
+ break;
+ case HANDLE_CERTIFICATEVALIDATIONREQUESTEXCEPTION:
+ case HANDLE_AUTHENTICATIONREQUESTEXCEPTION:
+ {
+//use internal auxiliary handler and return
+ if (m_xAuxiliaryHandler.is())
+ {
+ m_xAuxiliaryHandler->handle(xRequest);
+ return ::ucbhelper::InterceptedInteraction::E_INTERCEPTED;
+ }
+ else //simply abort
+ bAbort = true;
+ }
+ break;
+ }
+
+ // handle interaction by ourself
+ if (bAbort)
+ {
+ m_bHandledByMySelf = true;
+ css::uno::Reference< css::task::XInteractionContinuation > xAbort = ::ucbhelper::InterceptedInteraction::extractContinuation(
+ xRequest->getContinuations(),
+ cppu::UnoType<css::task::XInteractionAbort>::get() );
+ if (!xAbort.is())
+ return ::ucbhelper::InterceptedInteraction::E_NO_CONTINUATION_FOUND;
+ xAbort->select();
+ return ::ucbhelper::InterceptedInteraction::E_INTERCEPTED;
+ }
+
+ // Otherwise use internal handler.
+ if (m_xInterceptedHandler.is())
+ {
+ m_xInterceptedHandler->handle(xRequest);
+ }
+ return ::ucbhelper::InterceptedInteraction::E_INTERCEPTED;
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/storagehelper.cxx b/comphelper/source/misc/storagehelper.cxx
new file mode 100644
index 0000000000..9d3dbcd227
--- /dev/null
+++ b/comphelper/source/misc/storagehelper.cxx
@@ -0,0 +1,692 @@
+/* -*- 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 <config_gpgme.h>
+
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/embed/XEncryptionProtectedStorage.hpp>
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/embed/XTransactedObject.hpp>
+#include <com/sun/star/embed/StorageFactory.hpp>
+#include <com/sun/star/embed/FileSystemStorageFactory.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/ucb/SimpleFileAccess.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/beans/IllegalTypeException.hpp>
+#include <com/sun/star/xml/crypto/NSSInitializer.hpp>
+#include <com/sun/star/xml/crypto/XDigestContext.hpp>
+#include <com/sun/star/xml/crypto/DigestID.hpp>
+#include <com/sun/star/security/DocumentDigitalSignatures.hpp>
+#include <com/sun/star/security/XCertificate.hpp>
+
+#include <vector>
+
+#include <rtl/digest.h>
+#include <rtl/random.h>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+
+#include <ucbhelper/content.hxx>
+
+#include <comphelper/bytereader.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <comphelper/fileformat.h>
+#include <comphelper/hash.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/documentconstants.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <o3tl/string_view.hxx>
+
+#if HAVE_FEATURE_GPGME
+# include <context.h>
+# include <encryptionresult.h>
+# include <key.h>
+# include <data.h>
+#endif
+
+using namespace ::com::sun::star;
+
+namespace comphelper {
+
+
+uno::Reference< lang::XSingleServiceFactory > OStorageHelper::GetStorageFactory(
+ const uno::Reference< uno::XComponentContext >& rxContext )
+{
+ uno::Reference< uno::XComponentContext> xContext = rxContext.is() ? rxContext : ::comphelper::getProcessComponentContext();
+
+ return embed::StorageFactory::create( xContext );
+}
+
+
+uno::Reference< lang::XSingleServiceFactory > OStorageHelper::GetFileSystemStorageFactory(
+ const uno::Reference< uno::XComponentContext >& rxContext )
+{
+ return embed::FileSystemStorageFactory::create(rxContext);
+}
+
+
+uno::Reference< embed::XStorage > OStorageHelper::GetTemporaryStorage(
+ const uno::Reference< uno::XComponentContext >& rxContext )
+{
+ uno::Reference< embed::XStorage > xTempStorage( GetStorageFactory( rxContext )->createInstance(),
+ uno::UNO_QUERY_THROW );
+ return xTempStorage;
+}
+
+
+uno::Reference< embed::XStorage > OStorageHelper::GetStorageFromURL(
+ const OUString& aURL,
+ sal_Int32 nStorageMode,
+ const uno::Reference< uno::XComponentContext >& rxContext )
+{
+ uno::Sequence< uno::Any > aArgs{ uno::Any(aURL), uno::Any(nStorageMode) };
+ uno::Reference< embed::XStorage > xTempStorage( GetStorageFactory( rxContext )->createInstanceWithArguments( aArgs ),
+ uno::UNO_QUERY_THROW );
+ return xTempStorage;
+}
+
+
+uno::Reference< embed::XStorage > OStorageHelper::GetStorageFromURL2(
+ const OUString& aURL,
+ sal_Int32 nStorageMode,
+ const uno::Reference< uno::XComponentContext >& rxContext )
+{
+ uno::Sequence< uno::Any > aArgs{ uno::Any(aURL), uno::Any(nStorageMode) };
+
+ uno::Reference< lang::XSingleServiceFactory > xFact;
+ css::uno::Any anyEx;
+ try {
+ ::ucbhelper::Content aCntnt( aURL,
+ uno::Reference< css::ucb::XCommandEnvironment > (),
+ getProcessComponentContext() );
+ if (aCntnt.isDocument()) {
+ xFact = GetStorageFactory( rxContext );
+ } else {
+ xFact = GetFileSystemStorageFactory( rxContext );
+ }
+ } catch (uno::Exception &)
+ {
+ anyEx = cppu::getCaughtException();
+ }
+
+ if (!xFact.is())
+ {
+ if (anyEx.hasValue())
+ throw css::lang::WrappedTargetRuntimeException( "", nullptr, anyEx );
+ else
+ throw uno::RuntimeException();
+ }
+
+ uno::Reference< embed::XStorage > xTempStorage(
+ xFact->createInstanceWithArguments( aArgs ), uno::UNO_QUERY_THROW );
+ return xTempStorage;
+}
+
+
+uno::Reference< embed::XStorage > OStorageHelper::GetStorageFromInputStream(
+ const uno::Reference < io::XInputStream >& xStream,
+ const uno::Reference< uno::XComponentContext >& rxContext )
+{
+ uno::Sequence< uno::Any > aArgs{ uno::Any(xStream), uno::Any(embed::ElementModes::READ) };
+ uno::Reference< embed::XStorage > xTempStorage( GetStorageFactory( rxContext )->createInstanceWithArguments( aArgs ),
+ uno::UNO_QUERY_THROW );
+ return xTempStorage;
+}
+
+
+uno::Reference< embed::XStorage > OStorageHelper::GetStorageFromStream(
+ const uno::Reference < io::XStream >& xStream,
+ sal_Int32 nStorageMode,
+ const uno::Reference< uno::XComponentContext >& rxContext )
+{
+ uno::Sequence< uno::Any > aArgs{ uno::Any(xStream), uno::Any(nStorageMode) };
+ uno::Reference< embed::XStorage > xTempStorage( GetStorageFactory( rxContext )->createInstanceWithArguments( aArgs ),
+ uno::UNO_QUERY_THROW );
+ return xTempStorage;
+}
+
+
+void OStorageHelper::CopyInputToOutput(
+ const uno::Reference< io::XInputStream >& xInput,
+ const uno::Reference< io::XOutputStream >& xOutput )
+{
+ static const sal_Int32 nConstBufferSize = 32000;
+
+ if (auto pByteReader = dynamic_cast< comphelper::ByteReader* >( xInput.get() ))
+ {
+ if (auto pByteWriter = dynamic_cast< comphelper::ByteWriter* >( xOutput.get() ))
+ {
+ sal_Int32 nRead;
+ sal_Int8 aTempBuf[ nConstBufferSize ];
+ do
+ {
+ nRead = pByteReader->readSomeBytes ( aTempBuf, nConstBufferSize );
+ pByteWriter->writeBytes ( aTempBuf, nRead );
+ }
+ while ( nRead == nConstBufferSize );
+ return;
+ }
+ }
+
+ sal_Int32 nRead;
+ uno::Sequence < sal_Int8 > aSequence ( nConstBufferSize );
+ do
+ {
+ nRead = xInput->readBytes ( aSequence, nConstBufferSize );
+ if ( nRead < nConstBufferSize )
+ aSequence.realloc( nRead );
+ xOutput->writeBytes ( aSequence );
+ }
+ while ( nRead == nConstBufferSize );
+}
+
+
+uno::Reference< io::XInputStream > OStorageHelper::GetInputStreamFromURL(
+ const OUString& aURL,
+ const uno::Reference< uno::XComponentContext >& context )
+{
+ uno::Reference< io::XInputStream > xInputStream = ucb::SimpleFileAccess::create(context)->openFileRead( aURL );
+ if ( !xInputStream.is() )
+ throw uno::RuntimeException();
+
+ return xInputStream;
+}
+
+
+void OStorageHelper::SetCommonStorageEncryptionData(
+ const uno::Reference< embed::XStorage >& xStorage,
+ const uno::Sequence< beans::NamedValue >& aEncryptionData )
+{
+ uno::Reference< embed::XEncryptionProtectedStorage > xEncrSet( xStorage, uno::UNO_QUERY );
+ if ( !xEncrSet.is() )
+ throw io::IOException("no XEncryptionProtectedStorage"); // TODO
+
+ if ( aEncryptionData.getLength() == 2 &&
+ aEncryptionData[0].Name == "GpgInfos" &&
+ aEncryptionData[1].Name == "EncryptionKey" )
+ {
+ xEncrSet->setGpgProperties(
+ aEncryptionData[0].Value.get< uno::Sequence< uno::Sequence< beans::NamedValue > > >() );
+ xEncrSet->setEncryptionData(
+ aEncryptionData[1].Value.get< uno::Sequence< beans::NamedValue > >() );
+ }
+ else
+ xEncrSet->setEncryptionData( aEncryptionData );
+}
+
+
+sal_Int32 OStorageHelper::GetXStorageFormat(
+ const uno::Reference< embed::XStorage >& xStorage )
+{
+ uno::Reference< beans::XPropertySet > xStorProps( xStorage, uno::UNO_QUERY_THROW );
+
+ OUString aMediaType;
+ xStorProps->getPropertyValue("MediaType") >>= aMediaType;
+
+ sal_Int32 nResult = 0;
+
+ // TODO/LATER: the filter configuration could be used to detect it later, or better a special service
+ if (
+ aMediaType.equalsIgnoreAsciiCase(MIMETYPE_VND_SUN_XML_WRITER_ASCII ) ||
+ aMediaType.equalsIgnoreAsciiCase(MIMETYPE_VND_SUN_XML_WRITER_WEB_ASCII ) ||
+ aMediaType.equalsIgnoreAsciiCase(MIMETYPE_VND_SUN_XML_WRITER_GLOBAL_ASCII) ||
+ aMediaType.equalsIgnoreAsciiCase(MIMETYPE_VND_SUN_XML_DRAW_ASCII ) ||
+ aMediaType.equalsIgnoreAsciiCase(MIMETYPE_VND_SUN_XML_IMPRESS_ASCII ) ||
+ aMediaType.equalsIgnoreAsciiCase(MIMETYPE_VND_SUN_XML_CALC_ASCII ) ||
+ aMediaType.equalsIgnoreAsciiCase(MIMETYPE_VND_SUN_XML_CHART_ASCII ) ||
+ aMediaType.equalsIgnoreAsciiCase(MIMETYPE_VND_SUN_XML_MATH_ASCII )
+ )
+ {
+ nResult = SOFFICE_FILEFORMAT_60;
+ }
+ else if (
+ aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_TEXT_ASCII ) ||
+ aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_TEXT_WEB_ASCII ) ||
+ aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_TEXT_GLOBAL_ASCII ) ||
+ aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_DRAWING_ASCII ) ||
+ aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_PRESENTATION_ASCII) ||
+ aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_SPREADSHEET_ASCII ) ||
+ aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_CHART_ASCII ) ||
+ aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_FORMULA_ASCII ) ||
+ aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_DATABASE_ASCII ) ||
+ aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_REPORT_ASCII ) ||
+ aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_REPORT_CHART_ASCII ) ||
+ aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_TEXT_TEMPLATE_ASCII ) ||
+ aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_TEXT_GLOBAL_TEMPLATE_ASCII ) ||
+ aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_DRAWING_TEMPLATE_ASCII ) ||
+ aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_PRESENTATION_TEMPLATE_ASCII) ||
+ aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_SPREADSHEET_TEMPLATE_ASCII ) ||
+ aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_CHART_TEMPLATE_ASCII ) ||
+ aMediaType.equalsIgnoreAsciiCase(MIMETYPE_OASIS_OPENDOCUMENT_FORMULA_TEMPLATE_ASCII )
+ )
+ {
+ nResult = SOFFICE_FILEFORMAT_8;
+ }
+ else
+ {
+ // the mediatype is not known
+ OUString aMsg = __func__
+ + OUString::Concat(u":")
+ + OUString::number(__LINE__)
+ + ": unknown media type '"
+ + aMediaType
+ + "'";
+ throw beans::IllegalTypeException(aMsg);
+ }
+
+ return nResult;
+}
+
+
+uno::Reference< embed::XStorage > OStorageHelper::GetStorageOfFormatFromURL(
+ const OUString& aFormat,
+ const OUString& aURL,
+ sal_Int32 nStorageMode,
+ const uno::Reference< uno::XComponentContext >& rxContext )
+{
+ uno::Sequence< beans::PropertyValue > aProps{ comphelper::makePropertyValue("StorageFormat",
+ aFormat) };
+
+ uno::Sequence< uno::Any > aArgs{ uno::Any(aURL), uno::Any(nStorageMode), uno::Any(aProps) };
+ uno::Reference< embed::XStorage > xTempStorage( GetStorageFactory( rxContext )->createInstanceWithArguments( aArgs ),
+ uno::UNO_QUERY_THROW );
+ return xTempStorage;
+}
+
+
+uno::Reference< embed::XStorage > OStorageHelper::GetStorageOfFormatFromInputStream(
+ const OUString& aFormat,
+ const uno::Reference < io::XInputStream >& xStream,
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ bool bRepairStorage )
+{
+ uno::Sequence< beans::PropertyValue > aProps( bRepairStorage ? 2 : 1 );
+ auto pProps = aProps.getArray();
+ pProps[0].Name = "StorageFormat";
+ pProps[0].Value <<= aFormat;
+ if ( bRepairStorage )
+ {
+ pProps[1].Name = "RepairPackage";
+ pProps[1].Value <<= bRepairStorage;
+ }
+
+ uno::Sequence< uno::Any > aArgs{ uno::Any(xStream), uno::Any(embed::ElementModes::READ), uno::Any(aProps) };
+ uno::Reference< embed::XStorage > xTempStorage( GetStorageFactory( rxContext )->createInstanceWithArguments( aArgs ),
+ uno::UNO_QUERY_THROW );
+ return xTempStorage;
+}
+
+
+uno::Reference< embed::XStorage > OStorageHelper::GetStorageOfFormatFromStream(
+ const OUString& aFormat,
+ const uno::Reference < io::XStream >& xStream,
+ sal_Int32 nStorageMode,
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ bool bRepairStorage )
+{
+ uno::Sequence< beans::PropertyValue > aProps( bRepairStorage ? 2 : 1 );
+ auto pProps = aProps.getArray();
+ pProps[0].Name = "StorageFormat";
+ pProps[0].Value <<= aFormat;
+ if ( bRepairStorage )
+ {
+ pProps[1].Name = "RepairPackage";
+ pProps[1].Value <<= bRepairStorage;
+ }
+
+ uno::Sequence< uno::Any > aArgs{ uno::Any(xStream), uno::Any(nStorageMode), uno::Any(aProps) };
+ uno::Reference< embed::XStorage > xTempStorage( GetStorageFactory( rxContext )->createInstanceWithArguments( aArgs ),
+ uno::UNO_QUERY_THROW );
+ return xTempStorage;
+}
+
+
+uno::Sequence< beans::NamedValue > OStorageHelper::CreatePackageEncryptionData( std::u16string_view aPassword )
+{
+ // TODO/LATER: Should not the method be part of DocPasswordHelper?
+ uno::Sequence< beans::NamedValue > aEncryptionData;
+ if ( !aPassword.empty() )
+ {
+ sal_Int32 nSha1Ind = 0;
+ // generate SHA256 start key
+ try
+ {
+ uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+
+ uno::Reference< css::xml::crypto::XNSSInitializer > xDigestContextSupplier = css::xml::crypto::NSSInitializer::create(xContext);
+ uno::Reference< css::xml::crypto::XDigestContext > xDigestContext( xDigestContextSupplier->getDigestContext( css::xml::crypto::DigestID::SHA256, uno::Sequence< beans::NamedValue >() ), uno::UNO_SET_THROW );
+
+ OString aUTF8Password( OUStringToOString( aPassword, RTL_TEXTENCODING_UTF8 ) );
+ xDigestContext->updateDigest( uno::Sequence< sal_Int8 >( reinterpret_cast< const sal_Int8* >( aUTF8Password.getStr() ), aUTF8Password.getLength() ) );
+ uno::Sequence< sal_Int8 > aDigest = xDigestContext->finalizeDigestAndDispose();
+
+ ++nSha1Ind;
+ aEncryptionData = { { PACKAGE_ENCRYPTIONDATA_SHA256UTF8, uno::Any(aDigest) } };
+ }
+ catch ( uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("comphelper", "Can not create SHA256 digest!" );
+ }
+
+ // MS_1252 encoding was used for SO60 document format password encoding,
+ // this encoding supports only a minor subset of nonascii characters,
+ // but for compatibility reasons it has to be used for old document formats
+ aEncryptionData.realloc( nSha1Ind + 3 );
+ auto pEncryptionData = aEncryptionData.getArray();
+ // these are StarOffice not-quite-SHA1
+ pEncryptionData[nSha1Ind].Name = PACKAGE_ENCRYPTIONDATA_SHA1UTF8;
+ pEncryptionData[nSha1Ind + 1].Name = PACKAGE_ENCRYPTIONDATA_SHA1MS1252;
+
+ rtl_TextEncoding const pEncoding[2] = { RTL_TEXTENCODING_UTF8, RTL_TEXTENCODING_MS_1252 };
+
+ for ( sal_Int32 nInd = 0; nInd < 2; nInd++ )
+ {
+ OString aByteStrPass = OUStringToOString( aPassword, pEncoding[nInd] );
+
+ sal_uInt8 pBuffer[RTL_DIGEST_LENGTH_SHA1];
+ rtlDigestError nError = rtl_digest_SHA1( aByteStrPass.getStr(),
+ aByteStrPass.getLength(),
+ pBuffer,
+ RTL_DIGEST_LENGTH_SHA1 );
+
+ if ( nError != rtl_Digest_E_None )
+ {
+ aEncryptionData.realloc( nSha1Ind );
+ return aEncryptionData;
+ }
+
+ // coverity[overrun-buffer-arg : FALSE] - coverity has difficulty with css::uno::Sequence
+ pEncryptionData[nSha1Ind+nInd].Value <<= uno::Sequence< sal_Int8 >( reinterpret_cast<sal_Int8*>(pBuffer), RTL_DIGEST_LENGTH_SHA1 );
+ }
+
+ // actual SHA1
+ pEncryptionData[nSha1Ind + 2].Name = PACKAGE_ENCRYPTIONDATA_SHA1CORRECT;
+ OString aByteStrPass = OUStringToOString(aPassword, RTL_TEXTENCODING_UTF8);
+ std::vector<unsigned char> const sha1(::comphelper::Hash::calculateHash(
+ reinterpret_cast<unsigned char const*>(aByteStrPass.getStr()), aByteStrPass.getLength(),
+ ::comphelper::HashType::SHA1));
+ pEncryptionData[nSha1Ind + 2].Value <<= uno::Sequence<sal_Int8>(
+ reinterpret_cast<sal_Int8 const*>(sha1.data()), sha1.size());
+ }
+
+ return aEncryptionData;
+}
+
+uno::Sequence< beans::NamedValue > OStorageHelper::CreateGpgPackageEncryptionData()
+{
+#if HAVE_FEATURE_GPGME
+ // generate session key
+ // --------------------
+
+ rtlRandomPool aRandomPool = rtl_random_createPool();
+
+ // get 32 random chars out of it
+ uno::Sequence < sal_Int8 > aVector(32);
+ rtl_random_getBytes( aRandomPool, aVector.getArray(), aVector.getLength() );
+
+ rtl_random_destroyPool(aRandomPool);
+
+ std::vector< uno::Sequence< beans::NamedValue > > aGpgEncryptions;
+
+ uno::Reference< security::XDocumentDigitalSignatures > xSigner(
+ // here none of the version-dependent methods are called
+ security::DocumentDigitalSignatures::createDefault(
+ comphelper::getProcessComponentContext()));
+
+ // fire up certificate chooser dialog - user can multi-select!
+ const uno::Sequence< uno::Reference< security::XCertificate > > xSignCertificates=
+ xSigner->chooseEncryptionCertificate();
+
+ if (!xSignCertificates.hasElements())
+ return uno::Sequence< beans::NamedValue >(); // user cancelled
+
+ // generate one encrypted key entry for each recipient
+ // ---------------------------------------------------
+
+ std::unique_ptr<GpgME::Context> ctx;
+ GpgME::Error err = GpgME::checkEngine(GpgME::OpenPGP);
+ if (err)
+ throw uno::RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
+
+ ctx.reset( GpgME::Context::createForProtocol(GpgME::OpenPGP) );
+ if (ctx == nullptr)
+ throw uno::RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
+ ctx->setArmor(false);
+
+ for (const auto & cert : xSignCertificates)
+ {
+ uno::Sequence < sal_Int8 > aKeyID;
+ if (cert.is())
+ aKeyID = cert->getSHA1Thumbprint();
+
+ std::vector<GpgME::Key> keys
+ {
+ ctx->key(
+ reinterpret_cast<const char*>(aKeyID.getConstArray()),
+ err, false)
+ };
+
+ // ctx is setup now, let's encrypt the lot!
+ GpgME::Data plain(
+ reinterpret_cast<const char*>(aVector.getConstArray()),
+ size_t(aVector.getLength()), false);
+ GpgME::Data cipher;
+
+ GpgME::EncryptionResult crypt_res = ctx->encrypt(
+ keys, plain,
+ cipher, GpgME::Context::NoCompress);
+
+ off_t result = cipher.seek(0,SEEK_SET);
+ (void) result;
+ assert(result == 0);
+ int len=0, curr=0; char buf;
+ while( (curr=cipher.read(&buf, 1)) )
+ len += curr;
+
+ if(crypt_res.error() || !len)
+ throw lang::IllegalArgumentException(
+ "Not a suitable key, or failed to encrypt.",
+ css::uno::Reference<css::uno::XInterface>(), -1);
+
+ uno::Sequence < sal_Int8 > aCipherValue(len);
+ result = cipher.seek(0,SEEK_SET);
+ assert(result == 0);
+ if( cipher.read(aCipherValue.getArray(), len) != len )
+ throw uno::RuntimeException("The GpgME library failed to read the encrypted value.");
+
+ SAL_INFO("comphelper.crypto", "Generated gpg crypto of length: " << len);
+
+ uno::Sequence< beans::NamedValue > aGpgEncryptionEntry{
+ { "KeyId", uno::Any(aKeyID) },
+ { "KeyPacket", uno::Any(aKeyID) },
+ { "CipherValue", uno::Any(aCipherValue) }
+ };
+
+ aGpgEncryptions.push_back(aGpgEncryptionEntry);
+ }
+
+ uno::Sequence<beans::NamedValue> aEncryptionData
+ = { { PACKAGE_ENCRYPTIONDATA_SHA256UTF8, uno::Any(aVector) } };
+
+ uno::Sequence<beans::NamedValue> aContainer
+ = { { "GpgInfos", uno::Any(comphelper::containerToSequence(aGpgEncryptions)) },
+ { "EncryptionKey", uno::Any(aEncryptionData) } };
+
+ return aContainer;
+#else
+ return uno::Sequence< beans::NamedValue >();
+#endif
+}
+
+bool OStorageHelper::IsValidZipEntryFileName( std::u16string_view aName, bool bSlashAllowed )
+{
+ for ( size_t i = 0; i < aName.size(); i++ )
+ {
+ switch ( aName[i] )
+ {
+ case '\\':
+ case '?':
+ case '<':
+ case '>':
+ case '\"':
+ case '|':
+ case ':':
+ return false;
+ case '/':
+ if ( !bSlashAllowed )
+ return false;
+ break;
+ default:
+ if ( aName[i] < 32 || (aName[i] >= 0xD800 && aName[i] <= 0xDFFF) )
+ return false;
+ }
+ }
+ return true;
+}
+
+
+bool OStorageHelper::PathHasSegment( std::u16string_view aPath, std::u16string_view aSegment )
+{
+ bool bResult = false;
+ const size_t nPathLen = aPath.size();
+ const size_t nSegLen = aSegment.size();
+
+ if ( !aSegment.empty() && nPathLen >= nSegLen )
+ {
+ OUString aEndSegment = OUString::Concat("/") + aSegment;
+ OUString aInternalSegment = aEndSegment + "/";
+
+ if ( aPath.find( aInternalSegment ) != std::u16string_view::npos )
+ bResult = true;
+
+ if ( !bResult && o3tl::starts_with(aPath, aSegment ) )
+ {
+ if ( nPathLen == nSegLen || aPath[nSegLen] == '/' )
+ bResult = true;
+ }
+
+ if ( !bResult && nPathLen > nSegLen && aPath.substr( nPathLen - nSegLen - 1, nSegLen + 1 ) == aEndSegment )
+ bResult = true;
+ }
+
+ return bResult;
+}
+
+class LifecycleProxy::Impl
+ : public std::vector< uno::Reference< embed::XStorage > > {};
+LifecycleProxy::LifecycleProxy()
+ : m_xBadness( new Impl ) { }
+LifecycleProxy::~LifecycleProxy() { }
+
+void LifecycleProxy::commitStorages()
+{
+ std::for_each(m_xBadness->rbegin(), m_xBadness->rend(), // reverse order (outwards)
+ [](Impl::reference rxItem) {
+ uno::Reference<embed::XTransactedObject> const xTransaction(rxItem, uno::UNO_QUERY);
+ if (xTransaction.is())
+ {
+ xTransaction->commit();
+ }
+ });
+}
+
+static void splitPath( std::vector<OUString> &rElems, std::u16string_view rPath )
+{
+ for (sal_Int32 i = 0; i >= 0;)
+ rElems.push_back( OUString(o3tl::getToken(rPath, 0, '/', i )) );
+}
+
+static uno::Reference< embed::XStorage > LookupStorageAtPath(
+ const uno::Reference< embed::XStorage > &xParentStorage,
+ std::vector<OUString> &rElems, sal_uInt32 nOpenMode,
+ LifecycleProxy const &rNastiness )
+{
+ uno::Reference< embed::XStorage > xStorage( xParentStorage );
+ rNastiness.m_xBadness->push_back( xStorage );
+ for( size_t i = 0; i < rElems.size() && xStorage.is(); i++ )
+ {
+ xStorage = xStorage->openStorageElement( rElems[i], nOpenMode );
+ rNastiness.m_xBadness->push_back( xStorage );
+ }
+ return xStorage;
+}
+
+uno::Reference< embed::XStorage > OStorageHelper::GetStorageAtPath(
+ const uno::Reference< embed::XStorage > &xStorage,
+ std::u16string_view rPath, sal_uInt32 nOpenMode,
+ LifecycleProxy const &rNastiness )
+{
+ std::vector<OUString> aElems;
+ splitPath( aElems, rPath );
+ return LookupStorageAtPath( xStorage, aElems, nOpenMode, rNastiness );
+}
+
+uno::Reference< io::XStream > OStorageHelper::GetStreamAtPath(
+ const uno::Reference< embed::XStorage > &xParentStorage,
+ std::u16string_view rPath, sal_uInt32 nOpenMode,
+ LifecycleProxy const &rNastiness )
+{
+ std::vector<OUString> aElems;
+ splitPath( aElems, rPath );
+ OUString aName( aElems.back() );
+ aElems.pop_back();
+ sal_uInt32 nStorageMode = nOpenMode & ~embed::ElementModes::TRUNCATE;
+ uno::Reference< embed::XStorage > xStorage(
+ LookupStorageAtPath( xParentStorage, aElems, nStorageMode, rNastiness ),
+ uno::UNO_SET_THROW );
+ return xStorage->openStreamElement( aName, nOpenMode );
+}
+
+uno::Reference< io::XStream > OStorageHelper::GetStreamAtPackageURL(
+ uno::Reference< embed::XStorage > const& xParentStorage,
+ const OUString& rURL, sal_uInt32 const nOpenMode,
+ LifecycleProxy const & rNastiness)
+{
+ OUString path;
+ if (rURL.startsWithIgnoreAsciiCase("vnd.sun.star.Package:", &path))
+ {
+ return GetStreamAtPath(xParentStorage, path, nOpenMode, rNastiness);
+ }
+ return nullptr;
+}
+
+OUString OStorageHelper::GetODFVersionFromStorage(const uno::Reference<embed::XStorage>& xStorage)
+{
+ OUString aODFVersion;
+ try
+ {
+ uno::Reference<beans::XPropertySet> xPropSet(xStorage, uno::UNO_QUERY_THROW);
+ xPropSet->getPropertyValue("Version") >>= aODFVersion;
+ }
+ catch (uno::Exception&)
+ {
+ }
+ return aODFVersion;
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/string.cxx b/comphelper/source/misc/string.cxx
new file mode 100644
index 0000000000..e17951fc43
--- /dev/null
+++ b/comphelper/source/misc/string.cxx
@@ -0,0 +1,678 @@
+/* -*- 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 <string_view>
+#include <utility>
+#include <vector>
+#include <algorithm>
+
+#include <o3tl/safeint.hxx>
+#include <o3tl/string_view.hxx>
+#include <rtl/character.hxx>
+#include <rtl/ustring.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/string.hxx>
+#include <rtl/strbuf.hxx>
+#include <sal/log.hxx>
+#include <sal/types.h>
+
+#include <comphelper/string.hxx>
+#include <comphelper/stl_types.hxx>
+#include <comphelper/sequence.hxx>
+
+#include <com/sun/star/i18n/BreakIterator.hpp>
+#include <com/sun/star/i18n/CharType.hpp>
+#include <com/sun/star/i18n/Collator.hpp>
+
+
+namespace comphelper::string {
+
+namespace
+{
+ template <typename T, typename C> T tmpl_stripStart(const T &rIn,
+ const C cRemove)
+ {
+ if (rIn.empty())
+ return rIn;
+
+ typename T::size_type i = 0;
+
+ while (i < rIn.size())
+ {
+ if (rIn[i] != cRemove)
+ break;
+ ++i;
+ }
+
+ return rIn.substr(i);
+ }
+ template <typename T, typename C> T tmpl_stripStartString(const T &rIn,
+ const C cRemove)
+ {
+ if (rIn.isEmpty())
+ return rIn;
+
+ sal_Int32 i = 0;
+
+ while (i < rIn.getLength())
+ {
+ if (rIn[i] != cRemove)
+ break;
+ ++i;
+ }
+
+ return rIn.copy(i);
+ }
+}
+
+OString stripStart(const OString& rIn, char c)
+{
+ return tmpl_stripStartString<OString, char>(rIn, c);
+}
+
+std::string_view stripStart(std::string_view rIn, char c)
+{
+ return tmpl_stripStart<std::string_view, char>(rIn, c);
+}
+
+OUString stripStart(const OUString& rIn, sal_Unicode c)
+{
+ return tmpl_stripStartString<OUString, sal_Unicode>(rIn, c);
+}
+
+std::u16string_view stripStart(std::u16string_view rIn, sal_Unicode c)
+{
+ return tmpl_stripStart<std::u16string_view, sal_Unicode>(rIn, c);
+}
+
+namespace
+{
+ template <typename T, typename C> T tmpl_stripEnd(const T &rIn,
+ const C cRemove)
+ {
+ if (rIn.empty())
+ return rIn;
+
+ typename T::size_type i = rIn.size();
+
+ while (i > 0)
+ {
+ if (rIn[i-1] != cRemove)
+ break;
+ --i;
+ }
+
+ return rIn.substr(0, i);
+ }
+ template <typename T, typename C> T tmpl_stripEndString(const T &rIn,
+ const C cRemove)
+ {
+ if (rIn.isEmpty())
+ return rIn;
+
+ sal_Int32 i = rIn.getLength();
+
+ while (i > 0)
+ {
+ if (rIn[i-1] != cRemove)
+ break;
+ --i;
+ }
+
+ return rIn.copy(0, i);
+ }
+}
+
+OString stripEnd(const OString& rIn, char c)
+{
+ return tmpl_stripEndString<OString, char>(rIn, c);
+}
+
+std::string_view stripEnd(std::string_view rIn, char c)
+{
+ return tmpl_stripEnd<std::string_view, char>(rIn, c);
+}
+
+OUString stripEnd(const OUString& rIn, sal_Unicode c)
+{
+ return tmpl_stripEndString<OUString, sal_Unicode>(rIn, c);
+}
+
+std::u16string_view stripEnd(std::u16string_view rIn, sal_Unicode c)
+{
+ return tmpl_stripEnd<std::u16string_view, sal_Unicode>(rIn, c);
+}
+
+namespace
+{
+ template <typename T, typename C> T tmpl_strip(const T &rIn,
+ const C cRemove)
+ {
+ if (rIn.empty())
+ return rIn;
+
+ typename T::size_type end = rIn.size();
+ while (end > 0)
+ {
+ if (rIn[end-1] != cRemove)
+ break;
+ --end;
+ }
+
+ typename T::size_type start = 0;
+ while (start < end)
+ {
+ if (rIn[start] != cRemove)
+ break;
+ ++start;
+ }
+
+ return rIn.substr(start, end - start);
+ }
+ template <typename T, typename C> T tmpl_stripString(const T &rIn,
+ const C cRemove)
+ {
+ if (rIn.isEmpty())
+ return rIn;
+
+ sal_Int32 end = rIn.getLength();
+ while (end > 0)
+ {
+ if (rIn[end-1] != cRemove)
+ break;
+ --end;
+ }
+ sal_Int32 start = 0;
+ while (start < end)
+ {
+ if (rIn[start] != cRemove)
+ break;
+ ++start;
+ }
+
+ return rIn.copy(start, end - start);
+ }
+}
+
+OString strip(const OString& rIn, char c)
+{
+ return tmpl_stripString<OString, char>(rIn, c);
+}
+
+std::string_view strip(std::string_view rIn, char c)
+{
+ return tmpl_strip<std::string_view, char>(rIn, c);
+}
+
+OUString strip(const OUString& rIn, sal_Unicode c)
+{
+ return tmpl_stripString<OUString, sal_Unicode>(rIn, c);
+}
+
+std::u16string_view strip(std::u16string_view rIn, sal_Unicode c)
+{
+ return tmpl_strip<std::u16string_view, sal_Unicode>(rIn, c);
+}
+
+namespace
+{
+ template <typename T, typename C> sal_Int32 tmpl_getTokenCount( T rIn,
+ C cTok)
+ {
+ // Empty String: TokenCount by Definition is 0
+ if (rIn.empty())
+ return 0;
+
+ sal_Int32 nTokCount = 1;
+ for (typename T::size_type i = 0; i < rIn.size(); ++i)
+ {
+ if (rIn[i] == cTok)
+ ++nTokCount;
+ }
+ return nTokCount;
+ }
+}
+
+sal_Int32 getTokenCount(std::string_view rIn, char cTok)
+{
+ return tmpl_getTokenCount<std::string_view, char>(rIn, cTok);
+}
+
+sal_Int32 getTokenCount(std::u16string_view rIn, sal_Unicode cTok)
+{
+ return tmpl_getTokenCount<std::u16string_view, sal_Unicode>(rIn, cTok);
+}
+
+sal_uInt32 decimalStringToNumber(std::u16string_view str)
+{
+ sal_uInt32 result = 0;
+ for( sal_Int32 i = 0; i < static_cast<sal_Int32>(str.size()); )
+ {
+ sal_uInt32 c = o3tl::iterateCodePoints(str, &i);
+ sal_uInt32 value = 0;
+ if( c <= 0x0039) // ASCII decimal digits, most common
+ value = c - 0x0030;
+ else if( c >= 0x1D7F6 ) // mathematical monospace digits
+ value = c - 0x1D7F6;
+ else if( c >= 0x1D7EC ) // mathematical sans-serif bold digits
+ value = c - 0x1D7EC;
+ else if( c >= 0x1D7E2 ) // mathematical sans-serif digits
+ value = c - 0x1D7E2;
+ else if( c >= 0x1D7D8 ) // mathematical double-struck digits
+ value = c - 0x1D7D8;
+ else if( c >= 0x1D7CE ) // mathematical bold digits
+ value = c - 0x1D7CE;
+ else if( c >= 0x11066 ) // brahmi digits
+ value = c - 0x11066;
+ else if( c >= 0x104A0 ) // osmanya digits
+ value = c - 0x104A0;
+ else if( c >= 0xFF10 ) // fullwidth digits
+ value = c - 0xFF10;
+ else if( c >= 0xABF0 ) // meetei mayek digits
+ value = c - 0xABF0;
+ else if( c >= 0xAA50 ) // cham digits
+ value = c - 0xAA50;
+ else if( c >= 0xA9D0 ) // javanese digits
+ value = c - 0xA9D0;
+ else if( c >= 0xA900 ) // kayah li digits
+ value = c - 0xA900;
+ else if( c >= 0xA8D0 ) // saurashtra digits
+ value = c - 0xA8D0;
+ else if( c >= 0xA620 ) // vai digits
+ value = c - 0xA620;
+ else if( c >= 0x1C50 ) // ol chiki digits
+ value = c - 0x1C50;
+ else if( c >= 0x1C40 ) // lepcha digits
+ value = c - 0x1C40;
+ else if( c >= 0x1BB0 ) // sundanese digits
+ value = c - 0x1BB0;
+ else if( c >= 0x1B50 ) // balinese digits
+ value = c - 0x1B50;
+ else if( c >= 0x1A90 ) // tai tham tham digits
+ value = c - 0x1A90;
+ else if( c >= 0x1A80 ) // tai tham hora digits
+ value = c - 0x1A80;
+ else if( c >= 0x19D0 ) // new tai lue digits
+ value = c - 0x19D0;
+ else if( c >= 0x1946 ) // limbu digits
+ value = c - 0x1946;
+ else if( c >= 0x1810 ) // mongolian digits
+ value = c - 0x1810;
+ else if( c >= 0x17E0 ) // khmer digits
+ value = c - 0x17E0;
+ else if( c >= 0x1090 ) // myanmar shan digits
+ value = c - 0x1090;
+ else if( c >= 0x1040 ) // myanmar digits
+ value = c - 0x1040;
+ else if( c >= 0x0F20 ) // tibetan digits
+ value = c - 0x0F20;
+ else if( c >= 0x0ED0 ) // lao digits
+ value = c - 0x0ED0;
+ else if( c >= 0x0E50 ) // thai digits
+ value = c - 0x0E50;
+ else if( c >= 0x0D66 ) // malayalam digits
+ value = c - 0x0D66;
+ else if( c >= 0x0CE6 ) // kannada digits
+ value = c - 0x0CE6;
+ else if( c >= 0x0C66 ) // telugu digits
+ value = c - 0x0C66;
+ else if( c >= 0x0BE6 ) // tamil digits
+ value = c - 0x0BE6;
+ else if( c >= 0x0B66 ) // odia digits
+ value = c - 0x0B66;
+ else if( c >= 0x0AE6 ) // gujarati digits
+ value = c - 0x0AE6;
+ else if( c >= 0x0A66 ) // gurmukhi digits
+ value = c - 0x0A66;
+ else if( c >= 0x09E6 ) // bengali digits
+ value = c - 0x09E6;
+ else if( c >= 0x0966 ) // devanagari digit
+ value = c - 0x0966;
+ else if( c >= 0x07C0 ) // nko digits
+ value = c - 0x07C0;
+ else if( c >= 0x06F0 ) // extended arabic-indic digits
+ value = c - 0x06F0;
+ else if( c >= 0x0660 ) // arabic-indic digits
+ value = c - 0x0660;
+ result = result * 10 + value;
+ }
+ return result;
+}
+
+using namespace ::com::sun::star;
+
+// convert between sequence of string and comma separated string
+
+OUString convertCommaSeparated(
+ uno::Sequence< OUString > const& i_rSeq)
+{
+ OUStringBuffer buf;
+ ::comphelper::intersperse(
+ i_rSeq.begin(), i_rSeq.end(), ::comphelper::OUStringBufferAppender(buf), OUString( ", " ));
+ return buf.makeStringAndClear();
+}
+
+std::vector<OUString>
+ split(std::u16string_view rStr, sal_Unicode cSeparator)
+{
+ std::vector< OUString > vec;
+ std::size_t idx = 0;
+ do
+ {
+ std::u16string_view kw = o3tl::getToken(rStr, cSeparator, idx);
+ kw = o3tl::trim(kw);
+ if (!kw.empty())
+ {
+ vec.push_back(OUString(kw));
+ }
+
+ } while (idx != std::u16string_view::npos);
+
+ return vec;
+}
+
+uno::Sequence< OUString >
+ convertCommaSeparated( std::u16string_view i_rString )
+{
+ std::vector< OUString > vec = split(i_rString, ',');
+ return comphelper::containerToSequence(vec);
+}
+
+OString join(std::string_view rSeparator, const std::vector<OString>& rSequence)
+{
+ OStringBuffer aBuffer;
+ for (size_t i = 0; i < rSequence.size(); ++i)
+ {
+ if (i != 0)
+ aBuffer.append(rSeparator);
+ aBuffer.append(rSequence[i]);
+ }
+ return aBuffer.makeStringAndClear();
+}
+
+sal_Int32 compareNatural( const OUString & rLHS, const OUString & rRHS,
+ const uno::Reference< i18n::XCollator > &rCollator,
+ const uno::Reference< i18n::XBreakIterator > &rBI,
+ const lang::Locale &rLocale )
+{
+ sal_Int32 nRet = 0;
+
+ sal_Int32 nLHSLastNonDigitPos = 0;
+ sal_Int32 nRHSLastNonDigitPos = 0;
+ sal_Int32 nLHSFirstDigitPos = 0;
+ sal_Int32 nRHSFirstDigitPos = 0;
+
+ // Check if the string starts with a digit
+ sal_Int32 nStartsDigitLHS = rBI->endOfCharBlock(rLHS, nLHSFirstDigitPos, rLocale, i18n::CharType::DECIMAL_DIGIT_NUMBER);
+ sal_Int32 nStartsDigitRHS = rBI->endOfCharBlock(rRHS, nRHSFirstDigitPos, rLocale, i18n::CharType::DECIMAL_DIGIT_NUMBER);
+
+ if (nStartsDigitLHS > 0 && nStartsDigitRHS > 0)
+ {
+ sal_uInt32 nLHS = comphelper::string::decimalStringToNumber(rLHS.subView(0, nStartsDigitLHS));
+ sal_uInt32 nRHS = comphelper::string::decimalStringToNumber(rRHS.subView(0, nStartsDigitRHS));
+
+ if (nLHS != nRHS)
+ return nLHS < nRHS ? -1 : 1;
+ nLHSLastNonDigitPos = nStartsDigitLHS;
+ nRHSLastNonDigitPos = nStartsDigitRHS;
+ }
+ else if (nStartsDigitLHS > 0)
+ return -1;
+ else if (nStartsDigitRHS > 0)
+ return 1;
+
+ while (nLHSFirstDigitPos < rLHS.getLength() || nRHSFirstDigitPos < rRHS.getLength())
+ {
+ sal_Int32 nLHSChunkLen;
+ sal_Int32 nRHSChunkLen;
+
+ //Compare non digit block as normal strings
+ nLHSFirstDigitPos = rBI->nextCharBlock(rLHS, nLHSLastNonDigitPos, rLocale, i18n::CharType::DECIMAL_DIGIT_NUMBER);
+ nRHSFirstDigitPos = rBI->nextCharBlock(rRHS, nRHSLastNonDigitPos, rLocale, i18n::CharType::DECIMAL_DIGIT_NUMBER);
+
+ if (nLHSFirstDigitPos == -1)
+ nLHSFirstDigitPos = rLHS.getLength();
+
+ if (nRHSFirstDigitPos == -1)
+ nRHSFirstDigitPos = rRHS.getLength();
+
+ nLHSChunkLen = nLHSFirstDigitPos - nLHSLastNonDigitPos;
+ nRHSChunkLen = nRHSFirstDigitPos - nRHSLastNonDigitPos;
+
+ nRet = rCollator->compareSubstring(rLHS, nLHSLastNonDigitPos, nLHSChunkLen, rRHS, nRHSLastNonDigitPos, nRHSChunkLen);
+ if (nRet != 0)
+ break;
+
+ //Compare digit block as one number vs another
+ nLHSLastNonDigitPos = rBI->endOfCharBlock(rLHS, nLHSFirstDigitPos, rLocale, i18n::CharType::DECIMAL_DIGIT_NUMBER);
+ nRHSLastNonDigitPos = rBI->endOfCharBlock(rRHS, nRHSFirstDigitPos, rLocale, i18n::CharType::DECIMAL_DIGIT_NUMBER);
+ if (nLHSLastNonDigitPos == -1)
+ nLHSLastNonDigitPos = rLHS.getLength();
+ if (nRHSLastNonDigitPos == -1)
+ nRHSLastNonDigitPos = rRHS.getLength();
+ nLHSChunkLen = nLHSLastNonDigitPos - nLHSFirstDigitPos;
+ nRHSChunkLen = nRHSLastNonDigitPos - nRHSFirstDigitPos;
+
+ //To-Do: Possibly scale down those unicode codepoints that relate to
+ //numbers outside of the normal 0-9 range, e.g. see GetLocalizedChar in
+ //vcl
+
+ sal_uInt32 nLHS = comphelper::string::decimalStringToNumber(rLHS.subView(nLHSFirstDigitPos, nLHSChunkLen));
+ sal_uInt32 nRHS = comphelper::string::decimalStringToNumber(rRHS.subView(nRHSFirstDigitPos, nRHSChunkLen));
+
+ if (nLHS != nRHS)
+ {
+ nRet = (nLHS < nRHS) ? -1 : 1;
+ break;
+ }
+ }
+
+ return nRet;
+}
+
+NaturalStringSorter::NaturalStringSorter(
+ const uno::Reference< uno::XComponentContext > &rContext,
+ lang::Locale aLocale) : m_aLocale(std::move(aLocale))
+{
+ m_xCollator = i18n::Collator::create( rContext );
+ m_xCollator->loadDefaultCollator(m_aLocale, 0);
+ m_xBI = i18n::BreakIterator::create( rContext );
+}
+
+bool isdigitAsciiString(std::string_view rString)
+{
+ return std::all_of(
+ rString.data(), rString.data() + rString.size(),
+ [](unsigned char c){ return rtl::isAsciiDigit(c); });
+}
+
+bool isdigitAsciiString(std::u16string_view rString)
+{
+ return std::all_of(
+ rString.data(), rString.data() + rString.size(),
+ [](sal_Unicode c){ return rtl::isAsciiDigit(c); });
+}
+
+OUString reverseString(std::u16string_view rStr)
+{
+ if (rStr.empty())
+ return OUString();
+
+ std::size_t i = rStr.size();
+ OUStringBuffer sBuf(static_cast<sal_Int32>(i));
+ while (i)
+ sBuf.append(rStr[--i]);
+ return sBuf.makeStringAndClear();
+}
+
+OUString reverseCodePoints(OUString const & str) {
+ auto const len = str.getLength();
+ OUStringBuffer buf(len);
+ for (auto i = len; i != 0;) {
+ buf.appendUtf32(str.iterateCodePoints(&i, -1));
+ }
+ return buf.makeStringAndClear();
+}
+
+sal_Int32 indexOfAny(std::u16string_view rIn,
+ sal_Unicode const*const pChars, sal_Int32 const nPos)
+{
+ for (std::u16string_view::size_type i = nPos; i < rIn.size(); ++i)
+ {
+ sal_Unicode const c = rIn[i];
+ for (sal_Unicode const* pChar = pChars; *pChar; ++pChar)
+ {
+ if (c == *pChar)
+ {
+ return i;
+ }
+ }
+ }
+ return -1;
+}
+
+OUString removeAny(std::u16string_view rIn,
+ sal_Unicode const*const pChars)
+{
+ OUStringBuffer buf;
+ bool isFound(false);
+ for (std::u16string_view::size_type i = 0; i < rIn.size(); ++i)
+ {
+ sal_Unicode const c = rIn[i];
+ bool removeC(false);
+ for (sal_Unicode const* pChar = pChars; *pChar; ++pChar)
+ {
+ if (c == *pChar)
+ {
+ removeC = true;
+ break;
+ }
+ }
+ if (removeC)
+ {
+ if (!isFound)
+ {
+ if (i > 0)
+ {
+ buf.append(rIn.substr(0, i));
+ }
+ isFound = true;
+ }
+ }
+ else if (isFound)
+ {
+ buf.append(c);
+ }
+ }
+ return isFound ? buf.makeStringAndClear() : OUString(rIn);
+}
+
+OUString setToken(const OUString& rIn, sal_Int32 nToken, sal_Unicode cTok,
+ std::u16string_view rNewToken)
+{
+ sal_Int32 nLen = rIn.getLength();
+ sal_Int32 nTok = 0;
+ sal_Int32 nFirstChar = 0;
+ sal_Int32 i = 0;
+
+ // Determine token position and length
+ while ( i < nLen )
+ {
+ // Increase token count if match
+ if (rIn[i] == cTok)
+ {
+ ++nTok;
+
+ if (nTok == nToken)
+ nFirstChar = i+1;
+ else if (nTok > nToken)
+ break;
+ }
+
+ ++i;
+ }
+
+ if (nTok >= nToken)
+ return rIn.replaceAt(nFirstChar, i-nFirstChar, rNewToken);
+ return rIn;
+}
+
+/** Similar to OUString::replaceAt, but for an OUStringBuffer.
+
+ Replace n = count characters
+ from position index in this string with newStr.
+ */
+void replaceAt(OUStringBuffer& rIn, sal_Int32 nIndex, sal_Int32 nCount, std::u16string_view newStr )
+{
+ assert(nIndex >= 0 && nIndex <= rIn.getLength());
+ assert(nCount >= 0);
+ assert(nCount <= rIn.getLength() - nIndex);
+
+ /* Append? */
+ const sal_Int32 nOldLength = rIn.getLength();
+ if ( nIndex == nOldLength )
+ {
+ rIn.append(newStr);
+ return;
+ }
+
+ sal_Int32 nNewLength = nOldLength + newStr.size() - nCount;
+ if (newStr.size() > o3tl::make_unsigned(nCount))
+ rIn.ensureCapacity(nOldLength + newStr.size() - nCount);
+
+ sal_Unicode* pStr = const_cast<sal_Unicode*>(rIn.getStr());
+ memmove(pStr + nIndex + newStr.size(), pStr + nIndex + nCount, nOldLength - nIndex + nCount);
+ memcpy(pStr + nIndex, newStr.data(), newStr.size());
+
+ rIn.setLength(nNewLength);
+}
+
+OUString sanitizeStringSurrogates(const OUString& rString)
+{
+ sal_Int32 i=0;
+ while (i < rString.getLength())
+ {
+ sal_Unicode c = rString[i];
+ if (rtl::isHighSurrogate(c))
+ {
+ if (i+1 == rString.getLength()
+ || !rtl::isLowSurrogate(rString[i+1]))
+ {
+ SAL_WARN("comphelper", "Surrogate error: high without low");
+ return rString.copy(0, i);
+ }
+ ++i; //skip correct low
+ }
+ if (rtl::isLowSurrogate(c)) //bare low without preceding high
+ {
+ SAL_WARN("comphelper", "Surrogate error: low without high");
+ return rString.copy(0, i);
+ }
+ ++i;
+ }
+ return rString;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/synchronousdispatch.cxx b/comphelper/source/misc/synchronousdispatch.cxx
new file mode 100644
index 0000000000..1602c8963f
--- /dev/null
+++ b/comphelper/source/misc/synchronousdispatch.cxx
@@ -0,0 +1,81 @@
+/* -*- 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 <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/frame/XSynchronousDispatch.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+
+#include <comphelper/synchronousdispatch.hxx>
+#include <comphelper/processfactory.hxx>
+#include <sal/log.hxx>
+
+namespace comphelper
+{
+
+
+using namespace ::com::sun::star;
+
+uno::Reference< lang::XComponent > SynchronousDispatch::dispatch(
+ const uno::Reference< uno::XInterface > &xStartPoint,
+ const OUString &sURL,
+ const OUString &sTarget,
+ const uno::Sequence< beans::PropertyValue > &lArguments )
+{
+ util::URL aURL;
+ aURL.Complete = sURL;
+ uno::Reference < util::XURLTransformer > xTrans = util::URLTransformer::create( ::comphelper::getProcessComponentContext() );
+ xTrans->parseStrict( aURL );
+
+ uno::Reference < frame::XDispatch > xDispatcher;
+ uno::Reference < frame::XDispatchProvider > xProvider( xStartPoint, uno::UNO_QUERY );
+
+ if ( xProvider.is() )
+ xDispatcher = xProvider->queryDispatch( aURL, sTarget, 0 );
+
+ uno::Reference < lang::XComponent > aComponent;
+
+ if ( xDispatcher.is() )
+ {
+ try
+ {
+ uno::Any aRet;
+ uno::Reference < frame::XSynchronousDispatch > xSyncDisp( xDispatcher, uno::UNO_QUERY_THROW );
+
+ aRet = xSyncDisp->dispatchWithReturnValue( aURL, lArguments );
+
+ aRet >>= aComponent;
+ }
+ catch ( uno::Exception& )
+ {
+ // can't use TOOLS_WARN_EXCEPTION, as comphelper is used by libtl!
+ SAL_WARN("comphelper", "SynchronousDispatch::dispatch(): error while dispatching '"
+ << sURL << "' for '" << sTarget << "'!");
+ }
+ }
+
+ return aComponent;
+}
+
+
+} // namespace comphelper
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/syntaxhighlight.cxx b/comphelper/source/misc/syntaxhighlight.cxx
new file mode 100644
index 0000000000..89dcb73752
--- /dev/null
+++ b/comphelper/source/misc/syntaxhighlight.cxx
@@ -0,0 +1,741 @@
+/* -*- 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/character.hxx>
+#include <rtl/ustring.hxx>
+#include <unicode/uchar.h>
+#include <comphelper/syntaxhighlight.hxx>
+#include <o3tl/typed_flags_set.hxx>
+
+namespace {
+
+// Flags for character properties
+enum class CharFlags {
+ StartIdentifier = 0x0001,
+ InIdentifier = 0x0002,
+ StartNumber = 0x0004,
+ InNumber = 0x0008,
+ InHexNumber = 0x0010,
+ InOctNumber = 0x0020,
+ StartString = 0x0040,
+ Operator = 0x0080,
+ Space = 0x0100,
+ EOL = 0x0200
+};
+
+}
+
+namespace o3tl {
+ template<> struct typed_flags<CharFlags> : is_typed_flags<CharFlags, 0x03ff> {};
+}
+
+// ##########################################################################
+// ATTENTION: all these words need to be in lower case
+// ##########################################################################
+static const char* strListBasicKeyWords[] = {
+ "access",
+ "alias",
+ "and",
+ "any",
+ "append",
+ "as",
+ "attribute",
+ "base",
+ "binary",
+ "boolean",
+ "byref",
+ "byte",
+ "byval",
+ "call",
+ "case",
+ "cdecl",
+ "classmodule",
+ "close",
+ "compare",
+ "compatible",
+ "const",
+ "currency",
+ "date",
+ "declare",
+ "defbool",
+ "defcur",
+ "defdate",
+ "defdbl",
+ "deferr",
+ "defint",
+ "deflng",
+ "defobj",
+ "defsng",
+ "defstr",
+ "defvar",
+ "dim",
+ "do",
+ "doevents",
+ "double",
+ "each",
+ "else",
+ "elseif",
+ "end",
+ "end enum",
+ "end function",
+ "end if",
+ "end property",
+ "end select",
+ "end sub",
+ "end type",
+ "endif",
+ "enum",
+ "eqv",
+ "erase",
+ "error",
+ "exit",
+ "explicit",
+ "for",
+ "function",
+ "get",
+ "global",
+ "gosub",
+ "goto",
+ "if",
+ "imp",
+ "implements",
+ "in",
+ "input",
+ "integer",
+ "is",
+ "let",
+ "lib",
+ "like",
+ "line",
+ "line input",
+ "local",
+ "lock",
+ "long",
+ "loop",
+ "lprint",
+ "lset",
+ "mod",
+ "name",
+ "new",
+ "next",
+ "not",
+ "object",
+ "on",
+ "open",
+ "option",
+ "optional",
+ "or",
+ "output",
+ "paramarray",
+ "preserve",
+ "print",
+ "private",
+ "property",
+ "public",
+ "random",
+ "read",
+ "redim",
+ "rem",
+ "resume",
+ "return",
+ "rset",
+ "select",
+ "set",
+ "shared",
+ "single",
+ "static",
+ "step",
+ "stop",
+ "string",
+ "sub",
+ "system",
+ "text",
+ "then",
+ "to",
+ "type",
+ "typeof",
+ "until",
+ "variant",
+ "vbasupport",
+ "wend",
+ "while",
+ "with",
+ "withevents",
+ "write",
+ "xor"
+};
+
+
+static const char* strListSqlKeyWords[] = {
+ "all",
+ "and",
+ "any",
+ "as",
+ "asc",
+ "avg",
+ "between",
+ "by",
+ "cast",
+ "corresponding",
+ "count",
+ "create",
+ "cross",
+ "delete",
+ "desc",
+ "distinct",
+ "drop",
+ "escape",
+ "except",
+ "exists",
+ "false",
+ "from",
+ "full",
+ "global",
+ "group",
+ "having",
+ "in",
+ "inner",
+ "insert",
+ "intersect",
+ "into",
+ "is",
+ "join",
+ "left",
+ "like",
+ "limit",
+ "local",
+ "match",
+ "max",
+ "min",
+ "natural",
+ "not",
+ "null",
+ "on",
+ "or",
+ "order",
+ "outer",
+ "right",
+ "select",
+ "set",
+ "some",
+ "sum",
+ "table",
+ "temporary",
+ "true",
+ "union",
+ "unique",
+ "unknown",
+ "update",
+ "using",
+ "values",
+ "where"
+};
+
+
+extern "C" {
+
+static int compare_strings( const void *arg1, const void *arg2 )
+{
+ return strcmp( static_cast<char const *>(arg1), *static_cast<char * const *>(arg2) );
+}
+
+}
+
+namespace
+{
+ bool isAlpha(sal_Unicode c)
+ {
+ if (rtl::isAsciiAlpha(c))
+ return true;
+ return u_isalpha(c);
+ }
+}
+
+class SyntaxHighlighter::Tokenizer
+{
+ // Character information tables
+ CharFlags aCharTypeTab[256] = {};
+
+ // Auxiliary function: testing of the character flags
+ bool testCharFlags(sal_Unicode c, CharFlags nTestFlags) const;
+
+ // Get new token, EmptyString == nothing more over there
+ bool getNextToken(std::u16string_view::const_iterator& pos, std::u16string_view::const_iterator end, /*out*/TokenType& reType,
+ /*out*/std::u16string_view::const_iterator& rpStartPos, /*out*/std::u16string_view::const_iterator& rpEndPos) const;
+
+ const char** ppListKeyWords;
+ sal_uInt16 nKeyWordCount;
+
+public:
+ HighlighterLanguage const aLanguage;
+
+ explicit Tokenizer( HighlighterLanguage aLang );
+
+ void getHighlightPortions(std::u16string_view rLine,
+ /*out*/std::vector<HighlightPortion>& portions) const;
+ void setKeyWords( const char** ppKeyWords, sal_uInt16 nCount );
+};
+
+// Helper function: test character flag
+bool SyntaxHighlighter::Tokenizer::testCharFlags(sal_Unicode c, CharFlags nTestFlags) const
+{
+ bool bRet = false;
+ if( c != 0 && c <= 255 )
+ {
+ bRet = bool(aCharTypeTab[c] & nTestFlags);
+ }
+ else if( c > 255 )
+ {
+ bRet = (( CharFlags::StartIdentifier | CharFlags::InIdentifier ) & nTestFlags)
+ && isAlpha(c);
+ }
+ return bRet;
+}
+
+void SyntaxHighlighter::Tokenizer::setKeyWords( const char** ppKeyWords, sal_uInt16 nCount )
+{
+ ppListKeyWords = ppKeyWords;
+ nKeyWordCount = nCount;
+}
+
+bool SyntaxHighlighter::Tokenizer::getNextToken(std::u16string_view::const_iterator& pos, std::u16string_view::const_iterator end,
+ /*out*/TokenType& reType,
+ /*out*/std::u16string_view::const_iterator& rpStartPos, /*out*/std::u16string_view::const_iterator& rpEndPos) const
+{
+ reType = TokenType::Unknown;
+
+ rpStartPos = pos;
+
+ if( pos == end )
+ return false;
+
+ sal_Unicode c = *pos;
+ ++pos;
+
+ //*** Go through all possibilities ***
+ // Space?
+ if ( testCharFlags( c, CharFlags::Space ) )
+ {
+ while( pos != end && testCharFlags( *pos, CharFlags::Space ) )
+ ++pos;
+
+ reType = TokenType::Whitespace;
+ }
+
+ // Identifier?
+ else if ( testCharFlags( c, CharFlags::StartIdentifier ) )
+ {
+ bool bIdentifierChar;
+ do
+ {
+ if (pos == end)
+ break;
+ // Fetch next character
+ c = *pos;
+ bIdentifierChar = testCharFlags( c, CharFlags::InIdentifier );
+ if( bIdentifierChar )
+ ++pos;
+ }
+ while( bIdentifierChar );
+
+ reType = TokenType::Identifier;
+
+ // Keyword table
+ if (ppListKeyWords != nullptr)
+ {
+ int nCount = pos - rpStartPos;
+
+ // No keyword if string contains char > 255
+ bool bCanBeKeyword = true;
+ for( int i = 0 ; i < nCount ; i++ )
+ {
+ if( rpStartPos[i] > 255 )
+ {
+ bCanBeKeyword = false;
+ break;
+ }
+ }
+
+ if( bCanBeKeyword )
+ {
+ std::u16string_view aKWString(&*rpStartPos, nCount);
+ OString aByteStr = OUStringToOString(aKWString,
+ RTL_TEXTENCODING_ASCII_US).toAsciiLowerCase();
+ if ( bsearch( aByteStr.getStr(), ppListKeyWords, nKeyWordCount, sizeof( char* ),
+ compare_strings ) )
+ {
+ reType = TokenType::Keywords;
+
+ if( aByteStr == "rem" )
+ {
+ // Remove all characters until end of line or EOF
+ for (;;)
+ {
+ if (pos == end)
+ break;
+ sal_Unicode cPeek = *pos;
+ if ( testCharFlags( cPeek, CharFlags::EOL ) )
+ break;
+ ++pos;
+ }
+
+ reType = TokenType::Comment;
+ }
+ }
+ }
+ }
+ }
+
+ // Operator?
+ // only for BASIC '\'' should be a comment, otherwise it is a normal string and handled there
+ else if ( testCharFlags( c, CharFlags::Operator ) || ( (c == '\'') && (aLanguage==HighlighterLanguage::Basic)) )
+ {
+ // parameters for SQL view
+ if (((c==':') || (c=='?')) && (aLanguage == HighlighterLanguage::SQL))
+ {
+ if (c!='?')
+ {
+ bool bIdentifierChar;
+ do
+ {
+ // Get next character
+ if (pos == end)
+ break;
+ c = *pos;
+ bIdentifierChar = isAlpha(c);
+ if( bIdentifierChar )
+ ++pos;
+ }
+ while( bIdentifierChar );
+ }
+ reType = TokenType::Parameter;
+ }
+ else if ((c=='-') && (aLanguage == HighlighterLanguage::SQL))
+ {
+ if (pos != end && *pos=='-')
+ {
+ // Remove all characters until end of line or EOF
+ while( pos != end && !testCharFlags( *pos, CharFlags::EOL ) )
+ {
+ ++pos;
+ }
+ reType = TokenType::Comment;
+ }
+ else
+ reType = TokenType::Operator;
+ }
+ else if ((c=='/') && (aLanguage == HighlighterLanguage::SQL))
+ {
+ if (pos != end && *pos=='/')
+ {
+ // Remove all characters until end of line or EOF
+ while( pos != end && !testCharFlags( *pos, CharFlags::EOL ) )
+ {
+ ++pos;
+ }
+ reType = TokenType::Comment;
+ }
+ else
+ reType = TokenType::Operator;
+ }
+ else
+ {
+ // Apostrophe is Basic comment
+ if (( c == '\'') && (aLanguage == HighlighterLanguage::Basic))
+ {
+ // Skip all characters until end of input or end of line:
+ for (;;) {
+ if (pos == end)
+ break;
+ c = *pos;
+ if (testCharFlags(c, CharFlags::EOL)) {
+ break;
+ }
+ ++pos;
+ }
+
+ reType = TokenType::Comment;
+ }
+
+ // The real operator; can be easily used since not the actual
+ // operator (e.g. +=) is concerned, but the fact that it is one
+ if( reType != TokenType::Comment )
+ {
+ reType = TokenType::Operator;
+ }
+
+ }
+ }
+
+ // Object separator? Must be handled before Number
+ else if( c == '.' && ( pos == end || *pos < '0' || *pos > '9' ) )
+ {
+ reType = TokenType::Operator;
+ }
+
+ // Number?
+ else if( testCharFlags( c, CharFlags::StartNumber ) )
+ {
+ reType = TokenType::Number;
+
+ // Number system, 10 = normal, it is changed for Oct/Hex
+ int nRadix = 10;
+
+ // Is it an Oct or a Hex number?
+ if( c == '&' )
+ {
+ // Octal?
+ if( pos != end && (*pos == 'o' || *pos == 'O' ))
+ {
+ // remove o
+ ++pos;
+ nRadix = 8; // Octal base
+
+ // Read all numbers
+ while( pos != end && testCharFlags( *pos, CharFlags::InOctNumber ) )
+ ++pos;
+ }
+ // Hexadecimal?
+ else if( pos != end && (*pos == 'h' || *pos == 'H' ))
+ {
+ // remove x
+ ++pos;
+ nRadix = 16; // Hexadecimal base
+
+ // Read all numbers
+ while( pos != end && testCharFlags( *pos, CharFlags::InHexNumber ) )
+ ++pos;
+ }
+ else
+ {
+ reType = TokenType::Operator;
+ }
+ }
+
+ // When it is not Oct or Hex, then it is double
+ if( reType == TokenType::Number && nRadix == 10 )
+ {
+ // Flag if the last character is an exponent
+ bool bAfterExpChar = false;
+
+ // Read all numbers
+ while( pos != end && (testCharFlags( *pos, CharFlags::InNumber ) ||
+ (bAfterExpChar && *pos == '+' ) ||
+ (bAfterExpChar && *pos == '-' ) ))
+ // After exponent +/- are OK, too
+ {
+ c = *pos++;
+ bAfterExpChar = ( c == 'e' || c == 'E' );
+ }
+ }
+ }
+
+ // String?
+ else if( testCharFlags( c, CharFlags::StartString ) )
+ {
+ // Remember which character has opened the string
+ sal_Unicode cEndString = c;
+ if( c == '[' )
+ cEndString = ']';
+
+ // Read all characters
+ while( pos == end || *pos != cEndString )
+ {
+ // Detect EOF before reading next char, so we do not lose EOF
+ if( pos == end )
+ {
+ // ERROR: unterminated string literal
+ reType = TokenType::Error;
+ break;
+ }
+ c = *pos++;
+ if( testCharFlags( c, CharFlags::EOL ) )
+ {
+ // ERROR: unterminated string literal
+ reType = TokenType::Error;
+ break;
+ }
+ }
+
+ if( reType != TokenType::Error )
+ {
+ ++pos;
+ if( cEndString == ']' )
+ reType = TokenType::Identifier;
+ else
+ reType = TokenType::String;
+ }
+ }
+
+ // End of line?
+ else if( testCharFlags( c, CharFlags::EOL ) )
+ {
+ // If another EOL character comes, read it
+ if (pos != end)
+ {
+ sal_Unicode cNext = *pos;
+ if( cNext != c && testCharFlags( cNext, CharFlags::EOL ) )
+ ++pos;
+ }
+
+ reType = TokenType::EOL;
+ }
+
+ // All other will remain TokenType::Unknown
+
+ // Save end position
+ rpEndPos = pos;
+ return true;
+}
+
+SyntaxHighlighter::Tokenizer::Tokenizer( HighlighterLanguage aLang ): aLanguage(aLang)
+{
+ // Fill character table
+ sal_uInt16 i;
+
+ // Allowed characters for identifiers
+ CharFlags nHelpMask = CharFlags::StartIdentifier | CharFlags::InIdentifier;
+ for( i = 'a' ; i <= 'z' ; i++ )
+ aCharTypeTab[i] |= nHelpMask;
+ for( i = 'A' ; i <= 'Z' ; i++ )
+ aCharTypeTab[i] |= nHelpMask;
+ aCharTypeTab[int('_')] |= nHelpMask;
+ aCharTypeTab[int('$')] |= nHelpMask;
+
+ // Digit (can be identifier and number)
+ nHelpMask = CharFlags::InIdentifier | CharFlags::StartNumber |
+ CharFlags::InNumber | CharFlags::InHexNumber;
+ for( i = '0' ; i <= '9' ; i++ )
+ aCharTypeTab[i] |= nHelpMask;
+
+ // Add e, E, . and & here manually
+ aCharTypeTab[int('e')] |= CharFlags::InNumber;
+ aCharTypeTab[int('E')] |= CharFlags::InNumber;
+ aCharTypeTab[int('.')] |= CharFlags::InNumber | CharFlags::StartNumber;
+ aCharTypeTab[int('&')] |= CharFlags::StartNumber;
+
+ // Hexadecimal digit
+ for( i = 'a' ; i <= 'f' ; i++ )
+ aCharTypeTab[i] |= CharFlags::InHexNumber;
+ for( i = 'A' ; i <= 'F' ; i++ )
+ aCharTypeTab[i] |= CharFlags::InHexNumber;
+
+ // Octal digit
+ for( i = '0' ; i <= '7' ; i++ )
+ aCharTypeTab[i] |= CharFlags::InOctNumber;
+
+ // String literal start/end characters
+ aCharTypeTab[int('\'')] |= CharFlags::StartString;
+ aCharTypeTab[int('\"')] |= CharFlags::StartString;
+ aCharTypeTab[int('[')] |= CharFlags::StartString;
+ aCharTypeTab[int('`')] |= CharFlags::StartString;
+
+ // Operator characters
+ aCharTypeTab[int('!')] |= CharFlags::Operator;
+ aCharTypeTab[int('%')] |= CharFlags::Operator;
+ // aCharTypeTab[(int)'&'] |= CharFlags::Operator; Removed because of #i14140
+ aCharTypeTab[int('(')] |= CharFlags::Operator;
+ aCharTypeTab[int(')')] |= CharFlags::Operator;
+ aCharTypeTab[int('*')] |= CharFlags::Operator;
+ aCharTypeTab[int('+')] |= CharFlags::Operator;
+ aCharTypeTab[int(',')] |= CharFlags::Operator;
+ aCharTypeTab[int('-')] |= CharFlags::Operator;
+ aCharTypeTab[int('/')] |= CharFlags::Operator;
+ aCharTypeTab[int(':')] |= CharFlags::Operator;
+ aCharTypeTab[int('<')] |= CharFlags::Operator;
+ aCharTypeTab[int('=')] |= CharFlags::Operator;
+ aCharTypeTab[int('>')] |= CharFlags::Operator;
+ aCharTypeTab[int('?')] |= CharFlags::Operator;
+ aCharTypeTab[int('^')] |= CharFlags::Operator;
+ aCharTypeTab[int('|')] |= CharFlags::Operator;
+ aCharTypeTab[int('~')] |= CharFlags::Operator;
+ aCharTypeTab[int('{')] |= CharFlags::Operator;
+ aCharTypeTab[int('}')] |= CharFlags::Operator;
+ // aCharTypeTab[(int)'['] |= CharFlags::Operator; Removed because of #i17826
+ aCharTypeTab[int(']')] |= CharFlags::Operator;
+ aCharTypeTab[int(';')] |= CharFlags::Operator;
+
+ // Space
+ aCharTypeTab[int(' ') ] |= CharFlags::Space;
+ aCharTypeTab[int('\t')] |= CharFlags::Space;
+
+ // End of line characters
+ aCharTypeTab[int('\r')] |= CharFlags::EOL;
+ aCharTypeTab[int('\n')] |= CharFlags::EOL;
+
+ ppListKeyWords = nullptr;
+ nKeyWordCount = 0;
+}
+
+void SyntaxHighlighter::Tokenizer::getHighlightPortions(std::u16string_view rLine,
+ /*out*/std::vector<HighlightPortion>& portions) const
+{
+ // Set the position to the beginning of the source string
+ auto pos = rLine.begin();
+
+ // Variables for the out parameter
+ TokenType eType;
+ std::u16string_view::const_iterator pStartPos;
+ std::u16string_view::const_iterator pEndPos;
+
+ // Loop over all the tokens
+ while( getNextToken( pos, rLine.end(), eType, pStartPos, pEndPos ) )
+ {
+ portions.emplace_back(
+ pStartPos - rLine.begin(), pEndPos - rLine.begin(), eType);
+ }
+}
+
+
+SyntaxHighlighter::SyntaxHighlighter(HighlighterLanguage language):
+ m_tokenizer(new SyntaxHighlighter::Tokenizer(language))
+{
+ switch (language)
+ {
+ case HighlighterLanguage::Basic:
+ m_tokenizer->setKeyWords( strListBasicKeyWords,
+ std::size( strListBasicKeyWords ));
+ break;
+ case HighlighterLanguage::SQL:
+ m_tokenizer->setKeyWords( strListSqlKeyWords,
+ std::size( strListSqlKeyWords ));
+ break;
+ default:
+ assert(false); // this cannot happen
+ }
+}
+
+SyntaxHighlighter::~SyntaxHighlighter() {}
+
+void SyntaxHighlighter::getHighlightPortions(std::u16string_view rLine,
+ /*out*/std::vector<HighlightPortion>& portions) const
+{
+ m_tokenizer->getHighlightPortions( rLine, portions );
+}
+
+HighlighterLanguage SyntaxHighlighter::GetLanguage() const
+{
+ return m_tokenizer->aLanguage;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/threadpool.cxx b/comphelper/source/misc/threadpool.cxx
new file mode 100644
index 0000000000..f0a71eb051
--- /dev/null
+++ b/comphelper/source/misc/threadpool.cxx
@@ -0,0 +1,394 @@
+/* -*- 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 <comphelper/threadpool.hxx>
+
+#include <com/sun/star/uno/Exception.hpp>
+#include <config_options.h>
+#include <o3tl/safeint.hxx>
+#include <sal/config.h>
+#include <sal/log.hxx>
+#include <salhelper/thread.hxx>
+#include <algorithm>
+#include <memory>
+#include <thread>
+#include <chrono>
+#include <cstddef>
+#include <comphelper/debuggerinfo.hxx>
+#include <utility>
+
+#if defined HAVE_VALGRIND_HEADERS
+#include <valgrind/memcheck.h>
+#endif
+
+#if defined(_WIN32)
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#endif
+
+namespace comphelper {
+
+/** prevent waiting for a task from inside a task */
+#if defined DBG_UTIL && (defined LINUX || defined _WIN32)
+static thread_local bool gbIsWorkerThread;
+#endif
+
+// used to group thread-tasks for waiting in waitTillDone()
+class ThreadTaskTag
+{
+ std::mutex maMutex;
+ sal_Int32 mnTasksWorking;
+ std::condition_variable maTasksComplete;
+
+public:
+ ThreadTaskTag();
+ bool isDone();
+ void waitUntilDone();
+ void onTaskWorkerDone();
+ void onTaskPushed();
+};
+
+
+class ThreadPool::ThreadWorker : public salhelper::Thread
+{
+ ThreadPool *mpPool;
+public:
+
+ explicit ThreadWorker( ThreadPool *pPool ) :
+ salhelper::Thread("thread-pool"),
+ mpPool( pPool )
+ {
+ }
+
+ virtual void execute() override
+ {
+#if defined DBG_UTIL && (defined LINUX || defined _WIN32)
+ gbIsWorkerThread = true;
+#endif
+ std::unique_lock< std::mutex > aGuard( mpPool->maMutex );
+
+ while( !mpPool->mbTerminate )
+ {
+ std::unique_ptr<ThreadTask> pTask = mpPool->popWorkLocked( aGuard, true );
+ if( pTask )
+ {
+ std::shared_ptr<ThreadTaskTag> pTag(pTask->mpTag);
+ mpPool->incBusyWorker();
+ aGuard.unlock();
+
+ pTask->exec();
+ pTask.reset();
+
+ aGuard.lock();
+ mpPool->decBusyWorker();
+ pTag->onTaskWorkerDone();
+ }
+ }
+ }
+};
+
+ThreadPool::ThreadPool(std::size_t nWorkers)
+ : mbTerminate(true)
+ , mnMaxWorkers(nWorkers)
+ , mnBusyWorkers(0)
+{
+}
+
+ThreadPool::~ThreadPool()
+{
+ // note: calling shutdown from global variable dtor blocks forever on Win7
+ // note2: there isn't enough MSVCRT left on exit to call assert() properly
+ // so these asserts just print something to stderr but exit status is
+ // still 0, but hopefully they will be more helpful on non-WNT platforms
+ assert(mbTerminate);
+ assert(maTasks.empty());
+ assert(mnBusyWorkers == 0);
+}
+
+namespace {
+
+std::shared_ptr< ThreadPool >& GetStaticThreadPool()
+{
+ static std::shared_ptr< ThreadPool > POOL =
+ []()
+ {
+ const std::size_t nThreads = ThreadPool::getPreferredConcurrency();
+ return std::make_shared< ThreadPool >( nThreads );
+ }();
+ return POOL;
+}
+
+}
+
+ThreadPool& ThreadPool::getSharedOptimalPool()
+{
+ return *GetStaticThreadPool();
+}
+
+std::size_t ThreadPool::getPreferredConcurrency()
+{
+ static std::size_t ThreadCount = []()
+ {
+ const std::size_t nHardThreads = o3tl::clamp_to_unsigned<std::size_t>(
+ std::max(std::thread::hardware_concurrency(), 1U));
+ std::size_t nThreads = nHardThreads;
+ const char *pEnv = getenv("MAX_CONCURRENCY");
+ if (pEnv != nullptr)
+ {
+ // Override with user/admin preference.
+ nThreads = o3tl::clamp_to_unsigned<std::size_t>(rtl_str_toInt32(pEnv, 10));
+ }
+
+ nThreads = std::min(nHardThreads, nThreads);
+ return std::max<std::size_t>(nThreads, 1);
+ }();
+
+ return ThreadCount;
+}
+
+// Used to order shutdown, and to ensure there are no lingering
+// threads after LibreOfficeKit pre-init.
+void ThreadPool::shutdown()
+{
+// if (mbTerminate)
+// return;
+
+ std::unique_lock< std::mutex > aGuard( maMutex );
+ shutdownLocked(aGuard);
+}
+
+void ThreadPool::shutdownLocked(std::unique_lock<std::mutex>& aGuard)
+{
+ if( maWorkers.empty() )
+ { // no threads at all -> execute the work in-line
+ std::unique_ptr<ThreadTask> pTask;
+ while ( ( pTask = popWorkLocked(aGuard, false) ) )
+ {
+ std::shared_ptr<ThreadTaskTag> pTag(pTask->mpTag);
+ pTask->exec();
+ pTag->onTaskWorkerDone();
+ }
+ }
+ else
+ {
+ while( !maTasks.empty() )
+ {
+ maTasksChanged.wait( aGuard );
+ // In the (unlikely but possible?) case pushTask() gets called meanwhile,
+ // its notify_one() call is meant to wake a up a thread and process the task.
+ // But if this code gets woken up instead, it could lead to a deadlock.
+ // Pass on the notification.
+ maTasksChanged.notify_one();
+ }
+ }
+ assert( maTasks.empty() );
+
+ // coverity[missing_lock] - on purpose
+ mbTerminate = true;
+
+ maTasksChanged.notify_all();
+
+ decltype(maWorkers) aWorkers;
+ std::swap(maWorkers, aWorkers);
+ aGuard.unlock();
+
+ while (!aWorkers.empty())
+ {
+ rtl::Reference<ThreadWorker> xWorker = aWorkers.back();
+ aWorkers.pop_back();
+ assert(std::find(aWorkers.begin(), aWorkers.end(), xWorker)
+ == aWorkers.end());
+ {
+ xWorker->join();
+ xWorker.clear();
+ }
+ }
+}
+
+void ThreadPool::pushTask( std::unique_ptr<ThreadTask> pTask )
+{
+ std::scoped_lock< std::mutex > aGuard( maMutex );
+
+ mbTerminate = false;
+
+ // Worked on tasks are already removed from maTasks, so include the count of busy workers.
+ if (maWorkers.size() < mnMaxWorkers && maWorkers.size() <= maTasks.size() + mnBusyWorkers)
+ {
+ maWorkers.push_back( new ThreadWorker( this ) );
+ maWorkers.back()->launch();
+ }
+
+ pTask->mpTag->onTaskPushed();
+ maTasks.insert( maTasks.begin(), std::move(pTask) );
+
+ maTasksChanged.notify_one();
+}
+
+std::unique_ptr<ThreadTask> ThreadPool::popWorkLocked( std::unique_lock< std::mutex > & rGuard, bool bWait )
+{
+ do
+ {
+ if( !maTasks.empty() )
+ {
+ std::unique_ptr<ThreadTask> pTask = std::move(maTasks.back());
+ maTasks.pop_back();
+ return pTask;
+ }
+ else if (!bWait || mbTerminate)
+ return nullptr;
+
+ maTasksChanged.wait( rGuard );
+
+ } while (!mbTerminate);
+
+ return nullptr;
+}
+
+void ThreadPool::incBusyWorker()
+{
+ ++mnBusyWorkers;
+}
+
+void ThreadPool::decBusyWorker()
+{
+ assert(mnBusyWorkers >= 1);
+ --mnBusyWorkers;
+}
+
+void ThreadPool::waitUntilDone(const std::shared_ptr<ThreadTaskTag>& rTag, bool bJoin)
+{
+#if defined DBG_UTIL && (defined LINUX || defined _WIN32)
+ assert(!gbIsWorkerThread && "cannot wait for tasks from inside a task");
+#endif
+ {
+ std::unique_lock< std::mutex > aGuard( maMutex );
+
+ if( maWorkers.empty() )
+ { // no threads at all -> execute the work in-line
+ while (!rTag->isDone())
+ {
+ std::unique_ptr<ThreadTask> pTask = popWorkLocked(aGuard, false);
+ if (!pTask)
+ break;
+ std::shared_ptr<ThreadTaskTag> pTag(pTask->mpTag);
+ pTask->exec();
+ pTag->onTaskWorkerDone();
+ }
+ }
+ }
+
+ rTag->waitUntilDone();
+
+ if (bJoin)
+ joinThreadsIfIdle();
+}
+
+void ThreadPool::joinThreadsIfIdle()
+{
+ std::unique_lock< std::mutex > aGuard( maMutex );
+ if (isIdle()) // check if there are still tasks from another tag
+ {
+ shutdownLocked(aGuard);
+ }
+}
+
+std::shared_ptr<ThreadTaskTag> ThreadPool::createThreadTaskTag()
+{
+ return std::make_shared<ThreadTaskTag>();
+}
+
+bool ThreadPool::isTaskTagDone(const std::shared_ptr<ThreadTaskTag>& pTag)
+{
+ return pTag->isDone();
+}
+
+ThreadTask::ThreadTask(std::shared_ptr<ThreadTaskTag> xTag)
+ : mpTag(std::move(xTag))
+{
+}
+
+void ThreadTask::exec()
+{
+ try {
+ doWork();
+ }
+ catch (const std::exception &e)
+ {
+ SAL_WARN("comphelper", "exception in thread worker while calling doWork(): " << e.what());
+ }
+ catch (const css::uno::Exception &e)
+ {
+ SAL_WARN("comphelper", "exception in thread worker while calling doWork(): " << e);
+ }
+ catch (...)
+ {
+ SAL_WARN("comphelper", "unknown exception in thread worker while calling doWork()");
+ }
+}
+
+ThreadTaskTag::ThreadTaskTag() : mnTasksWorking(0)
+{
+}
+
+void ThreadTaskTag::onTaskPushed()
+{
+ std::scoped_lock< std::mutex > aGuard( maMutex );
+ mnTasksWorking++;
+ assert( mnTasksWorking < 65536 ); // sanity checking
+}
+
+void ThreadTaskTag::onTaskWorkerDone()
+{
+ std::scoped_lock< std::mutex > aGuard( maMutex );
+ mnTasksWorking--;
+ assert(mnTasksWorking >= 0);
+ if (mnTasksWorking == 0)
+ maTasksComplete.notify_all();
+}
+
+bool ThreadTaskTag::isDone()
+{
+ std::scoped_lock< std::mutex > aGuard( maMutex );
+ return mnTasksWorking == 0;
+}
+
+void ThreadTaskTag::waitUntilDone()
+{
+ std::unique_lock< std::mutex > aGuard( maMutex );
+ while( mnTasksWorking > 0 )
+ {
+#if defined DBG_UTIL && !defined NDEBUG
+ // 10 minute timeout in debug mode, unless the code is built with
+ // sanitizers or debugged in valgrind or gdb, in which case the threads
+ // should not time out in the middle of a debugging session
+ int maxTimeout = 10 * 60;
+#if !ENABLE_RUNTIME_OPTIMIZATIONS
+ maxTimeout = 30 * 60;
+#endif
+#if defined HAVE_VALGRIND_HEADERS
+ if( RUNNING_ON_VALGRIND )
+ maxTimeout = 30 * 60;
+#endif
+ if( isDebuggerAttached())
+ maxTimeout = 300 * 60;
+ std::cv_status result = maTasksComplete.wait_for(
+ aGuard, std::chrono::seconds( maxTimeout ));
+ assert(result != std::cv_status::timeout);
+#else
+ // 10 minute timeout in production so the app eventually throws some kind of error
+ if (maTasksComplete.wait_for(
+ aGuard, std::chrono::seconds( 10 * 60 )) == std::cv_status::timeout)
+ throw std::runtime_error("timeout waiting for threadpool tasks");
+#endif
+ }
+}
+
+} // namespace comphelper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/traceevent.cxx b/comphelper/source/misc/traceevent.cxx
new file mode 100644
index 0000000000..1296404ebd
--- /dev/null
+++ b/comphelper/source/misc/traceevent.cxx
@@ -0,0 +1,145 @@
+/* -*- 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 <atomic>
+#include <mutex>
+#include <iostream>
+
+#include <comphelper/profilezone.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/traceevent.hxx>
+
+namespace comphelper
+{
+#ifdef DBG_UTIL
+std::atomic<bool> TraceEvent::s_bRecording = (getenv("TRACE_EVENT_RECORDING") != nullptr);
+#else
+std::atomic<bool> TraceEvent::s_bRecording = false;
+#endif
+
+std::size_t TraceEvent::s_nBufferSize = 0;
+void (*TraceEvent::s_pBufferFullCallback)() = nullptr;
+
+int AsyncEvent::s_nIdCounter = 0;
+
+static thread_local int nProfileZoneNesting = 0; // Level of Nested Profile Zones
+
+namespace
+{
+std::vector<OUString> g_aRecording; // recorded data
+std::mutex g_aMutex;
+}
+
+void TraceEvent::addRecording(const OUString& sObject)
+{
+ bool bEmitCallback;
+ {
+ std::lock_guard aGuard(g_aMutex);
+
+ g_aRecording.emplace_back(sObject);
+
+ bEmitCallback = s_nBufferSize > 0 && g_aRecording.size() >= s_nBufferSize;
+ }
+ if (bEmitCallback && s_pBufferFullCallback != nullptr)
+ (*s_pBufferFullCallback)();
+}
+
+void TraceEvent::addInstantEvent(const char* sName, const std::map<OUString, OUString>& args)
+{
+ long long nNow = getNow();
+
+ int nPid = 0;
+ oslProcessInfo aProcessInfo;
+ aProcessInfo.Size = sizeof(oslProcessInfo);
+ if (osl_getProcessInfo(nullptr, osl_Process_IDENTIFIER, &aProcessInfo) == osl_Process_E_None)
+ nPid = aProcessInfo.Ident;
+
+ addRecording("{"
+ "\"name:\""
+ + OUString(sName, strlen(sName), RTL_TEXTENCODING_UTF8)
+ + "\","
+ "\"ph\":\"i\""
+ + createArgsString(args) + ",\"ts\":" + OUString::number(nNow)
+ + ","
+ "\"pid\":"
+ + OUString::number(nPid)
+ + ","
+ "\"tid\":"
+ + OUString::number(osl_getThreadIdentifier(nullptr)) + "},");
+}
+
+void TraceEvent::startRecording()
+{
+ std::lock_guard aGuard(g_aMutex);
+ s_bRecording = true;
+}
+
+void TraceEvent::stopRecording() { s_bRecording = false; }
+
+void TraceEvent::setBufferSizeAndCallback(std::size_t bufferSize, void (*bufferFullCallback)())
+{
+ s_nBufferSize = bufferSize;
+ s_pBufferFullCallback = bufferFullCallback;
+}
+
+std::vector<OUString> TraceEvent::getEventVectorAndClear()
+{
+ bool bRecording;
+ std::vector<OUString> aRecording;
+ {
+ std::lock_guard aGuard(g_aMutex);
+ bRecording = s_bRecording;
+ stopRecording();
+ aRecording.swap(g_aRecording);
+ }
+ // reset start time and nesting level
+ if (bRecording)
+ startRecording();
+ return aRecording;
+}
+
+css::uno::Sequence<OUString> TraceEvent::getRecordingAndClear()
+{
+ return comphelper::containerToSequence(getEventVectorAndClear());
+}
+
+void ProfileZone::addRecording()
+{
+ assert(s_bRecording);
+
+ long long nNow = getNow();
+
+ // Generate a single "Complete Event" (type X)
+ TraceEvent::addRecording("{"
+ "\"name\":\""
+ + OUString(m_sName, strlen(m_sName), RTL_TEXTENCODING_UTF8)
+ + "\","
+ "\"ph\":\"X\","
+ "\"ts\":"
+ + OUString::number(m_nCreateTime)
+ + ","
+ "\"dur\":"
+ + OUString::number(nNow - m_nCreateTime) + m_sArgs
+ + ","
+ "\"pid\":"
+ + OUString::number(m_nPid)
+ + ","
+ "\"tid\":"
+ + OUString::number(osl_getThreadIdentifier(nullptr)) + "},");
+}
+
+int ProfileZone::getNestingLevel() { return nProfileZoneNesting; }
+
+void ProfileZone::setNestingLevel(int nNestingLevel) { nProfileZoneNesting = nNestingLevel; }
+
+} // namespace comphelper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/typedescriptionref.hxx b/comphelper/source/misc/typedescriptionref.hxx
new file mode 100644
index 0000000000..f4580cb2e4
--- /dev/null
+++ b/comphelper/source/misc/typedescriptionref.hxx
@@ -0,0 +1,51 @@
+/* -*- 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 <typelib/typedescription.h>
+
+namespace comphelper::detail
+{
+// This is like com::sun::star::uno::TypeDescription, but it uses TYPELIB_DANGER_GET
+// (which the code used originally, but it's easier to have a class to handle ownership).
+class TypeDescriptionRef
+{
+public:
+ TypeDescriptionRef(typelib_TypeDescriptionReference* typeDef)
+ {
+ TYPELIB_DANGER_GET(&typeDescr, typeDef);
+ }
+ ~TypeDescriptionRef() { TYPELIB_DANGER_RELEASE(typeDescr); }
+ typelib_TypeDescription* get() { return typeDescr; }
+ typelib_TypeDescription* operator->() { return typeDescr; }
+ bool is() { return typeDescr != nullptr; }
+ bool equals(const TypeDescriptionRef& other) const
+ {
+ return typeDescr && other.typeDescr
+ && typelib_typedescription_equals(typeDescr, other.typeDescr);
+ }
+
+private:
+ typelib_TypeDescription* typeDescr = nullptr;
+};
+
+} // namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/types.cxx b/comphelper/source/misc/types.cxx
new file mode 100644
index 0000000000..8887d5b5ac
--- /dev/null
+++ b/comphelper/source/misc/types.cxx
@@ -0,0 +1,146 @@
+/* -*- 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 <comphelper/types.hxx>
+#include <comphelper/extract.hxx>
+#include <com/sun/star/awt/FontUnderline.hpp>
+#include <com/sun/star/awt/FontStrikeout.hpp>
+#include <com/sun/star/awt/FontDescriptor.hpp>
+#include <o3tl/any.hxx>
+#include <osl/diagnose.h>
+#include <typelib/typedescription.hxx>
+#include <sal/log.hxx>
+
+namespace comphelper
+{
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::lang;
+
+sal_Int64 getINT64(const Any& _rAny)
+{
+ sal_Int64 nReturn = 0;
+ if (!(_rAny >>= nReturn))
+ SAL_WARN("comphelper", "conversion from Any to sal_Int64 failed");
+ return nReturn;
+}
+
+sal_Int32 getINT32(const Any& _rAny)
+{
+ sal_Int32 nReturn = 0;
+ if (!(_rAny >>= nReturn))
+ SAL_WARN("comphelper", "conversion from Any to sal_Int32 failed");
+ return nReturn;
+}
+
+sal_Int16 getINT16(const Any& _rAny)
+{
+ sal_Int16 nReturn = 0;
+ if (!(_rAny >>= nReturn))
+ SAL_WARN("comphelper", "conversion from Any to sal_Int16 failed");
+ return nReturn;
+}
+
+double getDouble(const Any& _rAny)
+{
+ double nReturn = 0.0;
+ if (!(_rAny >>= nReturn))
+ SAL_WARN("comphelper", "conversion from Any to double failed");
+ return nReturn;
+}
+
+float getFloat(const Any& _rAny)
+{
+ float nReturn = 0.0;
+ if (!(_rAny >>= nReturn))
+ SAL_WARN("comphelper", "conversion from Any to float failed");
+ return nReturn;
+}
+
+OUString getString(const Any& _rAny)
+{
+ OUString nReturn;
+ if (!(_rAny >>= nReturn))
+ SAL_WARN("comphelper", "conversion from Any to OUString failed");
+ return nReturn;
+}
+
+bool getBOOL(const Any& _rAny)
+{
+ bool bReturn = false;
+ if (auto b = o3tl::tryAccess<bool>(_rAny))
+ bReturn = *b;
+ else
+ OSL_FAIL("comphelper::getBOOL : invalid argument !");
+ return bReturn;
+}
+
+sal_Int32 getEnumAsINT32(const Any& _rAny)
+{
+ sal_Int32 nReturn = 0;
+ if (!::cppu::enum2int(nReturn, _rAny))
+ throw IllegalArgumentException("enum2int failed",
+ css::uno::Reference<css::uno::XInterface>(), -1);
+ return nReturn;
+}
+
+FontDescriptor getDefaultFont()
+{
+ FontDescriptor aReturn;
+ aReturn.Slant = FontSlant_DONTKNOW;
+ aReturn.Underline = FontUnderline::DONTKNOW;
+ aReturn.Strikeout = com::sun::star::awt::FontStrikeout::DONTKNOW;
+ return aReturn;
+}
+
+bool isAssignableFrom(const Type& _rAssignable, const Type& _rFrom)
+{
+ // get the type lib descriptions
+ typelib_TypeDescription* pAssignable = nullptr;
+ _rAssignable.getDescription(&pAssignable);
+
+ typelib_TypeDescription* pFrom = nullptr;
+ _rFrom.getDescription(&pFrom);
+
+ // and ask the type lib
+ return typelib_typedescription_isAssignableFrom(pAssignable, pFrom);
+}
+
+Type getSequenceElementType(const Type& _rSequenceType)
+{
+ OSL_ENSURE(_rSequenceType.getTypeClass() == TypeClass_SEQUENCE,
+ "getSequenceElementType: must be called with a sequence type!");
+
+ if (_rSequenceType.getTypeClass() != TypeClass_SEQUENCE)
+ return Type();
+
+ TypeDescription aTD(_rSequenceType);
+ typelib_IndirectTypeDescription* pSequenceTD
+ = reinterpret_cast<typelib_IndirectTypeDescription*>(aTD.get());
+
+ OSL_ASSERT(pSequenceTD && pSequenceTD->pType);
+ if (pSequenceTD && pSequenceTD->pType)
+ return Type(pSequenceTD->pType);
+
+ return Type();
+}
+
+} // namespace comphelper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/weakeventlistener.cxx b/comphelper/source/misc/weakeventlistener.cxx
new file mode 100644
index 0000000000..0543816d05
--- /dev/null
+++ b/comphelper/source/misc/weakeventlistener.cxx
@@ -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 .
+ */
+
+#include <comphelper/weakeventlistener.hxx>
+#include <osl/diagnose.h>
+
+
+namespace comphelper
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+
+ OWeakListenerAdapterBase::~OWeakListenerAdapterBase()
+ {
+ }
+
+ OWeakEventListenerAdapter::OWeakEventListenerAdapter( Reference< XWeak > const & _rxListener, Reference< XComponent > const & _rxBroadcaster )
+ :OWeakEventListenerAdapter_Base( _rxListener, _rxBroadcaster )
+ {
+ // add ourself as listener to the broadcaster
+ OSL_ENSURE( _rxBroadcaster.is(), "OWeakEventListenerAdapter::OWeakEventListenerAdapter: invalid broadcaster!" );
+ if ( _rxBroadcaster.is() )
+ {
+ osl_atomic_increment( &m_refCount );
+ {
+ _rxBroadcaster->addEventListener( this );
+ }
+ osl_atomic_decrement( &m_refCount );
+ OSL_ENSURE( m_refCount > 0, "OWeakEventListenerAdapter::OWeakEventListenerAdapter: oops - not to be used with implementations which hold their listeners weak!" );
+ // the one and only reason for this adapter class (A) is to add as listener to a component (C) which
+ // holds its listeners hard, and forward all calls then to another listener (L) which is
+ // held weak by A.
+ // Now if C holds listeners weak, then we do not need A, we can add L directly to C.
+ }
+
+ OSL_ENSURE( getListener().is(), "OWeakEventListenerAdapter::OWeakEventListenerAdapter: invalid listener (does not support the XEventListener interface)!" );
+ }
+
+
+ void OWeakEventListenerAdapter::disposing( std::unique_lock<std::mutex>& /*rGuard*/ )
+ {
+ Reference< XComponent > xBroadcaster( getBroadcaster( ), UNO_QUERY );
+ OSL_ENSURE( xBroadcaster.is(), "OWeakEventListenerAdapter::disposing: broadcaster is invalid in the meantime! How this?" );
+ if ( xBroadcaster.is() )
+ {
+ xBroadcaster->removeEventListener( this );
+ }
+
+ resetListener();
+ }
+
+
+} // namespace comphelper
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/xmlsechelper.cxx b/comphelper/source/misc/xmlsechelper.cxx
new file mode 100644
index 0000000000..5b1a438abb
--- /dev/null
+++ b/comphelper/source/misc/xmlsechelper.cxx
@@ -0,0 +1,319 @@
+/* -*- 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 <comphelper/xmlsechelper.hxx>
+
+#include <rtl/ustrbuf.hxx>
+#include <osl/diagnose.h>
+#include <o3tl/string_view.hxx>
+
+#include <utility>
+#include <vector>
+
+namespace comphelper::xmlsec
+{
+ OUString GetCertificateKind( const css::security::CertificateKind &rKind )
+ {
+ switch (rKind)
+ {
+ case css::security::CertificateKind_X509:
+ return "X.509";
+ case css::security::CertificateKind_OPENPGP:
+ return "OpenPGP";
+ default:
+ return OUString();
+ }
+ }
+
+ /*
+ Creates two strings based on the distinguished name which are displayed in the
+ certificate details view. The first string contains only the values of the attribute
+ and values pairs, which are separated by commas. All escape characters ('"') are
+ removed.
+ The second string is for the details view at the bottom. It shows the attribute/value
+ pairs on different lines. All escape characters ('"') are removed.
+ */
+ std::pair< OUString, OUString> GetDNForCertDetailsView( std::u16string_view rRawString)
+ {
+ std::vector< std::pair< OUString, OUString > > vecAttrValueOfDN = parseDN(rRawString);
+ OUStringBuffer s1, s2;
+ for (auto i = vecAttrValueOfDN.cbegin(); i < vecAttrValueOfDN.cend(); ++i)
+ {
+ if (i != vecAttrValueOfDN.cbegin())
+ {
+ s1.append(',');
+ s2.append('\n');
+ }
+ s1.append(i->second);
+ s2.append(i->first + " = " + i->second);
+ }
+ return std::make_pair(s1.makeStringAndClear(), s2.makeStringAndClear());
+ }
+
+/*
+ Whenever the attribute value contains special characters, such as '"' or ',' (without '')
+ then the value will be enclosed in double quotes by the respective Windows or NSS function
+ which we use to retrieve, for example, the subject name. If double quotes appear in the value then
+ they are escaped with a double quote. This function removes the escape characters.
+*/
+#ifdef _WIN32
+std::vector< std::pair< OUString, OUString> > parseDN(std::u16string_view rRawString)
+{
+ std::vector< std::pair<OUString, OUString> > retVal;
+ bool bInEscape = false;
+ bool bInValue = false;
+ bool bInType = true;
+ sal_Int32 nTypeNameStart = 0;
+ std::u16string_view sType;
+ OUStringBuffer sbufValue;
+ size_t length = rRawString.size();
+
+ for (size_t i = 0; i < length; i++)
+ {
+ sal_Unicode c = rRawString[i];
+
+ if (c == '=')
+ {
+ if (! bInValue)
+ {
+ sType = rRawString.substr(nTypeNameStart, i - nTypeNameStart);
+ sType = o3tl::trim(sType);
+ bInType = false;
+ }
+ else
+ {
+ sbufValue.append(c);
+ }
+ }
+ else if (c == '"')
+ {
+ if (!bInEscape)
+ {
+ //If this is the quote is the first of the couple which enclose the
+ //whole value, because the value contains special characters
+ //then we just drop it. That is, this character must be followed by
+ //a character which is not '"'.
+ if ( i + 1 < length && rRawString[i+1] == '"')
+ bInEscape = true;
+ else
+ bInValue = !bInValue; //value is enclosed in " "
+ }
+ else
+ {
+ //This quote is escaped by a preceding quote and therefore is
+ //part of the value
+ sbufValue.append(c);
+ bInEscape = false;
+ }
+ }
+ else if (c == ',' || c == '+')
+ {
+ //The comma separate the attribute value pairs.
+ //If the comma is not part of a value (the value would then be enclosed in '"'),
+ //then we have reached the end of the value
+ if (!bInValue)
+ {
+ OSL_ASSERT(!sType.empty());
+ retVal.push_back(std::make_pair(OUString(sType), sbufValue.makeStringAndClear()));
+ sType = {};
+ //The next char is the start of the new type
+ nTypeNameStart = i + 1;
+ bInType = true;
+ }
+ else
+ {
+ //The whole string is enclosed because it contains special characters.
+ //The enclosing '"' are not part of certificate but will be added by
+ //the function (Windows or NSS) which retrieves DN
+ sbufValue.append(c);
+ }
+ }
+ else
+ {
+ if (!bInType)
+ sbufValue.append(c);
+ }
+ }
+ if (sbufValue.getLength())
+ {
+ OSL_ASSERT(!sType.empty());
+ retVal.push_back(std::make_pair(OUString(sType), sbufValue.makeStringAndClear()));
+ }
+ return retVal;
+ }
+#else
+std::vector< std::pair< OUString, OUString> > parseDN(std::u16string_view rRawString)
+ {
+ std::vector< std::pair<OUString, OUString> > retVal;
+ //bInEscape == true means that the preceding character is an escape character
+ bool bInEscape = false;
+ bool bInValue = false;
+ bool bInType = true;
+ sal_Int32 nTypeNameStart = 0;
+ std::u16string_view sType;
+ OUStringBuffer sbufValue;
+ size_t length = rRawString.size();
+
+ for (size_t i = 0; i < length; i++)
+ {
+ sal_Unicode c = rRawString[i];
+
+ if (c == '=')
+ {
+ if (! bInValue)
+ {
+ sType = rRawString.substr(nTypeNameStart, i - nTypeNameStart);
+ sType = o3tl::trim(sType);
+ bInType = false;
+ }
+ else
+ {
+ sbufValue.append(c);
+ }
+ }
+ else if (c == '\\')
+ {
+ if (!bInEscape)
+ {
+ bInEscape = true;
+ }
+ else
+ { // bInEscape is true
+ sbufValue.append(c);
+ bInEscape = false;
+ }
+ }
+ else if (c == '"')
+ {
+ //an unescaped '"' is either at the beginning or end of the value
+ if (!bInEscape)
+ {
+ if ( !bInValue)
+ bInValue = true;
+ else if (bInValue)
+ bInValue = false;
+ }
+ else
+ {
+ //This quote is escaped by a preceding quote and therefore is
+ //part of the value
+ sbufValue.append(c);
+ bInEscape = false;
+ }
+ }
+ else if (c == ',' || c == '+')
+ {
+ //The comma separate the attribute value pairs.
+ //If the comma is not part of a value (the value would then be enclosed in '"'),
+ //then we have reached the end of the value
+ if (!bInValue)
+ {
+ OSL_ASSERT(!sType.empty());
+ retVal.emplace_back(sType, sbufValue.makeStringAndClear());
+ sType = {};
+ //The next char is the start of the new type
+ nTypeNameStart = i + 1;
+ bInType = true;
+ }
+ else
+ {
+ //The whole string is enclosed because it contains special characters.
+ //The enclosing '"' are not part of certificate but will be added by
+ //the function (Windows or NSS) which retrieves DN
+ sbufValue.append(c);
+ }
+ }
+ else
+ {
+ if (!bInType)
+ {
+ sbufValue.append(c);
+ bInEscape = false;
+ }
+ }
+ }
+ if (!sbufValue.isEmpty())
+ {
+ OSL_ASSERT(!sType.empty());
+ retVal.emplace_back(sType, sbufValue.makeStringAndClear());
+ }
+ return retVal;
+ }
+
+#endif
+
+ OUString GetContentPart( const OUString& _rRawString, const css::security::CertificateKind &rKind )
+ {
+ char const * aIDs[] = { "CN", "OU", "O", "E", nullptr };
+
+ // tdf#131733 Don't process OpenPGP certs, only X509
+ if (rKind == css::security::CertificateKind_OPENPGP )
+ return _rRawString;
+
+ OUString retVal;
+ int i = 0;
+ std::vector< std::pair< OUString, OUString > > vecAttrValueOfDN = parseDN(_rRawString);
+ while ( aIDs[i] )
+ {
+ OUString sPartId = OUString::createFromAscii( aIDs[i++] );
+ auto idn = std::find_if(vecAttrValueOfDN.cbegin(), vecAttrValueOfDN.cend(),
+ [&sPartId](const std::pair< OUString, OUString >& dn) { return dn.first == sPartId; });
+ if (idn != vecAttrValueOfDN.cend())
+ retVal = idn->second;
+ if (!retVal.isEmpty())
+ break;
+ }
+ return retVal.isEmpty() ? _rRawString : retVal;
+ }
+
+ OUString GetHexString( const css::uno::Sequence< sal_Int8 >& _rSeq, const char* _pSep, sal_uInt16 _nLineBreak )
+ {
+ const sal_Int8* pSerNumSeq = _rSeq.getConstArray();
+ int nCnt = _rSeq.getLength();
+ OUStringBuffer aStr;
+ const char pHexDigs[ 17 ] = "0123456789ABCDEF";
+ char pBuffer[ 3 ] = " ";
+ sal_uInt8 nNum;
+ sal_uInt16 nBreakStart = _nLineBreak? _nLineBreak : 1;
+ sal_uInt16 nBreak = nBreakStart;
+ for( int i = 0 ; i < nCnt ; ++i )
+ {
+ nNum = sal_uInt8( pSerNumSeq[ i ] );
+
+ // exchange the buffer[0] and buffer[1], which make it consistent with Mozilla and Windows
+ pBuffer[ 1 ] = pHexDigs[ nNum & 0x0F ];
+ nNum >>= 4;
+ pBuffer[ 0 ] = pHexDigs[ nNum ];
+ aStr.appendAscii( pBuffer );
+
+ --nBreak;
+ if( nBreak )
+ aStr.appendAscii( _pSep );
+ else
+ {
+ nBreak = nBreakStart;
+ aStr.append( '\n' );
+ }
+ }
+
+ return aStr.makeStringAndClear();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/officeinstdir/officeinstallationdirectories.cxx b/comphelper/source/officeinstdir/officeinstallationdirectories.cxx
new file mode 100644
index 0000000000..010655964d
--- /dev/null
+++ b/comphelper/source/officeinstdir/officeinstallationdirectories.cxx
@@ -0,0 +1,249 @@
+/* -*- 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 <config_folders.h>
+
+#include <cppuhelper/supportsservice.hxx>
+
+/**************************************************************************
+ TODO
+ **************************************************************************
+
+ *************************************************************************/
+
+#include <osl/file.hxx>
+#include <com/sun/star/util/theMacroExpander.hpp>
+#include <comphelper/fileurl.hxx>
+#include <utility>
+
+#include "officeinstallationdirectories.hxx"
+
+using namespace com::sun::star;
+
+static bool makeCanonicalFileURL( OUString & rURL )
+{
+ OSL_ENSURE(comphelper::isFileUrl(rURL), "File URL expected!");
+
+ OUString aNormalizedURL;
+ if ( osl::FileBase::getAbsoluteFileURL( OUString(),
+ rURL,
+ aNormalizedURL )
+ != osl::DirectoryItem::E_None )
+ return false;
+
+ osl::DirectoryItem aDirItem;
+ if ( osl::DirectoryItem::get( aNormalizedURL, aDirItem )
+ != osl::DirectoryItem::E_None )
+ return false;
+
+ osl::FileStatus aFileStatus( osl_FileStatus_Mask_FileURL );
+
+ if ( aDirItem.getFileStatus( aFileStatus )
+ == osl::DirectoryItem::E_None )
+ {
+ aNormalizedURL = aFileStatus.getFileURL();
+
+ if ( !aNormalizedURL.isEmpty() )
+ {
+ if ( !aNormalizedURL.endsWith("/") )
+ rURL = aNormalizedURL;
+ else
+ rURL = aNormalizedURL
+ .copy( 0, aNormalizedURL.getLength() - 1 );
+
+ return true;
+ }
+ }
+ return false;
+}
+
+namespace comphelper {
+
+constexpr OUString g_aOfficeBrandDirMacro(u"$(brandbaseurl)"_ustr);
+constexpr OUString g_aUserDirMacro(u"$(userdataurl)"_ustr);
+
+OfficeInstallationDirectories::OfficeInstallationDirectories(
+ uno::Reference< uno::XComponentContext > xCtx )
+: m_xCtx(std::move( xCtx ))
+{
+}
+
+
+// virtual
+OfficeInstallationDirectories::~OfficeInstallationDirectories()
+{
+}
+
+
+// util::XOfficeInstallationDirectories
+
+
+// virtual
+OUString SAL_CALL
+OfficeInstallationDirectories::getOfficeInstallationDirectoryURL()
+{
+ initDirs();
+ return *m_xOfficeBrandDir;
+}
+
+
+// virtual
+OUString SAL_CALL
+OfficeInstallationDirectories::getOfficeUserDataDirectoryURL()
+{
+ initDirs();
+ return *m_xUserDir;
+}
+
+
+
+// virtual
+OUString SAL_CALL
+OfficeInstallationDirectories::makeRelocatableURL( const OUString& URL )
+{
+ if ( !URL.isEmpty() )
+ {
+ initDirs();
+
+ OUString aCanonicalURL( URL );
+ makeCanonicalFileURL( aCanonicalURL );
+
+ sal_Int32 nIndex = aCanonicalURL.indexOf( *m_xOfficeBrandDir );
+ if ( nIndex != -1 )
+ {
+ return
+ aCanonicalURL.replaceAt( nIndex,
+ m_xOfficeBrandDir->getLength(),
+ g_aOfficeBrandDirMacro );
+ }
+ else
+ {
+ nIndex = aCanonicalURL.indexOf( *m_xUserDir );
+ if ( nIndex != -1 )
+ {
+ return
+ aCanonicalURL.replaceAt( nIndex,
+ m_xUserDir->getLength(),
+ g_aUserDirMacro );
+ }
+ }
+ }
+ return URL;
+}
+
+
+// virtual
+OUString SAL_CALL
+OfficeInstallationDirectories::makeAbsoluteURL( const OUString& URL )
+{
+ if ( !URL.isEmpty() )
+ {
+ sal_Int32 nIndex = URL.indexOf( g_aOfficeBrandDirMacro );
+ if ( nIndex != -1 )
+ {
+ initDirs();
+
+ return
+ URL.replaceAt( nIndex,
+ g_aOfficeBrandDirMacro.getLength(),
+ *m_xOfficeBrandDir );
+ }
+ else
+ {
+ nIndex = URL.indexOf( g_aUserDirMacro );
+ if ( nIndex != -1 )
+ {
+ initDirs();
+
+ return
+ URL.replaceAt( nIndex,
+ g_aUserDirMacro.getLength(),
+ *m_xUserDir );
+ }
+ }
+ }
+ return URL;
+}
+
+
+// lang::XServiceInfo
+
+
+// virtual
+OUString SAL_CALL
+OfficeInstallationDirectories::getImplementationName()
+{
+ return "com.sun.star.comp.util.OfficeInstallationDirectories";
+}
+
+// virtual
+sal_Bool SAL_CALL
+OfficeInstallationDirectories::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+// virtual
+uno::Sequence< OUString > SAL_CALL
+OfficeInstallationDirectories::getSupportedServiceNames()
+{
+ return { "com.sun.star.util.OfficeInstallationDirectories" };
+}
+
+void OfficeInstallationDirectories::initDirs()
+{
+ if ( m_xOfficeBrandDir)
+ return;
+
+ std::unique_lock aGuard( m_aMutex );
+ if ( m_xOfficeBrandDir )
+ return;
+
+ uno::Reference< util::XMacroExpander > xExpander = util::theMacroExpander::get(m_xCtx);
+
+ m_xOfficeBrandDir = xExpander->expandMacros( "$BRAND_BASE_DIR" );
+
+ OSL_ENSURE( !m_xOfficeBrandDir->isEmpty(),
+ "Unable to obtain office brand installation directory!" );
+
+ makeCanonicalFileURL( *m_xOfficeBrandDir );
+
+ m_xUserDir =
+ xExpander->expandMacros(
+ "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE( "bootstrap" ) ":UserInstallation}" );
+
+ OSL_ENSURE( !m_xUserDir->isEmpty(),
+ "Unable to obtain office user data directory!" );
+
+ makeCanonicalFileURL( *m_xUserDir );
+}
+
+}
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_util_OfficeInstallationDirectories(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(
+ new comphelper::OfficeInstallationDirectories(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/officeinstdir/officeinstallationdirectories.hxx b/comphelper/source/officeinstdir/officeinstallationdirectories.hxx
new file mode 100644
index 0000000000..d0d86b76c4
--- /dev/null
+++ b/comphelper/source/officeinstdir/officeinstallationdirectories.hxx
@@ -0,0 +1,76 @@
+/* -*- 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 <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/util/XOfficeInstallationDirectories.hpp>
+
+#include <mutex>
+#include <optional>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+namespace comphelper {
+
+
+
+typedef cppu::WeakImplHelper<
+ css::util::XOfficeInstallationDirectories,
+ css::lang::XServiceInfo > OfficeInstallationDirectories_Base;
+
+class OfficeInstallationDirectories : public OfficeInstallationDirectories_Base
+{
+public:
+ explicit OfficeInstallationDirectories(
+ css::uno::Reference< css::uno::XComponentContext > xCtx );
+ virtual ~OfficeInstallationDirectories() override;
+
+ // XOfficeInstallationDirectories
+ virtual OUString SAL_CALL
+ getOfficeInstallationDirectoryURL() override;
+ virtual OUString SAL_CALL
+ getOfficeUserDataDirectoryURL() override;
+ virtual OUString SAL_CALL
+ makeRelocatableURL( const OUString& URL ) override;
+ virtual OUString SAL_CALL
+ makeAbsoluteURL( const OUString& URL ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL
+ getImplementationName() override;
+ virtual sal_Bool SAL_CALL
+ supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getSupportedServiceNames() override;
+
+private:
+ void initDirs();
+
+ std::mutex m_aMutex;
+ css::uno::Reference< css::uno::XComponentContext > m_xCtx;
+ std::optional<OUString> m_xOfficeBrandDir;
+ std::optional<OUString> m_xUserDir;
+};
+
+} // namespace comphelper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/processfactory/processfactory.cxx b/comphelper/source/processfactory/processfactory.cxx
new file mode 100644
index 0000000000..c503b8ff1e
--- /dev/null
+++ b/comphelper/source/processfactory/processfactory.cxx
@@ -0,0 +1,118 @@
+/* -*- 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 <mutex>
+#include <comphelper/processfactory.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/uno/DeploymentException.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+using namespace ::com::sun::star;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace osl;
+
+namespace comphelper
+{
+
+namespace {
+
+class LocalProcessFactory {
+public:
+ void set( const Reference< XMultiServiceFactory >& xSMgr )
+ {
+ std::unique_lock aGuard( maMutex );
+
+ xProcessFactory = xSMgr;
+ }
+
+ Reference< XMultiServiceFactory > get() const
+ {
+ std::unique_lock aGuard( maMutex );
+
+ return xProcessFactory;
+ }
+
+private:
+ mutable std::mutex maMutex;
+ Reference< XMultiServiceFactory > xProcessFactory;
+};
+
+/*
+ This var preserves only that the above xProcessFactory variable will not be create when
+ the library is loaded.
+*/
+LocalProcessFactory localProcessFactory;
+
+}
+
+void setProcessServiceFactory(const Reference< XMultiServiceFactory >& xSMgr)
+{
+ localProcessFactory.set( xSMgr );
+}
+
+Reference< XMultiServiceFactory > getProcessServiceFactory()
+{
+ Reference< XMultiServiceFactory> xReturn = localProcessFactory.get();
+ if ( !xReturn.is() )
+ {
+ throw DeploymentException( "null process service factory" );
+ }
+ return xReturn;
+}
+
+Reference< XComponentContext > getComponentContext(
+ Reference< XMultiServiceFactory > const & factory)
+{
+ Reference< XComponentContext > xRet;
+ uno::Reference<beans::XPropertySet> const xProps( factory, uno::UNO_QUERY );
+ if (xProps.is()) {
+ static constexpr OUStringLiteral DEFAULT_CONTEXT = u"DefaultContext";
+ try {
+ xRet.set( xProps->getPropertyValue(DEFAULT_CONTEXT),
+ uno::UNO_QUERY );
+ }
+ catch (beans::UnknownPropertyException & e) {
+ throw DeploymentException(
+ "unknown service factory DefaultContext property: " + e.Message,
+ Reference<XInterface>(factory, UNO_QUERY) );
+ }
+ }
+ if ( !xRet.is() )
+ {
+ throw DeploymentException(
+ "no service factory DefaultContext",
+ Reference<XInterface>(factory, UNO_QUERY) );
+ }
+ return xRet;
+}
+
+Reference< XComponentContext > getProcessComponentContext()
+{
+ static const uno::Reference<XComponentContext> processComponentContext = getComponentContext( getProcessServiceFactory() );
+ return processComponentContext;
+}
+
+} // namespace comphelper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/property/ChainablePropertySet.cxx b/comphelper/source/property/ChainablePropertySet.cxx
new file mode 100644
index 0000000000..0805afe72b
--- /dev/null
+++ b/comphelper/source/property/ChainablePropertySet.cxx
@@ -0,0 +1,239 @@
+/* -*- 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 <comphelper/ChainablePropertySet.hxx>
+#include <comphelper/ChainablePropertySetInfo.hxx>
+#include <comphelper/solarmutex.hxx>
+
+
+#include <memory>
+#include <optional>
+
+using namespace ::comphelper;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::beans;
+
+ChainablePropertySet::ChainablePropertySet( comphelper::ChainablePropertySetInfo* pInfo, comphelper::SolarMutex* pMutex )
+ noexcept
+: mpMutex ( pMutex )
+, mxInfo ( pInfo )
+{
+}
+
+ChainablePropertySet::~ChainablePropertySet()
+ noexcept
+{
+}
+
+// XPropertySet
+Reference< XPropertySetInfo > SAL_CALL ChainablePropertySet::getPropertySetInfo( )
+{
+ return mxInfo;
+}
+
+void SAL_CALL ChainablePropertySet::setPropertyValue( const OUString& rPropertyName, const Any& rValue )
+{
+ // acquire mutex in c-tor and releases it in the d-tor (exception safe!).
+ std::optional< osl::Guard< comphelper::SolarMutex > > xMutexGuard;
+ if (mpMutex)
+ xMutexGuard.emplace( mpMutex );
+
+ PropertyInfoHash::const_iterator aIter = mxInfo->maMap.find ( rPropertyName );
+
+ if( aIter == mxInfo->maMap.end())
+ throw UnknownPropertyException( rPropertyName, static_cast< XPropertySet* >( this ) );
+
+ _preSetValues();
+ _setSingleValue( *((*aIter).second), rValue );
+ _postSetValues();
+}
+
+Any SAL_CALL ChainablePropertySet::getPropertyValue( const OUString& rPropertyName )
+{
+ // acquire mutex in c-tor and releases it in the d-tor (exception safe!).
+ std::optional< osl::Guard< comphelper::SolarMutex > > xMutexGuard;
+ if (mpMutex)
+ xMutexGuard.emplace( mpMutex );
+
+ PropertyInfoHash::const_iterator aIter = mxInfo->maMap.find ( rPropertyName );
+
+ if( aIter == mxInfo->maMap.end())
+ throw UnknownPropertyException( rPropertyName, static_cast< XPropertySet* >( this ) );
+
+ Any aAny;
+ _preGetValues ();
+ _getSingleValue( *((*aIter).second), aAny );
+ _postGetValues ();
+
+ return aAny;
+}
+
+void SAL_CALL ChainablePropertySet::addPropertyChangeListener( const OUString&, const Reference< XPropertyChangeListener >& )
+{
+ // todo
+}
+
+void SAL_CALL ChainablePropertySet::removePropertyChangeListener( const OUString&, const Reference< XPropertyChangeListener >& )
+{
+ // todo
+}
+
+void SAL_CALL ChainablePropertySet::addVetoableChangeListener( const OUString&, const Reference< XVetoableChangeListener >& )
+{
+ // todo
+}
+
+void SAL_CALL ChainablePropertySet::removeVetoableChangeListener( const OUString&, const Reference< XVetoableChangeListener >& )
+{
+ // todo
+}
+
+// XMultiPropertySet
+void SAL_CALL ChainablePropertySet::setPropertyValues(const Sequence< OUString >& rPropertyNames, const Sequence< Any >& rValues)
+{
+ // acquire mutex in c-tor and releases it in the d-tor (exception safe!).
+ std::optional< osl::Guard< comphelper::SolarMutex > > xMutexGuard;
+ if (mpMutex)
+ xMutexGuard.emplace( mpMutex );
+
+ const sal_Int32 nCount = rPropertyNames.getLength();
+
+ if( nCount != rValues.getLength() )
+ throw IllegalArgumentException("lengths do not match", static_cast<cppu::OWeakObject*>(this), -1);
+
+ if( !nCount )
+ return;
+
+ _preSetValues();
+
+ const Any * pAny = rValues.getConstArray();
+ const OUString * pString = rPropertyNames.getConstArray();
+ PropertyInfoHash::const_iterator aEnd = mxInfo->maMap.end(), aIter;
+
+ for ( sal_Int32 i = 0; i < nCount; ++i, ++pString, ++pAny )
+ {
+ aIter = mxInfo->maMap.find ( *pString );
+ if ( aIter == aEnd )
+ throw RuntimeException( *pString, static_cast< XPropertySet* >( this ) );
+
+ _setSingleValue ( *((*aIter).second), *pAny );
+ }
+
+ _postSetValues();
+}
+
+Sequence< Any > SAL_CALL ChainablePropertySet::getPropertyValues(const Sequence< OUString >& rPropertyNames)
+{
+ // acquire mutex in c-tor and releases it in the d-tor (exception safe!).
+ std::optional< osl::Guard< comphelper::SolarMutex > > xMutexGuard;
+ if (mpMutex)
+ xMutexGuard.emplace( mpMutex );
+
+ const sal_Int32 nCount = rPropertyNames.getLength();
+
+ Sequence < Any > aValues ( nCount );
+
+ if( nCount )
+ {
+ _preGetValues();
+
+ Any * pAny = aValues.getArray();
+ const OUString * pString = rPropertyNames.getConstArray();
+ PropertyInfoHash::const_iterator aEnd = mxInfo->maMap.end(), aIter;
+
+ for ( sal_Int32 i = 0; i < nCount; ++i, ++pString, ++pAny )
+ {
+ aIter = mxInfo->maMap.find ( *pString );
+ if ( aIter == aEnd )
+ throw RuntimeException( *pString, static_cast< XPropertySet* >( this ) );
+
+ _getSingleValue ( *((*aIter).second), *pAny );
+ }
+
+ _postGetValues();
+ }
+ return aValues;
+}
+
+void SAL_CALL ChainablePropertySet::addPropertiesChangeListener( const Sequence< OUString >&, const Reference< XPropertiesChangeListener >& )
+{
+ // todo
+}
+
+void SAL_CALL ChainablePropertySet::removePropertiesChangeListener( const Reference< XPropertiesChangeListener >& )
+{
+ // todo
+}
+
+void SAL_CALL ChainablePropertySet::firePropertiesChangeEvent( const Sequence< OUString >&, const Reference< XPropertiesChangeListener >& )
+{
+ // todo
+}
+
+// XPropertyState
+PropertyState SAL_CALL ChainablePropertySet::getPropertyState( const OUString& PropertyName )
+{
+ PropertyInfoHash::const_iterator aIter = mxInfo->maMap.find( PropertyName );
+ if( aIter == mxInfo->maMap.end())
+ throw UnknownPropertyException( PropertyName, static_cast< XPropertySet* >( this ) );
+
+ return PropertyState_AMBIGUOUS_VALUE;
+}
+
+Sequence< PropertyState > SAL_CALL ChainablePropertySet::getPropertyStates( const Sequence< OUString >& rPropertyNames )
+{
+ const sal_Int32 nCount = rPropertyNames.getLength();
+
+ Sequence< PropertyState > aStates( nCount );
+ if( nCount )
+ {
+ PropertyState * pState = aStates.getArray();
+ const OUString * pString = rPropertyNames.getConstArray();
+ PropertyInfoHash::const_iterator aEnd = mxInfo->maMap.end(), aIter;
+
+ for ( sal_Int32 i = 0; i < nCount; ++i, ++pString, ++pState )
+ {
+ aIter = mxInfo->maMap.find ( *pString );
+ if ( aIter == aEnd )
+ throw UnknownPropertyException( *pString, static_cast< XPropertySet* >( this ) );
+ }
+ }
+ return aStates;
+}
+
+void SAL_CALL ChainablePropertySet::setPropertyToDefault( const OUString& rPropertyName )
+{
+ PropertyInfoHash::const_iterator aIter = mxInfo->maMap.find ( rPropertyName );
+
+ if( aIter == mxInfo->maMap.end())
+ throw UnknownPropertyException( rPropertyName, static_cast< XPropertySet* >( this ) );
+}
+
+Any SAL_CALL ChainablePropertySet::getPropertyDefault( const OUString& rPropertyName )
+{
+ PropertyInfoHash::const_iterator aIter = mxInfo->maMap.find ( rPropertyName );
+
+ if( aIter == mxInfo->maMap.end())
+ throw UnknownPropertyException( rPropertyName, static_cast< XPropertySet* >( this ) );
+ return Any();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/property/ChainablePropertySetInfo.cxx b/comphelper/source/property/ChainablePropertySetInfo.cxx
new file mode 100644
index 0000000000..10b4d5fda6
--- /dev/null
+++ b/comphelper/source/property/ChainablePropertySetInfo.cxx
@@ -0,0 +1,95 @@
+/* -*- 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 <comphelper/ChainablePropertySetInfo.hxx>
+#include <sal/log.hxx>
+
+using ::comphelper::PropertyInfo;
+using ::comphelper::ChainablePropertySetInfo;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::beans::Property;
+using ::com::sun::star::beans::UnknownPropertyException;
+
+ChainablePropertySetInfo::ChainablePropertySetInfo( PropertyInfo const * pMap )
+{
+ for( ; !pMap->maName.isEmpty(); ++pMap )
+ {
+ SAL_WARN_IF(
+ maMap.find(pMap->maName) != maMap.end(),
+ "comphelper", "Duplicate property name \"" << pMap->maName << "\"");
+ maMap[pMap->maName] = pMap;
+ }
+}
+
+ChainablePropertySetInfo::~ChainablePropertySetInfo()
+ noexcept
+{
+}
+
+void ChainablePropertySetInfo::remove( const OUString& aName )
+{
+ maMap.erase ( aName );
+ if ( maProperties.hasElements() )
+ maProperties.realloc( 0 );
+}
+
+Sequence< ::Property > SAL_CALL ChainablePropertySetInfo::getProperties()
+{
+ sal_Int32 nSize = maMap.size();
+ if( maProperties.getLength() != nSize )
+ {
+ maProperties.realloc ( nSize );
+ Property* pProperties = maProperties.getArray();
+
+ for (auto const& elem : maMap)
+ {
+ PropertyInfo const * pInfo = elem.second;
+
+ pProperties->Name = pInfo->maName;
+ pProperties->Handle = pInfo->mnHandle;
+ pProperties->Type = pInfo->maType;
+ pProperties->Attributes = pInfo->mnAttributes;
+ ++pProperties;
+ }
+ }
+ return maProperties;
+}
+
+Property SAL_CALL ChainablePropertySetInfo::getPropertyByName( const OUString& rName )
+{
+ PropertyInfoHash::iterator aIter = maMap.find( rName );
+
+ if ( maMap.end() == aIter )
+ throw UnknownPropertyException( rName, *this );
+
+ PropertyInfo const *pInfo = (*aIter).second;
+ Property aProperty;
+ aProperty.Name = pInfo->maName;
+ aProperty.Handle = pInfo->mnHandle;
+ aProperty.Type = pInfo->maType;
+ aProperty.Attributes = pInfo->mnAttributes;
+ return aProperty;
+}
+
+sal_Bool SAL_CALL ChainablePropertySetInfo::hasPropertyByName( const OUString& rName )
+{
+ return maMap.find ( rName ) != maMap.end();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/property/MasterPropertySet.cxx b/comphelper/source/property/MasterPropertySet.cxx
new file mode 100644
index 0000000000..922a4c1c69
--- /dev/null
+++ b/comphelper/source/property/MasterPropertySet.cxx
@@ -0,0 +1,398 @@
+/* -*- 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 <comphelper/MasterPropertySet.hxx>
+#include <comphelper/MasterPropertySetInfo.hxx>
+#include <comphelper/ChainablePropertySet.hxx>
+#include <comphelper/ChainablePropertySetInfo.hxx>
+#include <comphelper/solarmutex.hxx>
+
+
+#include <memory>
+#include <vector>
+#include <optional>
+
+namespace {
+
+class AutoOGuardArray
+{
+ std::vector<std::optional< osl::Guard< comphelper::SolarMutex > >> maGuardArray;
+
+public:
+ explicit AutoOGuardArray( sal_Int32 nNumElements );
+
+ std::optional< osl::Guard< comphelper::SolarMutex > > & operator[] ( sal_Int32 i ) { return maGuardArray[i]; }
+};
+
+}
+
+AutoOGuardArray::AutoOGuardArray( sal_Int32 nNumElements ) : maGuardArray(nNumElements)
+{
+}
+
+
+using namespace ::comphelper;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::beans;
+
+
+SlaveData::SlaveData ( ChainablePropertySet *pSlave)
+: mxSlave ( pSlave )
+, mbInit ( false )
+{
+}
+
+MasterPropertySet::MasterPropertySet( comphelper::MasterPropertySetInfo* pInfo, comphelper::SolarMutex* pMutex )
+ noexcept
+: mpMutex ( pMutex )
+, mnLastId ( 0 )
+, mxInfo ( pInfo )
+{
+}
+
+MasterPropertySet::~MasterPropertySet()
+ noexcept
+{
+ for( const auto& rSlave : maSlaveMap )
+ delete rSlave.second;
+}
+
+// XPropertySet
+Reference< XPropertySetInfo > SAL_CALL MasterPropertySet::getPropertySetInfo( )
+{
+ return mxInfo;
+}
+
+void MasterPropertySet::registerSlave ( ChainablePropertySet *pNewSet )
+ noexcept
+{
+ maSlaveMap [ ++mnLastId ] = new SlaveData ( pNewSet );
+ mxInfo->add ( pNewSet->mxInfo->maMap, mnLastId );
+}
+
+void SAL_CALL MasterPropertySet::setPropertyValue( const OUString& rPropertyName, const Any& rValue )
+{
+ // acquire mutex in c-tor and releases it in the d-tor (exception safe!).
+ std::optional< osl::Guard< comphelper::SolarMutex > > xMutexGuard;
+ if (mpMutex)
+ xMutexGuard.emplace( mpMutex );
+
+ PropertyDataHash::const_iterator aIter = mxInfo->maMap.find ( rPropertyName );
+
+ if( aIter == mxInfo->maMap.end())
+ throw UnknownPropertyException( rPropertyName, static_cast< XPropertySet* >( this ) );
+
+ if ( (*aIter).second->mnMapId == 0 ) // 0 means it's one of ours !
+ {
+ _preSetValues();
+ _setSingleValue( *((*aIter).second->mpInfo), rValue );
+ _postSetValues();
+ }
+ else
+ {
+ ChainablePropertySet * pSlave = maSlaveMap [ (*aIter).second->mnMapId ]->mxSlave.get();
+
+ // acquire mutex in c-tor and releases it in the d-tor (exception safe!).
+ std::optional< osl::Guard< comphelper::SolarMutex > > xMutexGuard2;
+ if (pSlave->mpMutex)
+ xMutexGuard2.emplace( pSlave->mpMutex );
+
+ pSlave->_preSetValues();
+ pSlave->_setSingleValue( *((*aIter).second->mpInfo), rValue );
+ pSlave->_postSetValues();
+ }
+}
+
+Any SAL_CALL MasterPropertySet::getPropertyValue( const OUString& rPropertyName )
+{
+ // acquire mutex in c-tor and releases it in the d-tor (exception safe!).
+ std::optional< osl::Guard< comphelper::SolarMutex > > xMutexGuard;
+ if (mpMutex)
+ xMutexGuard.emplace( mpMutex );
+
+ PropertyDataHash::const_iterator aIter = mxInfo->maMap.find ( rPropertyName );
+
+ if( aIter == mxInfo->maMap.end())
+ throw UnknownPropertyException( rPropertyName, static_cast< XPropertySet* >( this ) );
+
+ Any aAny;
+ if ( (*aIter).second->mnMapId == 0 ) // 0 means it's one of ours !
+ {
+ _preGetValues();
+ _getSingleValue( *((*aIter).second->mpInfo), aAny );
+ _postGetValues();
+ }
+ else
+ {
+ ChainablePropertySet * pSlave = maSlaveMap [ (*aIter).second->mnMapId ]->mxSlave.get();
+
+ // acquire mutex in c-tor and releases it in the d-tor (exception safe!).
+ std::optional< osl::Guard< comphelper::SolarMutex > > xMutexGuard2;
+ if (pSlave->mpMutex)
+ xMutexGuard2.emplace( pSlave->mpMutex );
+
+ pSlave->_preGetValues();
+ pSlave->_getSingleValue( *((*aIter).second->mpInfo), aAny );
+ pSlave->_postGetValues();
+ }
+ return aAny;
+}
+
+void SAL_CALL MasterPropertySet::addPropertyChangeListener( const OUString&, const Reference< XPropertyChangeListener >& )
+{
+ // todo
+}
+
+void SAL_CALL MasterPropertySet::removePropertyChangeListener( const OUString&, const Reference< XPropertyChangeListener >& )
+{
+ // todo
+}
+
+void SAL_CALL MasterPropertySet::addVetoableChangeListener( const OUString&, const Reference< XVetoableChangeListener >& )
+{
+ // todo
+}
+
+void SAL_CALL MasterPropertySet::removeVetoableChangeListener( const OUString&, const Reference< XVetoableChangeListener >& )
+{
+ // todo
+}
+
+// XMultiPropertySet
+void SAL_CALL MasterPropertySet::setPropertyValues( const Sequence< OUString >& aPropertyNames, const Sequence< Any >& aValues )
+{
+ // acquire mutex in c-tor and releases it in the d-tor (exception safe!).
+ std::optional< osl::Guard< comphelper::SolarMutex > > xMutexGuard;
+ if (mpMutex)
+ xMutexGuard.emplace( mpMutex );
+
+ const sal_Int32 nCount = aPropertyNames.getLength();
+
+ if( nCount != aValues.getLength() )
+ throw IllegalArgumentException();
+
+ if( !nCount )
+ return;
+
+ _preSetValues();
+
+ const Any * pAny = aValues.getConstArray();
+ const OUString * pString = aPropertyNames.getConstArray();
+ PropertyDataHash::const_iterator aEnd = mxInfo->maMap.end(), aIter;
+
+ //!! have a unique_ptr to an array of OGuards in order to have the
+ //!! allocated memory properly freed (exception safe!).
+ //!! Since the array itself has unique_ptrs as members we have to use a
+ //!! helper class 'AutoOGuardArray' in order to have
+ //!! the acquired locks properly released.
+ AutoOGuardArray aOGuardArray( nCount );
+
+ for ( sal_Int32 i = 0; i < nCount; ++i, ++pString, ++pAny )
+ {
+ aIter = mxInfo->maMap.find ( *pString );
+ if ( aIter == aEnd )
+ throw RuntimeException( *pString, static_cast< XPropertySet* >( this ) );
+
+ if ( (*aIter).second->mnMapId == 0 ) // 0 means it's one of ours !
+ _setSingleValue( *((*aIter).second->mpInfo), *pAny );
+ else
+ {
+ SlaveData * pSlave = maSlaveMap [ (*aIter).second->mnMapId ];
+ if (!pSlave->IsInit())
+ {
+ // acquire mutex in c-tor and releases it in the d-tor (exception safe!).
+ if (pSlave->mxSlave->mpMutex)
+ aOGuardArray[i].emplace( pSlave->mxSlave->mpMutex );
+
+ pSlave->mxSlave->_preSetValues();
+ pSlave->SetInit ( true );
+ }
+ pSlave->mxSlave->_setSingleValue( *((*aIter).second->mpInfo), *pAny );
+ }
+ }
+
+ _postSetValues();
+ for( const auto& rSlave : maSlaveMap )
+ {
+ if( rSlave.second->IsInit() )
+ {
+ rSlave.second->mxSlave->_postSetValues();
+ rSlave.second->SetInit( false );
+ }
+ }
+}
+
+Sequence< Any > SAL_CALL MasterPropertySet::getPropertyValues( const Sequence< OUString >& aPropertyNames )
+{
+ // acquire mutex in c-tor and releases it in the d-tor (exception safe!).
+ std::optional< osl::Guard< comphelper::SolarMutex > > xMutexGuard;
+ if (mpMutex)
+ xMutexGuard.emplace( mpMutex );
+
+ const sal_Int32 nCount = aPropertyNames.getLength();
+
+ Sequence < Any > aValues ( nCount );
+
+ if( nCount )
+ {
+ _preGetValues();
+
+ Any * pAny = aValues.getArray();
+ const OUString * pString = aPropertyNames.getConstArray();
+ PropertyDataHash::const_iterator aEnd = mxInfo->maMap.end(), aIter;
+
+ //!! have a unique_ptr to an array of OGuards in order to have the
+ //!! allocated memory properly freed (exception safe!).
+ //!! Since the array itself has unique_ptrs as members we have to use a
+ //!! helper class 'AutoOGuardArray' in order to have
+ //!! the acquired locks properly released.
+ AutoOGuardArray aOGuardArray( nCount );
+
+ for ( sal_Int32 i = 0; i < nCount; ++i, ++pString, ++pAny )
+ {
+ aIter = mxInfo->maMap.find ( *pString );
+ if ( aIter == aEnd )
+ throw RuntimeException( *pString, static_cast< XPropertySet* >( this ) );
+
+ if ( (*aIter).second->mnMapId == 0 ) // 0 means it's one of ours !
+ _getSingleValue( *((*aIter).second->mpInfo), *pAny );
+ else
+ {
+ SlaveData * pSlave = maSlaveMap [ (*aIter).second->mnMapId ];
+ if (!pSlave->IsInit())
+ {
+ // acquire mutex in c-tor and releases it in the d-tor (exception safe!).
+ if (pSlave->mxSlave->mpMutex)
+ aOGuardArray[i].emplace( pSlave->mxSlave->mpMutex );
+
+ pSlave->mxSlave->_preGetValues();
+ pSlave->SetInit ( true );
+ }
+ pSlave->mxSlave->_getSingleValue( *((*aIter).second->mpInfo), *pAny );
+ }
+ }
+
+ _postSetValues();
+ for( const auto& rSlave : maSlaveMap )
+ {
+ if( rSlave.second->IsInit() )
+ {
+ rSlave.second->mxSlave->_postSetValues();
+ rSlave.second->SetInit( false );
+ }
+ }
+ }
+ return aValues;
+}
+
+void SAL_CALL MasterPropertySet::addPropertiesChangeListener( const Sequence< OUString >&, const Reference< XPropertiesChangeListener >& )
+{
+ // todo
+}
+
+void SAL_CALL MasterPropertySet::removePropertiesChangeListener( const Reference< XPropertiesChangeListener >& )
+{
+ // todo
+}
+
+void SAL_CALL MasterPropertySet::firePropertiesChangeEvent( const Sequence< OUString >&, const Reference< XPropertiesChangeListener >& )
+{
+ // todo
+}
+
+// XPropertyState
+PropertyState SAL_CALL MasterPropertySet::getPropertyState( const OUString& PropertyName )
+{
+ PropertyDataHash::const_iterator aIter = mxInfo->maMap.find( PropertyName );
+ if( aIter == mxInfo->maMap.end())
+ throw UnknownPropertyException( PropertyName, static_cast< XPropertySet* >( this ) );
+
+ // 0 means it's one of ours !
+ if ( (*aIter).second->mnMapId != 0 )
+ {
+ ChainablePropertySet * pSlave = maSlaveMap [ (*aIter).second->mnMapId ]->mxSlave.get();
+
+ // acquire mutex in c-tor and releases it in the d-tor (exception safe!).
+ std::optional< osl::Guard< comphelper::SolarMutex > > xMutexGuard;
+ if (pSlave->mpMutex)
+ xMutexGuard.emplace( pSlave->mpMutex );
+ }
+
+ return PropertyState_AMBIGUOUS_VALUE;
+}
+
+Sequence< PropertyState > SAL_CALL MasterPropertySet::getPropertyStates( const Sequence< OUString >& rPropertyNames )
+{
+ const sal_Int32 nCount = rPropertyNames.getLength();
+
+ Sequence< PropertyState > aStates( nCount );
+ if( nCount )
+ {
+ PropertyState * pState = aStates.getArray();
+ const OUString * pString = rPropertyNames.getConstArray();
+ PropertyDataHash::const_iterator aEnd = mxInfo->maMap.end(), aIter;
+
+ for ( sal_Int32 i = 0; i < nCount; ++i, ++pString, ++pState )
+ {
+ aIter = mxInfo->maMap.find ( *pString );
+ if ( aIter == aEnd )
+ throw UnknownPropertyException( *pString, static_cast< XPropertySet* >( this ) );
+
+ // 0 means it's one of ours !
+ if ( (*aIter).second->mnMapId != 0 )
+ {
+ SlaveData * pSlave = maSlaveMap [ (*aIter).second->mnMapId ];
+ if (!pSlave->IsInit())
+ {
+ pSlave->SetInit ( true );
+ }
+ }
+ }
+ for( const auto& rSlave : maSlaveMap )
+ {
+ if( rSlave.second->IsInit() )
+ {
+ rSlave.second->SetInit( false );
+ }
+ }
+ }
+ return aStates;
+}
+
+void SAL_CALL MasterPropertySet::setPropertyToDefault( const OUString& rPropertyName )
+{
+ PropertyDataHash::const_iterator aIter = mxInfo->maMap.find ( rPropertyName );
+
+ if( aIter == mxInfo->maMap.end())
+ throw UnknownPropertyException( rPropertyName, static_cast< XPropertySet* >( this ) );
+}
+
+Any SAL_CALL MasterPropertySet::getPropertyDefault( const OUString& rPropertyName )
+{
+ PropertyDataHash::const_iterator aIter = mxInfo->maMap.find ( rPropertyName );
+
+ if( aIter == mxInfo->maMap.end())
+ throw UnknownPropertyException( rPropertyName, static_cast< XPropertySet* >( this ) );
+ return Any();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/property/MasterPropertySetInfo.cxx b/comphelper/source/property/MasterPropertySetInfo.cxx
new file mode 100644
index 0000000000..db8ddb7699
--- /dev/null
+++ b/comphelper/source/property/MasterPropertySetInfo.cxx
@@ -0,0 +1,105 @@
+/* -*- 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 <comphelper/MasterPropertySetInfo.hxx>
+#include <sal/log.hxx>
+
+using ::comphelper::PropertyInfo;
+using ::comphelper::MasterPropertySetInfo;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::beans::Property;
+using ::com::sun::star::beans::UnknownPropertyException;
+
+MasterPropertySetInfo::MasterPropertySetInfo( PropertyInfo const * pMap )
+{
+ for ( ; !pMap->maName.isEmpty(); ++pMap )
+ {
+ SAL_WARN_IF(
+ maMap.find(pMap->maName) != maMap.end(),
+ "comphelper", "Duplicate property name \"" << pMap->maName << "\"");
+ maMap[pMap->maName] = new PropertyData ( 0, pMap );
+ }
+}
+
+MasterPropertySetInfo::~MasterPropertySetInfo()
+ noexcept
+{
+ for( const auto& rObj : maMap )
+ delete rObj.second;
+}
+
+void MasterPropertySetInfo::add( PropertyInfoHash &rHash, sal_uInt8 nMapId )
+{
+ if( maProperties.hasElements() )
+ maProperties.realloc( 0 );
+
+ for( const auto& rObj : rHash )
+ {
+ SAL_WARN_IF(
+ maMap.find(rObj.first) != maMap.end(),
+ "comphelper", "Duplicate property name \"" << rObj.first << "\"");
+ maMap[rObj.first] = new PropertyData ( nMapId, rObj.second );
+ }
+}
+
+Sequence< ::Property > SAL_CALL MasterPropertySetInfo::getProperties()
+{
+ sal_Int32 nSize = maMap.size();
+ if( maProperties.getLength() != nSize )
+ {
+ maProperties.realloc ( nSize );
+ Property* pProperties = maProperties.getArray();
+
+ for (auto const& elem : maMap)
+ {
+ PropertyInfo const * pInfo = elem.second->mpInfo;
+
+ pProperties->Name = pInfo->maName;
+ pProperties->Handle = pInfo->mnHandle;
+ pProperties->Type = pInfo->maType;
+ pProperties->Attributes = pInfo->mnAttributes;
+ ++pProperties;
+ }
+ }
+ return maProperties;
+}
+
+Property SAL_CALL MasterPropertySetInfo::getPropertyByName( const OUString& rName )
+{
+ PropertyDataHash::iterator aIter = maMap.find( rName );
+
+ if ( maMap.end() == aIter )
+ throw UnknownPropertyException( rName, *this );
+
+ PropertyInfo const *pInfo = (*aIter).second->mpInfo;
+ Property aProperty;
+ aProperty.Name = pInfo->maName;
+ aProperty.Handle = pInfo->mnHandle;
+ aProperty.Type = pInfo->maType;
+
+ aProperty.Attributes = pInfo->mnAttributes;
+ return aProperty;
+}
+
+sal_Bool SAL_CALL MasterPropertySetInfo::hasPropertyByName( const OUString& rName )
+{
+ return maMap.find ( rName ) != maMap.end();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/property/genericpropertyset.cxx b/comphelper/source/property/genericpropertyset.cxx
new file mode 100644
index 0000000000..65a3b9f476
--- /dev/null
+++ b/comphelper/source/property/genericpropertyset.cxx
@@ -0,0 +1,240 @@
+/* -*- 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 <map>
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XTypeProvider.hpp>
+#include <cppuhelper/weakagg.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/multiinterfacecontainer4.hxx>
+#include <comphelper/propertysethelper.hxx>
+#include <mutex>
+#include <rtl/ref.hxx>
+#include <comphelper/genericpropertyset.hxx>
+#include <comphelper/propertysetinfo.hxx>
+
+using namespace ::osl;
+using namespace ::cppu;
+using namespace ::comphelper;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+
+namespace comphelper
+{
+ namespace {
+
+ class GenericPropertySet : public OWeakObject,
+ public XServiceInfo,
+ public XTypeProvider,
+ public PropertySetHelper
+ {
+ private:
+ std::map<OUString, Any> maAnyMap;
+ std::mutex maMutex;
+ comphelper::OMultiTypeInterfaceContainerHelperVar4<OUString, XPropertyChangeListener> m_aListener;
+
+ protected:
+ virtual void _setPropertyValues( const PropertyMapEntry** ppEntries, const Any* pValues ) override;
+ virtual void _getPropertyValues( const PropertyMapEntry** ppEntries, Any* pValue ) override;
+
+ public:
+ explicit GenericPropertySet( PropertySetInfo* pInfo ) noexcept;
+
+ // XInterface
+ virtual Any SAL_CALL queryInterface( const Type & rType ) override;
+ virtual void SAL_CALL acquire() noexcept override;
+ virtual void SAL_CALL release() noexcept override;
+
+ // XTypeProvider
+ virtual Sequence< Type > SAL_CALL getTypes( ) override;
+ virtual Sequence< sal_Int8 > SAL_CALL getImplementationId( ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XPropertySet
+ virtual void SAL_CALL addPropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener ) override;
+ virtual void SAL_CALL removePropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& aListener ) override;
+ };
+
+ }
+}
+
+
+GenericPropertySet::GenericPropertySet( PropertySetInfo* pInfo ) noexcept
+: PropertySetHelper( pInfo )
+{
+}
+
+void SAL_CALL GenericPropertySet::addPropertyChangeListener( const OUString& aPropertyName, const Reference< XPropertyChangeListener >& xListener )
+{
+ Reference < XPropertySetInfo > xInfo = getPropertySetInfo( );
+ if ( !xInfo.is() )
+ return;
+
+ std::unique_lock aGuard(maMutex);
+ if ( aPropertyName.isEmpty() )
+ {
+ Sequence< Property> aSeq = xInfo->getProperties();
+ const Property* pIter = aSeq.getConstArray();
+ const Property* pEnd = pIter + aSeq.getLength();
+ for( ; pIter != pEnd ; ++pIter)
+ {
+ m_aListener.addInterface(aGuard, pIter->Name,xListener);
+ }
+ }
+ else if ( xInfo->hasPropertyByName(aPropertyName) )
+ m_aListener.addInterface(aGuard, aPropertyName,xListener);
+ else
+ throw UnknownPropertyException( aPropertyName, *this );
+}
+
+void SAL_CALL GenericPropertySet::removePropertyChangeListener( const OUString& aPropertyName, const Reference< XPropertyChangeListener >& xListener )
+{
+ Reference < XPropertySetInfo > xInfo = getPropertySetInfo( );
+ if ( !xInfo.is() )
+ return;
+
+ std::unique_lock aGuard(maMutex);
+ if ( aPropertyName.isEmpty() )
+ {
+ Sequence< Property> aSeq = xInfo->getProperties();
+ const Property* pIter = aSeq.getConstArray();
+ const Property* pEnd = pIter + aSeq.getLength();
+ for( ; pIter != pEnd ; ++pIter)
+ {
+ m_aListener.removeInterface(aGuard, pIter->Name,xListener);
+ }
+ }
+ else if ( xInfo->hasPropertyByName(aPropertyName) )
+ m_aListener.removeInterface(aGuard, aPropertyName,xListener);
+ else
+ throw UnknownPropertyException( aPropertyName, *this );
+}
+
+void GenericPropertySet::_setPropertyValues( const PropertyMapEntry** ppEntries, const Any* pValues )
+{
+ std::unique_lock aGuard(maMutex);
+
+ while( *ppEntries )
+ {
+ OInterfaceContainerHelper4<XPropertyChangeListener> * pHelper = m_aListener.getContainer(aGuard, (*ppEntries)->maName);
+
+ maAnyMap[ (*ppEntries)->maName ] = *pValues;
+
+ if ( pHelper )
+ {
+ PropertyChangeEvent aEvt;
+ aEvt.PropertyName = (*ppEntries)->maName;
+ aEvt.NewValue = *pValues;
+ pHelper->notifyEach( aGuard, &XPropertyChangeListener::propertyChange, aEvt );
+ }
+
+ ppEntries++;
+ pValues++;
+ }
+}
+
+void GenericPropertySet::_getPropertyValues( const comphelper::PropertyMapEntry** ppEntries, Any* pValue )
+{
+ std::unique_lock aGuard(maMutex);
+
+ while( *ppEntries )
+ {
+ *pValue = maAnyMap[ (*ppEntries)->maName ];
+
+ ppEntries++;
+ pValue++;
+ }
+}
+
+// XInterface
+
+Any SAL_CALL GenericPropertySet::queryInterface( const Type & rType )
+{
+ Any aAny;
+
+ if( rType == cppu::UnoType<XServiceInfo>::get())
+ aAny <<= Reference< XServiceInfo >(this);
+ else if( rType == cppu::UnoType<XTypeProvider>::get())
+ aAny <<= Reference< XTypeProvider >(this);
+ else if( rType == cppu::UnoType<XPropertySet>::get())
+ aAny <<= Reference< XPropertySet >(this);
+ else if( rType == cppu::UnoType<XMultiPropertySet>::get())
+ aAny <<= Reference< XMultiPropertySet >(this);
+ else
+ aAny = OWeakObject::queryInterface( rType );
+
+ return aAny;
+}
+
+void SAL_CALL GenericPropertySet::acquire() noexcept
+{
+ OWeakObject::acquire();
+}
+
+void SAL_CALL GenericPropertySet::release() noexcept
+{
+ OWeakObject::release();
+}
+
+uno::Sequence< uno::Type > SAL_CALL GenericPropertySet::getTypes()
+{
+ return uno::Sequence {
+ cppu::UnoType<XAggregation>::get(),
+ cppu::UnoType<XServiceInfo>::get(),
+ cppu::UnoType<XTypeProvider>::get(),
+ cppu::UnoType<XPropertySet>::get(),
+ cppu::UnoType<XMultiPropertySet>::get() };
+}
+
+uno::Sequence< sal_Int8 > SAL_CALL GenericPropertySet::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// XServiceInfo
+sal_Bool SAL_CALL GenericPropertySet::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+OUString SAL_CALL GenericPropertySet::getImplementationName()
+{
+ return "com.sun.star.comp.comphelper.GenericPropertySet";
+}
+
+Sequence< OUString > SAL_CALL GenericPropertySet::getSupportedServiceNames( )
+{
+ return { "com.sun.star.beans.XPropertySet" };
+}
+
+css::uno::Reference< css::beans::XPropertySet > comphelper::GenericPropertySet_CreateInstance( comphelper::PropertySetInfo* pInfo )
+{
+ return static_cast<XPropertySet*>(new GenericPropertySet( pInfo ));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/property/opropertybag.cxx b/comphelper/source/property/opropertybag.cxx
new file mode 100644
index 0000000000..e0b389c191
--- /dev/null
+++ b/comphelper/source/property/opropertybag.cxx
@@ -0,0 +1,540 @@
+/* -*- 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 "opropertybag.hxx"
+
+#include <com/sun/star/beans/IllegalTypeException.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/Property.hpp>
+
+#include <comphelper/namedvaluecollection.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <cppuhelper/exc_hlp.hxx>
+
+#include <algorithm>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+using namespace ::com::sun::star;
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_comphelper_OPropertyBag (
+ css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new comphelper::OPropertyBag());
+}
+
+namespace comphelper
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::util;
+ using namespace ::com::sun::star::container;
+
+ OPropertyBag::OPropertyBag()
+ :OPropertyBag_PBase( GetBroadcastHelper(), this )
+ ,::cppu::IEventNotificationHook()
+ ,m_bAutoAddProperties( false )
+ ,m_NotifyListeners(m_aMutex)
+ ,m_isModified(false)
+
+ {
+ }
+
+
+ OPropertyBag::~OPropertyBag()
+ {
+ }
+
+
+ IMPLEMENT_FORWARD_XINTERFACE2( OPropertyBag, OPropertyBag_Base, OPropertyBag_PBase )
+ IMPLEMENT_FORWARD_XTYPEPROVIDER2( OPropertyBag, OPropertyBag_Base, OPropertyBag_PBase )
+
+ void SAL_CALL OPropertyBag::initialize( const Sequence< Any >& _rArguments )
+ {
+ Sequence< Type > aTypes;
+ bool AllowEmptyPropertyName(false);
+ bool AutomaticAddition(false);
+
+ if (_rArguments.getLength() == 3
+ && (_rArguments[0] >>= aTypes)
+ && (_rArguments[1] >>= AllowEmptyPropertyName)
+ && (_rArguments[2] >>= AutomaticAddition))
+ {
+ m_aAllowedTypes.insert(std::cbegin(aTypes), std::cend(aTypes));
+ m_bAutoAddProperties = AutomaticAddition;
+
+ } else {
+ ::comphelper::NamedValueCollection aArguments( _rArguments );
+
+ if ( aArguments.get_ensureType( "AllowedTypes", aTypes ) )
+ m_aAllowedTypes.insert(std::cbegin(aTypes), std::cend(aTypes));
+
+ aArguments.get_ensureType( "AutomaticAddition", m_bAutoAddProperties );
+ aArguments.get_ensureType( "AllowEmptyPropertyName",
+ AllowEmptyPropertyName );
+ }
+ if (AllowEmptyPropertyName) {
+ m_aDynamicProperties.setAllowEmptyPropertyName(
+ AllowEmptyPropertyName);
+ }
+ }
+
+ OUString SAL_CALL OPropertyBag::getImplementationName()
+ {
+ return "com.sun.star.comp.comphelper.OPropertyBag";
+ }
+
+ sal_Bool SAL_CALL OPropertyBag::supportsService( const OUString& rServiceName )
+ {
+ return cppu::supportsService(this, rServiceName);
+ }
+
+ Sequence< OUString > SAL_CALL OPropertyBag::getSupportedServiceNames( )
+ {
+ return { "com.sun.star.beans.PropertyBag" };
+ }
+
+ void OPropertyBag::fireEvents(
+ sal_Int32 * /*pnHandles*/,
+ sal_Int32 nCount,
+ sal_Bool bVetoable,
+ bool bIgnoreRuntimeExceptionsWhileFiring)
+ {
+ if (nCount && !bVetoable) {
+ setModifiedImpl(true, bIgnoreRuntimeExceptionsWhileFiring);
+ }
+ }
+
+ void OPropertyBag::setModifiedImpl(bool bModified,
+ bool bIgnoreRuntimeExceptionsWhileFiring)
+ {
+ { // do not lock mutex while notifying (#i93514#) to prevent deadlock
+ ::osl::MutexGuard aGuard( m_aMutex );
+ m_isModified = bModified;
+ }
+ if (!bModified)
+ return;
+
+ try {
+ Reference<XInterface> xThis(*this);
+ EventObject event(xThis);
+ m_NotifyListeners.notifyEach(
+ &XModifyListener::modified, event);
+ } catch (RuntimeException &) {
+ if (!bIgnoreRuntimeExceptionsWhileFiring) {
+ throw;
+ }
+ } catch (Exception &) {
+ // ignore
+ }
+ }
+
+
+ sal_Bool SAL_CALL OPropertyBag::isModified()
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return m_isModified;
+ }
+
+ void SAL_CALL OPropertyBag::setModified( sal_Bool bModified )
+ {
+ setModifiedImpl(bModified, false);
+ }
+
+ void SAL_CALL OPropertyBag::addModifyListener(
+ const Reference< XModifyListener > & xListener)
+ {
+ m_NotifyListeners.addInterface(xListener);
+ }
+
+ void SAL_CALL OPropertyBag::removeModifyListener(
+ const Reference< XModifyListener > & xListener)
+ {
+ m_NotifyListeners.removeInterface(xListener);
+ }
+
+
+ Reference< XPropertySetInfo > SAL_CALL OPropertyBag::getPropertySetInfo( )
+ {
+ return createPropertySetInfo( getInfoHelper() );
+ }
+
+
+ sal_Bool SAL_CALL OPropertyBag::has( const Any& /*aElement*/ )
+ {
+ // XSet is only a workaround for addProperty not being able to add default-void properties.
+ // So, everything of XSet except insert is implemented empty
+ return false;
+ }
+
+
+ void SAL_CALL OPropertyBag::insert( const Any& _element )
+ {
+ // This is a workaround for addProperty not being able to add default-void properties.
+ // If we ever have a smarter XPropertyContainer::addProperty interface, we can remove this, ehm, well, hack.
+ Property aProperty;
+ if ( !( _element >>= aProperty ) )
+ throw IllegalArgumentException( "element is not Property", *this, 1 );
+
+ {
+ osl::MutexGuard g(m_aMutex);
+
+ // check whether the type is allowed, everything else will be checked
+ // by m_aDynamicProperties
+ if (!m_aAllowedTypes.empty()
+ && m_aAllowedTypes.find(aProperty.Type) == m_aAllowedTypes.end())
+ throw IllegalArgumentException("not in list of allowed types", *this, 1);
+
+ m_aDynamicProperties.addVoidProperty(aProperty.Name, aProperty.Type, findFreeHandle(),
+ aProperty.Attributes);
+
+ // our property info is dirty
+ m_pArrayHelper.reset();
+ }
+ setModified(true);
+ }
+
+
+ void SAL_CALL OPropertyBag::remove( const Any& /*aElement*/ )
+ {
+ // XSet is only a workaround for addProperty not being able to add default-void properties.
+ // So, everything of XSet except insert is implemented empty
+ throw NoSuchElementException( OUString(), *this );
+ }
+
+
+ Reference< XEnumeration > SAL_CALL OPropertyBag::createEnumeration( )
+ {
+ // XSet is only a workaround for addProperty not being able to add default-void properties.
+ // So, everything of XSet except insert is implemented empty
+ return nullptr;
+ }
+
+
+ Type SAL_CALL OPropertyBag::getElementType( )
+ {
+ // XSet is only a workaround for addProperty not being able to add default-void properties.
+ // So, everything of XSet except insert is implemented empty
+ return Type();
+ }
+
+
+ sal_Bool SAL_CALL OPropertyBag::hasElements( )
+ {
+ // XSet is only a workaround for addProperty not being able to add default-void properties.
+ // So, everything of XSet except insert is implemented empty
+ return false;
+ }
+
+
+ void SAL_CALL OPropertyBag::getFastPropertyValue( Any& _rValue, sal_Int32 _nHandle ) const
+ {
+ m_aDynamicProperties.getFastPropertyValue( _nHandle, _rValue );
+ }
+
+ sal_Bool SAL_CALL OPropertyBag::convertFastPropertyValue( Any& _rConvertedValue, Any& _rOldValue, sal_Int32 _nHandle, const Any& _rValue )
+ {
+ return m_aDynamicProperties.convertFastPropertyValue( _nHandle, _rValue, _rConvertedValue, _rOldValue );
+ }
+
+ void SAL_CALL OPropertyBag::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const Any& rValue )
+ {
+ m_aDynamicProperties.setFastPropertyValue( nHandle, rValue );
+ }
+
+
+ ::cppu::IPropertyArrayHelper& SAL_CALL OPropertyBag::getInfoHelper()
+ {
+ if (!m_pArrayHelper)
+ {
+ Sequence< Property > aProperties;
+ m_aDynamicProperties.describeProperties( aProperties );
+ m_pArrayHelper.reset( new ::cppu::OPropertyArrayHelper( aProperties ) );
+ }
+ return *m_pArrayHelper;
+
+ }
+
+
+ sal_Int32 OPropertyBag::findFreeHandle() const
+ {
+ const sal_Int32 nPrime = 1009;
+ const sal_Int32 nSeed = 11;
+
+ sal_Int32 nCheck = nSeed;
+ while ( m_aDynamicProperties.hasPropertyByHandle( nCheck ) && ( nCheck != 1 ) )
+ {
+ nCheck = ( nCheck * nSeed ) % nPrime;
+ }
+
+ if ( nCheck == 1 )
+ { // uh ... we already have 1008 handles used up
+ // -> simply count upwards
+ while ( m_aDynamicProperties.hasPropertyByHandle( nCheck ) )
+ ++nCheck;
+ }
+
+ return nCheck;
+ }
+
+
+ void SAL_CALL OPropertyBag::addProperty( const OUString& _rName, ::sal_Int16 _nAttributes, const Any& _rInitialValue )
+ {
+ {
+ osl::MutexGuard g(m_aMutex);
+
+ // check whether the type is allowed, everything else will be checked
+ // by m_aDynamicProperties
+ const Type& aPropertyType = _rInitialValue.getValueType();
+ if (_rInitialValue.hasValue() && !m_aAllowedTypes.empty()
+ && m_aAllowedTypes.find(aPropertyType) == m_aAllowedTypes.end())
+ throw IllegalTypeException(OUString(), *this);
+
+ m_aDynamicProperties.addProperty(_rName, findFreeHandle(), _nAttributes,
+ _rInitialValue);
+
+ // our property info is dirty
+ m_pArrayHelper.reset();
+ }
+ setModified(true);
+ }
+
+
+ void SAL_CALL OPropertyBag::removeProperty( const OUString& _rName )
+ {
+ {
+ osl::MutexGuard g(m_aMutex);
+
+ m_aDynamicProperties.removeProperty(_rName);
+
+ // our property info is dirty
+ m_pArrayHelper.reset();
+ }
+ setModified(true);
+ }
+
+
+ namespace
+ {
+ struct ComparePropertyValueByName
+ {
+ bool operator()( const PropertyValue& _rLHS, const PropertyValue& _rRHS )
+ {
+ return _rLHS.Name < _rRHS.Name;
+ }
+ };
+
+ template< typename CLASS >
+ struct TransformPropertyToName
+ {
+ const OUString& operator()( const CLASS& _rProp )
+ {
+ return _rProp.Name;
+ }
+ };
+
+ struct ExtractPropertyValue
+ {
+ const Any& operator()( const PropertyValue& _rProp )
+ {
+ return _rProp.Value;
+ }
+ };
+ }
+
+
+ Sequence< PropertyValue > SAL_CALL OPropertyBag::getPropertyValues( )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ // all registered properties
+ Sequence< Property > aProperties;
+ m_aDynamicProperties.describeProperties( aProperties );
+
+ // their names
+ Sequence< OUString > aNames( aProperties.getLength() );
+ std::transform(
+ std::cbegin(aProperties),
+ std::cend(aProperties),
+ aNames.getArray(),
+ TransformPropertyToName< Property >()
+ );
+
+ // their values
+ Sequence< Any > aValues;
+ try
+ {
+ aValues = OPropertyBag_PBase::getPropertyValues( aNames );
+ if ( aValues.getLength() != aNames.getLength() )
+ throw RuntimeException();
+ }
+ catch( const RuntimeException& )
+ {
+ throw;
+ }
+ catch( const Exception& )
+ {
+ // ignore
+ }
+
+ // merge names and values, and retrieve the state/handle
+ ::cppu::IPropertyArrayHelper& rPropInfo = getInfoHelper();
+
+ Sequence< PropertyValue > aPropertyValues( aNames.getLength() );
+ const OUString* pName = aNames.getConstArray();
+ const OUString* pNamesEnd = aNames.getConstArray() + aNames.getLength();
+ const Any* pValue = aValues.getArray();
+ PropertyValue* pPropertyValue = aPropertyValues.getArray();
+
+ for ( ; pName != pNamesEnd; ++pName, ++pValue, ++pPropertyValue )
+ {
+ pPropertyValue->Name = *pName;
+ pPropertyValue->Handle = rPropInfo.getHandleByName( *pName );
+ pPropertyValue->Value = *pValue;
+ pPropertyValue->State = getPropertyStateByHandle( pPropertyValue->Handle );
+ }
+
+ return aPropertyValues;
+ }
+
+
+ void OPropertyBag::impl_setPropertyValues_throw( const Sequence< PropertyValue >& _rProps )
+ {
+ // sort (the XMultiPropertySet interface requires this)
+ Sequence< PropertyValue > aProperties( _rProps );
+ auto [begin, end] = asNonConstRange(aProperties);
+ std::sort(
+ begin,
+ end,
+ ComparePropertyValueByName()
+ );
+
+ // a sequence of names
+ Sequence< OUString > aNames( aProperties.getLength() );
+ std::transform(
+ std::cbegin(aProperties),
+ std::cend(aProperties),
+ aNames.getArray(),
+ TransformPropertyToName< PropertyValue >()
+ );
+
+ try
+ {
+ // check for unknown properties
+ // we cannot simply rely on the XMultiPropertySet::setPropertyValues
+ // implementation of our base class, since it does not throw
+ // an UnknownPropertyException. More precise, XMultiPropertySet::setPropertyValues
+ // does not allow to throw this exception, while XPropertyAccess::setPropertyValues
+ // requires it
+ sal_Int32 nCount = aNames.getLength();
+
+ Sequence< sal_Int32 > aHandles( nCount );
+ sal_Int32* pHandle = aHandles.getArray();
+ const PropertyValue* pProperty = aProperties.getConstArray();
+ for ( const OUString* pName = aNames.getConstArray();
+ pName != aNames.getConstArray() + aNames.getLength();
+ ++pName, ++pHandle, ++pProperty
+ )
+ {
+ ::cppu::IPropertyArrayHelper& rPropInfo = getInfoHelper();
+ *pHandle = rPropInfo.getHandleByName( *pName );
+ if ( *pHandle != -1 )
+ continue;
+
+ // there's a property requested which we do not know
+ if ( m_bAutoAddProperties )
+ {
+ // add the property
+ sal_Int16 const nAttributes = PropertyAttribute::BOUND | PropertyAttribute::REMOVABLE | PropertyAttribute::MAYBEDEFAULT;
+ addProperty( *pName, nAttributes, pProperty->Value );
+ continue;
+ }
+
+ // no way out
+ throw UnknownPropertyException( *pName, *this );
+ }
+
+ // a sequence of values
+ Sequence< Any > aValues( aProperties.getLength() );
+ std::transform(
+ std::cbegin(aProperties),
+ std::cend(aProperties),
+ aValues.getArray(),
+ ExtractPropertyValue()
+ );
+
+ setFastPropertyValues( nCount, aHandles.getArray(), aValues.getConstArray(), nCount );
+ }
+ catch( const PropertyVetoException& ) { throw; }
+ catch( const IllegalArgumentException& ) { throw; }
+ catch( const WrappedTargetException& ) { throw; }
+ catch( const RuntimeException& ) { throw; }
+ catch( const UnknownPropertyException& ) { throw; }
+ catch( const Exception& )
+ {
+ throw WrappedTargetException( OUString(), *this, ::cppu::getCaughtException() );
+ }
+ }
+
+
+ void SAL_CALL OPropertyBag::setPropertyValues( const Sequence< PropertyValue >& _rProps )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_setPropertyValues_throw( _rProps );
+ }
+
+
+ PropertyState OPropertyBag::getPropertyStateByHandle( sal_Int32 _nHandle )
+ {
+ // for properties which do not support the MAYBEDEFAULT attribute, don't rely on the base class, but
+ // assume they're always in DIRECT state.
+ // (Note that this probably would belong into the base class. However, this would mean we would need
+ // to check all existent usages of the base class, where MAYBEDEFAULT is *not* set, but
+ // a default is nonetheless supplied/used. This is hard to accomplish reliably, in the
+ // current phase. #i78593#
+
+ ::cppu::IPropertyArrayHelper& rPropInfo = getInfoHelper();
+ sal_Int16 nAttributes(0);
+ OSL_VERIFY( rPropInfo.fillPropertyMembersByHandle( nullptr, &nAttributes, _nHandle ) );
+ if ( ( nAttributes & PropertyAttribute::MAYBEDEFAULT ) == 0 )
+ return PropertyState_DIRECT_VALUE;
+
+ return OPropertyBag_PBase::getPropertyStateByHandle( _nHandle );
+ }
+
+
+ Any OPropertyBag::getPropertyDefaultByHandle( sal_Int32 _nHandle ) const
+ {
+ Any aDefault;
+ m_aDynamicProperties.getPropertyDefaultByHandle( _nHandle, aDefault );
+ return aDefault;
+ }
+
+
+} // namespace comphelper
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/property/opropertybag.hxx b/comphelper/source/property/opropertybag.hxx
new file mode 100644
index 0000000000..66c38d870c
--- /dev/null
+++ b/comphelper/source/property/opropertybag.hxx
@@ -0,0 +1,218 @@
+/* -*- 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 <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/util/XModifiable.hpp>
+#include <com/sun/star/beans/XPropertyBag.hpp>
+#include <com/sun/star/container/XSet.hpp>
+
+#include <cppuhelper/implbase.hxx>
+#include <comphelper/interfacecontainer3.hxx>
+#include <comphelper/propstate.hxx>
+#include <comphelper/broadcasthelper.hxx>
+#include <comphelper/propertybag.hxx>
+#include <comphelper/uno3.hxx>
+
+#include <map>
+#include <set>
+#include <memory>
+
+
+namespace comphelper
+{
+
+
+ struct UnoTypeLess
+ {
+ bool operator()( const css::uno::Type& _rLHS, const css::uno::Type& _rRHS ) const
+ {
+ return rtl_ustr_compare(
+ _rLHS.getTypeLibType()->pTypeName->buffer,
+ _rRHS.getTypeLibType()->pTypeName->buffer
+ ) < 0;
+ }
+ };
+
+ typedef std::map< sal_Int32, css::uno::Any > MapInt2Any;
+ typedef std::set< css::uno::Type, UnoTypeLess > TypeBag;
+
+ typedef ::cppu::WeakImplHelper < css::beans::XPropertyBag
+ , css::util::XModifiable
+ , css::lang::XServiceInfo
+ , css::lang::XInitialization
+ , css::container::XSet
+ > OPropertyBag_Base;
+ typedef ::comphelper::OPropertyStateHelper OPropertyBag_PBase;
+
+ class OPropertyBag final : public ::comphelper::OMutexAndBroadcastHelper // must be before OPropertyBag_PBase
+ ,public OPropertyBag_PBase
+ ,public OPropertyBag_Base
+ ,public ::cppu::IEventNotificationHook
+ {
+ private:
+ /// our IPropertyArrayHelper implementation
+ std::unique_ptr< ::cppu::OPropertyArrayHelper >
+ m_pArrayHelper;
+ ::comphelper::PropertyBag
+ m_aDynamicProperties;
+ /// set of allowed property types
+ TypeBag m_aAllowedTypes;
+ /// should we automatically add properties which are tried to set, if they don't exist previously?
+ bool m_bAutoAddProperties;
+
+ /// for notification
+ ::comphelper::OInterfaceContainerHelper3<css::util::XModifyListener> m_NotifyListeners;
+ /// modify flag
+ bool m_isModified;
+
+ public:
+ //noncopyable
+ OPropertyBag(const OPropertyBag&) = delete;
+ const OPropertyBag& operator=(const OPropertyBag&) = delete;
+ OPropertyBag();
+ virtual ~OPropertyBag() override;
+
+ private:
+ DECLARE_XINTERFACE()
+ DECLARE_XTYPEPROVIDER()
+
+ /** === begin UNO interface implementations == **/
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+ // XModifiable:
+ virtual sal_Bool SAL_CALL isModified( ) override;
+ virtual void SAL_CALL setModified( sal_Bool bModified ) override;
+
+ // XModifyBroadcaster
+ virtual void SAL_CALL addModifyListener(
+ const css::uno::Reference<
+ css::util::XModifyListener > & xListener) override;
+ virtual void SAL_CALL removeModifyListener(
+ const css::uno::Reference<
+ css::util::XModifyListener > & xListener) override;
+
+ // XPropertyContainer
+ virtual void SAL_CALL addProperty( const OUString& Name, ::sal_Int16 Attributes, const css::uno::Any& DefaultValue ) override;
+ virtual void SAL_CALL removeProperty( const OUString& Name ) override;
+
+ // XPropertyAccess
+ virtual css::uno::Sequence< css::beans::PropertyValue > SAL_CALL getPropertyValues( ) override;
+ virtual void SAL_CALL setPropertyValues( const css::uno::Sequence< css::beans::PropertyValue >& aProps ) override;
+
+ // XPropertySet
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override;
+ virtual void SAL_CALL setPropertyValue(const OUString& p1, const css::uno::Any& p2) override
+ { OPropertyBag_PBase::setPropertyValue(p1, p2); }
+ virtual css::uno::Any SAL_CALL getPropertyValue(const OUString& p1) override
+ { return OPropertyBag_PBase::getPropertyValue(p1); }
+ virtual void SAL_CALL addPropertyChangeListener(const OUString& p1, const css::uno::Reference<css::beans::XPropertyChangeListener>& p2) override
+ { OPropertyBag_PBase::addPropertyChangeListener(p1, p2); }
+ virtual void SAL_CALL removePropertyChangeListener(const OUString& p1, const css::uno::Reference<css::beans::XPropertyChangeListener>& p2) override
+ { OPropertyBag_PBase::removePropertyChangeListener(p1, p2); }
+ virtual void SAL_CALL addVetoableChangeListener(const OUString& p1, const css::uno::Reference<css::beans::XVetoableChangeListener>& p2) override
+ { OPropertyBag_PBase::addVetoableChangeListener(p1, p2); }
+ virtual void SAL_CALL removeVetoableChangeListener(const OUString& p1, const css::uno::Reference<css::beans::XVetoableChangeListener>& p2) override
+ { OPropertyBag_PBase::removeVetoableChangeListener(p1, p2); }
+
+ // XSet
+ virtual sal_Bool SAL_CALL has( const css::uno::Any& aElement ) override;
+ virtual void SAL_CALL insert( const css::uno::Any& aElement ) override;
+ virtual void SAL_CALL remove( const css::uno::Any& aElement ) override;
+
+ // XEnumerationAccess (base of XSet)
+ virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration( ) override;
+
+ // XElementAccess (base of XEnumerationAccess)
+ virtual css::uno::Type SAL_CALL getElementType( ) override;
+ virtual sal_Bool SAL_CALL hasElements( ) override;
+ // UNO interface implementations
+
+ // XPropertyState
+ virtual css::uno::Any getPropertyDefaultByHandle( sal_Int32 _nHandle ) const override;
+
+ // OPropertyStateHelper
+ virtual css::beans::PropertyState getPropertyStateByHandle( sal_Int32 _nHandle ) override;
+
+ // OPropertySetHelper
+ virtual void SAL_CALL getFastPropertyValue( css::uno::Any& rValue, sal_Int32 nHandle ) const override;
+ virtual sal_Bool SAL_CALL convertFastPropertyValue( css::uno::Any & rConvertedValue, css::uno::Any & rOldValue, sal_Int32 nHandle, const css::uno::Any& rValue ) override;
+ virtual void SAL_CALL setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const css::uno::Any& rValue ) override;
+ virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override;
+
+ // IEventNotificationHook
+ virtual void fireEvents(
+ sal_Int32 * pnHandles,
+ sal_Int32 nCount,
+ sal_Bool bVetoable,
+ bool bIgnoreRuntimeExceptionsWhileFiring) override;
+
+ void setModifiedImpl( bool bModified,
+ bool bIgnoreRuntimeExceptionsWhileFiring);
+
+ /** finds a free property handle
+ @precond
+ our mutex is locked
+ */
+ sal_Int32 findFreeHandle() const;
+
+ /** implements the setPropertyValues method
+ @param _rProps
+ the property values to set
+
+ @throws PropertyVetoException
+ if the XMultiPropertySet::setPropertyValues call does so
+
+ @throws css::lang::IllegalArgumentException
+ if the XMultiPropertySet::setPropertyValues call does so
+
+ @throws css::lang::WrappedTargetException
+ if the XMultiPropertySet::setPropertyValues call does so
+
+ @throws css::uno::RuntimeException
+ if the XMultiPropertySet::setPropertyValues call does so
+
+ @throws css::beans::UnknownPropertyException
+ if the XMultiPropertySet::setPropertyValues call does so, and <arg>_bTolerateUnknownProperties</arg>
+ was set to <FALSE/>
+
+ @throws css::lang::WrappedTargetException
+ if the XMultiPropertySet::setPropertyValues call did throw an exception not listed
+ above
+ */
+ void impl_setPropertyValues_throw( const css::uno::Sequence< css::beans::PropertyValue >& _rProps );
+
+ using ::cppu::OPropertySetHelper::getPropertyValues;
+ using ::cppu::OPropertySetHelper::setPropertyValues;
+ using ::cppu::OPropertySetHelper::getFastPropertyValue;
+ };
+
+
+} // namespace comphelper
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/property/propagg.cxx b/comphelper/source/property/propagg.cxx
new file mode 100644
index 0000000000..5a0574460c
--- /dev/null
+++ b/comphelper/source/property/propagg.cxx
@@ -0,0 +1,876 @@
+/* -*- 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 <comphelper/propagg.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <o3tl/sorted_vector.hxx>
+#include <typeinfo>
+#include <algorithm>
+#include <cstddef>
+#include <unordered_set>
+#include <memory>
+
+
+namespace comphelper
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::beans;
+
+ using namespace internal;
+
+
+ namespace
+ {
+ const Property* lcl_findPropertyByName( const std::vector< Property >& _rProps, const OUString& _rName )
+ {
+ Property aNameProp(_rName, 0, Type(), 0);
+ auto pResult = std::lower_bound(_rProps.begin(), _rProps.end(), aNameProp, PropertyCompareByName());
+ if ( pResult == _rProps.end() || pResult->Name != _rName )
+ return nullptr;
+
+ return &*pResult;
+ }
+ }
+
+OPropertyArrayAggregationHelper::OPropertyArrayAggregationHelper(
+ const Sequence< Property >& _rProperties, const Sequence< Property >& _rAggProperties,
+ IPropertyInfoService* _pInfoService, sal_Int32 _nFirstAggregateId )
+{
+ // if properties are present both at the delegatee and the aggregate, then the former are supposed to win
+ // merge and sort properties by name, delete duplicates (stable sort ensures delegator properties win)
+ m_aProperties.insert( m_aProperties.end(), _rProperties.begin(), _rProperties.end() );
+ m_aProperties.insert( m_aProperties.end(), _rAggProperties.begin(), _rAggProperties.end() );
+ std::stable_sort( m_aProperties.begin(), m_aProperties.end(), PropertyCompareByName() );
+ m_aProperties.erase( std::unique(m_aProperties.begin(), m_aProperties.end(),
+ []( const css::beans::Property& x, const css::beans::Property& y ) -> bool { return x.Name == y.Name; } ),
+ m_aProperties.end() );
+ m_aProperties.shrink_to_fit();
+
+ // fill aDelegatorProps with names from _rProperties for a fast existence check
+ // different kinds of properties are processed differently
+ std::unordered_set< OUString > aDelegatorProps;
+ aDelegatorProps.reserve( _rProperties.getLength() );
+ for( auto &delegateProp: _rProperties )
+ {
+ const auto inserted = aDelegatorProps.insert( delegateProp.Name );
+ OSL_ENSURE( inserted.second,
+ "OPropertyArrayAggregationHelper::OPropertyArrayAggregationHelper: duplicate delegatee property!" );
+ }
+
+ std::unordered_set< sal_Int32 > existingHandles;
+ existingHandles.reserve( m_aProperties.size() );
+ sal_Int32 nAggregateHandle = _nFirstAggregateId;
+ for ( std::size_t nMPLoop = 0; nMPLoop < m_aProperties.size(); ++nMPLoop )
+ {
+ auto &prop = m_aProperties[ nMPLoop ];
+ if ( aDelegatorProps.find( prop.Name ) != aDelegatorProps.end() )
+ {
+ m_aPropertyAccessors.insert_or_assign(
+ prop.Handle, OPropertyAccessor( -1, nMPLoop, false ));
+ existingHandles.insert( prop.Handle );
+ }
+ else
+ {
+ // determine the handle for the property which we will expose to the outside world
+ sal_Int32 nHandle = -1;
+ // ask the info service first
+ if ( _pInfoService )
+ nHandle = _pInfoService->getPreferredPropertyId( prop.Name );
+
+ if ( ( -1 == nHandle ) || ( existingHandles.find( nHandle ) != existingHandles.end() ) )
+ {
+ // 1. no handle from the info service -> default
+ // 2. conflicts -> use another one (which we don't check anymore, assuming _nFirstAggregateId was large enough)
+ nHandle = nAggregateHandle++;
+ }
+ else
+ {
+ existingHandles.insert( nHandle );
+ }
+
+ // remember the accessor for this property
+ m_aPropertyAccessors.insert_or_assign(
+ nHandle, OPropertyAccessor( prop.Handle, nMPLoop, true ));
+ prop.Handle = nHandle;
+ }
+ }
+}
+
+
+OPropertyArrayAggregationHelper::PropertyOrigin OPropertyArrayAggregationHelper::classifyProperty( const OUString& _rName )
+{
+ PropertyOrigin eOrigin = PropertyOrigin::Unknown;
+ // look up the name
+ const Property* pPropertyDescriptor = lcl_findPropertyByName( m_aProperties, _rName );
+ if ( pPropertyDescriptor )
+ {
+ // look up the handle for this name
+ auto aPos = m_aPropertyAccessors.find( pPropertyDescriptor->Handle );
+ OSL_ENSURE( m_aPropertyAccessors.end() != aPos, "OPropertyArrayAggregationHelper::classifyProperty: should have this handle in my map!" );
+ if ( m_aPropertyAccessors.end() != aPos )
+ {
+ eOrigin = aPos->second.bAggregate ? PropertyOrigin::Aggregate : PropertyOrigin::Delegator;
+ }
+ }
+ return eOrigin;
+}
+
+
+Property OPropertyArrayAggregationHelper::getPropertyByName( const OUString& _rPropertyName )
+{
+ const Property* pProperty = findPropertyByName( _rPropertyName );
+
+ if ( !pProperty )
+ throw UnknownPropertyException(_rPropertyName);
+
+ return *pProperty;
+}
+
+
+sal_Bool OPropertyArrayAggregationHelper::hasPropertyByName(const OUString& _rPropertyName)
+{
+ return nullptr != findPropertyByName( _rPropertyName );
+}
+
+
+const Property* OPropertyArrayAggregationHelper::findPropertyByName(const OUString& _rName ) const
+{
+ return lcl_findPropertyByName( m_aProperties, _rName );
+}
+
+
+sal_Int32 OPropertyArrayAggregationHelper::getHandleByName(const OUString& _rPropertyName)
+{
+ const Property* pProperty = findPropertyByName( _rPropertyName );
+ return pProperty ? pProperty->Handle : -1;
+}
+
+
+sal_Bool OPropertyArrayAggregationHelper::fillPropertyMembersByHandle(
+ OUString* _pPropName, sal_Int16* _pAttributes, sal_Int32 _nHandle)
+{
+ auto i = m_aPropertyAccessors.find(_nHandle);
+ bool bRet = i != m_aPropertyAccessors.end();
+ if (bRet)
+ {
+ const css::beans::Property& rProperty = m_aProperties[(*i).second.nPos];
+ if (_pPropName)
+ *_pPropName = rProperty.Name;
+ if (_pAttributes)
+ *_pAttributes = rProperty.Attributes;
+ }
+ return bRet;
+}
+
+
+bool OPropertyArrayAggregationHelper::getPropertyByHandle( sal_Int32 _nHandle, Property& _rProperty ) const
+{
+ auto pos = m_aPropertyAccessors.find(_nHandle);
+ if ( pos != m_aPropertyAccessors.end() )
+ {
+ _rProperty = m_aProperties[ pos->second.nPos ];
+ return true;
+ }
+ return false;
+}
+
+
+bool OPropertyArrayAggregationHelper::fillAggregatePropertyInfoByHandle(
+ OUString* _pPropName, sal_Int32* _pOriginalHandle, sal_Int32 _nHandle) const
+{
+ auto i = m_aPropertyAccessors.find(_nHandle);
+ bool bRet = i != m_aPropertyAccessors.end() && (*i).second.bAggregate;
+ if (bRet)
+ {
+ if (_pOriginalHandle)
+ *_pOriginalHandle = (*i).second.nOriginalHandle;
+ if (_pPropName)
+ {
+ OSL_ENSURE((*i).second.nPos < m_aProperties.size(),"Invalid index for sequence!");
+ const css::beans::Property& rProperty = m_aProperties[(*i).second.nPos];
+ *_pPropName = rProperty.Name;
+ }
+ }
+ return bRet;
+}
+
+
+css::uno::Sequence< css::beans::Property> OPropertyArrayAggregationHelper::getProperties()
+{
+ return comphelper::containerToSequence(m_aProperties);
+}
+
+
+sal_Int32 OPropertyArrayAggregationHelper::fillHandles(
+ sal_Int32* _pHandles, const css::uno::Sequence< OUString >& _rPropNames )
+{
+ sal_Int32 nHitCount = 0;
+ const OUString* pReqProps = _rPropNames.getConstArray();
+ sal_Int32 nReqLen = _rPropNames.getLength();
+
+ Property aNameProp;
+ for( sal_Int32 i = 0; i < nReqLen; ++i )
+ {
+ aNameProp.Name = pReqProps[i];
+ auto findIter = std::lower_bound(m_aProperties.begin(), m_aProperties.end(), aNameProp, PropertyCompareByName());
+ if ( findIter != m_aProperties.end() && findIter->Name == pReqProps[i] )
+ {
+ _pHandles[i] = findIter->Handle;
+ nHitCount++;
+ }
+ }
+ return nHitCount;
+}
+
+namespace internal
+{
+ class PropertyForwarder
+ {
+ private:
+ OPropertySetAggregationHelper& m_rAggregationHelper;
+ o3tl::sorted_vector< sal_Int32 > m_aProperties;
+ sal_Int32 m_nCurrentlyForwarding;
+
+ public:
+ explicit PropertyForwarder( OPropertySetAggregationHelper& _rAggregationHelper );
+
+ /** declares that the forwarder should be responsible for the given property
+
+ @param _nHandle
+ the public handle (<em>not</em> the original handle!) of the property
+ */
+ void takeResponsibilityFor( sal_Int32 _nHandle );
+
+ /** checks whether the forwarder is responsible for the given property
+ */
+ bool isResponsibleFor( sal_Int32 _nHandle ) const;
+
+ /// actually forwards a property value to the aggregate
+ ///
+ /// @throws Exception
+ void doForward( sal_Int32 _nHandle, const Any& _rValue );
+
+ sal_Int32 getCurrentlyForwardedProperty( ) const { return m_nCurrentlyForwarding; }
+ };
+
+
+ PropertyForwarder::PropertyForwarder( OPropertySetAggregationHelper& _rAggregationHelper )
+ :m_rAggregationHelper( _rAggregationHelper )
+ ,m_nCurrentlyForwarding( -1 )
+ {
+ }
+
+
+ void PropertyForwarder::takeResponsibilityFor( sal_Int32 _nHandle )
+ {
+ m_aProperties.insert( _nHandle );
+ }
+
+
+ bool PropertyForwarder::isResponsibleFor( sal_Int32 _nHandle ) const
+ {
+ return m_aProperties.find( _nHandle ) != m_aProperties.end();
+ }
+
+
+ void PropertyForwarder::doForward( sal_Int32 _nHandle, const Any& _rValue )
+ {
+ OSL_ENSURE( m_rAggregationHelper.m_xAggregateSet.is(), "PropertyForwarder::doForward: no property set!" );
+ if ( !m_rAggregationHelper.m_xAggregateSet.is() )
+ return;
+
+ m_rAggregationHelper.forwardingPropertyValue( _nHandle );
+
+ OSL_ENSURE( m_nCurrentlyForwarding == -1, "PropertyForwarder::doForward: reentrance?" );
+ m_nCurrentlyForwarding = _nHandle;
+
+ try
+ {
+ m_rAggregationHelper.m_xAggregateSet->setPropertyValue( m_rAggregationHelper.getPropertyName( _nHandle ), _rValue );
+ // TODO: cache the property name? (it's a O(log n) search)
+ }
+ catch( const Exception& )
+ {
+ m_rAggregationHelper.forwardedPropertyValue( _nHandle );
+ throw;
+ }
+
+ m_nCurrentlyForwarding = -1;
+
+ m_rAggregationHelper.forwardedPropertyValue( _nHandle );
+ }
+}
+
+OPropertySetAggregationHelper::OPropertySetAggregationHelper( ::cppu::OBroadcastHelper& rBHlp )
+ :OPropertyStateHelper( rBHlp )
+ ,m_bListening( false )
+{
+ m_pForwarder.reset( new PropertyForwarder( *this ) );
+}
+
+
+OPropertySetAggregationHelper::~OPropertySetAggregationHelper()
+{
+}
+
+
+css::uno::Any SAL_CALL OPropertySetAggregationHelper::queryInterface(const css::uno::Type& _rType)
+{
+ css::uno::Any aReturn = OPropertyStateHelper::queryInterface(_rType);
+
+ if ( !aReturn.hasValue() )
+ aReturn = cppu::queryInterface(_rType
+ ,static_cast< css::beans::XPropertiesChangeListener*>(this)
+ ,static_cast< css::beans::XVetoableChangeListener*>(this)
+ ,static_cast< css::lang::XEventListener*>(static_cast< css::beans::XPropertiesChangeListener*>(this))
+ );
+
+ return aReturn;
+}
+
+
+void OPropertySetAggregationHelper::disposing()
+{
+ osl::MutexGuard aGuard(rBHelper.rMutex);
+
+ if ( m_xAggregateSet.is() && m_bListening )
+ {
+ // register as a single listener
+ m_xAggregateMultiSet->removePropertiesChangeListener(this);
+ m_xAggregateSet->removeVetoableChangeListener(OUString(), this);
+ m_bListening = false;
+ }
+
+ OPropertyStateHelper::disposing();
+}
+
+
+void SAL_CALL OPropertySetAggregationHelper::disposing(const css::lang::EventObject& _rSource)
+{
+ OSL_ENSURE(m_xAggregateSet.is(), "OPropertySetAggregationHelper::disposing : don't have an aggregate anymore !");
+ if (_rSource.Source == m_xAggregateSet)
+ m_bListening = false;
+}
+
+
+void SAL_CALL OPropertySetAggregationHelper::propertiesChange(const css::uno::Sequence< css::beans::PropertyChangeEvent>& _rEvents)
+{
+ OSL_ENSURE(m_xAggregateSet.is(), "OPropertySetAggregationHelper::propertiesChange : have no aggregate !");
+
+ sal_Int32 nLen = _rEvents.getLength();
+ cppu::IPropertyArrayHelper& rPH = getInfoHelper();
+
+ if (1 == nLen)
+ {
+ const css::beans::PropertyChangeEvent& evt = _rEvents.getConstArray()[0];
+ OSL_ENSURE(!evt.PropertyName.isEmpty(), "OPropertySetAggregationHelper::propertiesChange : invalid event !");
+ // we had a bug where this assertion would have us saved a whole day :) (72514)
+ sal_Int32 nHandle = rPH.getHandleByName( evt.PropertyName );
+
+ // If nHandle is -1 the event marks a (aggregate) property which we hide to callers
+ // If isCurrentlyForwardingProperty( nHandle ) is <TRUE/>, then we ourself triggered
+ // setting this property. In this case, it will be notified later (by the OPropertySetHelper
+ // implementation)
+
+ if ( ( nHandle != -1 ) && !isCurrentlyForwardingProperty( nHandle ) )
+ fire(&nHandle, &evt.NewValue, &evt.OldValue, 1, false);
+ }
+ else
+ {
+ std::unique_ptr<sal_Int32[]> pHandles(new sal_Int32[nLen]);
+ std::unique_ptr< css::uno::Any[]> pNewValues(new css::uno::Any[nLen]);
+ std::unique_ptr< css::uno::Any[]> pOldValues(new css::uno::Any[nLen]);
+
+ sal_Int32 nDest = 0;
+ for (const css::beans::PropertyChangeEvent& rEvent : _rEvents)
+ {
+ sal_Int32 nHandle = rPH.getHandleByName(rEvent.PropertyName);
+ if ( ( nHandle != -1 ) && !isCurrentlyForwardingProperty( nHandle ) )
+ { // same as above : -1 is valid (73247) ...
+ pHandles[nDest] = nHandle;
+ pNewValues[nDest] = rEvent.NewValue;
+ pOldValues[nDest] = rEvent.OldValue;
+ ++nDest;
+ }
+ }
+
+ if (nDest)
+ fire(pHandles.get(), pNewValues.get(), pOldValues.get(), nDest, false);
+ }
+}
+
+
+void SAL_CALL OPropertySetAggregationHelper::vetoableChange(const css::beans::PropertyChangeEvent& _rEvent)
+{
+ OSL_ENSURE(m_xAggregateSet.is(), "OPropertySetAggregationHelper::vetoableChange : have no aggregate !");
+
+ cppu::IPropertyArrayHelper& rPH = getInfoHelper();
+
+ sal_Int32 nHandle = rPH.getHandleByName(_rEvent.PropertyName);
+ fire(&nHandle, &_rEvent.NewValue, &_rEvent.OldValue, 1, true);
+}
+
+
+void OPropertySetAggregationHelper::setAggregation(const css::uno::Reference< css::uno::XInterface >& _rxDelegate)
+{
+ osl::MutexGuard aGuard(rBHelper.rMutex);
+
+ if (m_bListening && m_xAggregateSet.is())
+ {
+ m_xAggregateMultiSet->removePropertiesChangeListener(this);
+ m_xAggregateSet->removeVetoableChangeListener(OUString(), this);
+ m_bListening = false;
+ }
+
+ m_xAggregateState.set(_rxDelegate, css::uno::UNO_QUERY);
+ m_xAggregateSet.set(_rxDelegate, css::uno::UNO_QUERY);
+ m_xAggregateMultiSet.set(_rxDelegate, css::uno::UNO_QUERY);
+ m_xAggregateFastSet.set(_rxDelegate, css::uno::UNO_QUERY);
+
+ // must support XPropertySet and XMultiPropertySet
+ if ( m_xAggregateSet.is() && !m_xAggregateMultiSet.is() )
+ throw css::lang::IllegalArgumentException();
+}
+
+
+void OPropertySetAggregationHelper::startListening()
+{
+ osl::MutexGuard aGuard(rBHelper.rMutex);
+
+ if (!m_bListening && m_xAggregateSet.is())
+ {
+ // register as a single listener
+ css::uno::Sequence< OUString > aPropertyNames;
+ m_xAggregateMultiSet->addPropertiesChangeListener(aPropertyNames, this);
+ m_xAggregateSet->addVetoableChangeListener(OUString(), this);
+
+ m_bListening = true;
+ }
+}
+
+
+void SAL_CALL OPropertySetAggregationHelper::addVetoableChangeListener(const OUString& _rPropertyName,
+ const css::uno::Reference< css::beans::XVetoableChangeListener>& _rxListener)
+{
+ OPropertySetHelper::addVetoableChangeListener(_rPropertyName, _rxListener);
+ if (!m_bListening)
+ startListening();
+}
+
+
+void SAL_CALL OPropertySetAggregationHelper::addPropertyChangeListener(const OUString& _rPropertyName,
+ const css::uno::Reference< css::beans::XPropertyChangeListener>& _rxListener)
+{
+ OPropertySetHelper::addPropertyChangeListener(_rPropertyName, _rxListener);
+ if (!m_bListening)
+ startListening();
+}
+
+
+void SAL_CALL OPropertySetAggregationHelper::addPropertiesChangeListener(const css::uno::Sequence< OUString >& _rPropertyNames,
+ const css::uno::Reference< css::beans::XPropertiesChangeListener>& _rxListener)
+{
+ OPropertySetHelper::addPropertiesChangeListener(_rPropertyNames, _rxListener);
+ if (!m_bListening)
+ startListening();
+}
+
+
+sal_Int32 OPropertySetAggregationHelper::getOriginalHandle(sal_Int32 nHandle) const
+{
+ OPropertyArrayAggregationHelper& rPH = static_cast<OPropertyArrayAggregationHelper&>( const_cast<OPropertySetAggregationHelper*>(this)->getInfoHelper() );
+ sal_Int32 nOriginalHandle = -1;
+ (void)rPH.fillAggregatePropertyInfoByHandle(nullptr, &nOriginalHandle, nHandle);
+ return nOriginalHandle;
+}
+
+
+OUString OPropertySetAggregationHelper::getPropertyName( sal_Int32 _nHandle ) const
+{
+ OPropertyArrayAggregationHelper& rPH = static_cast< OPropertyArrayAggregationHelper& >( const_cast<OPropertySetAggregationHelper*>(this)->getInfoHelper() );
+ Property aProperty;
+ OSL_VERIFY( rPH.getPropertyByHandle( _nHandle, aProperty ) );
+ return aProperty.Name;
+}
+
+
+void SAL_CALL OPropertySetAggregationHelper::setFastPropertyValue(sal_Int32 _nHandle, const css::uno::Any& _rValue)
+{
+ OPropertyArrayAggregationHelper& rPH = static_cast< OPropertyArrayAggregationHelper& >( getInfoHelper() );
+ OUString aPropName;
+ sal_Int32 nOriginalHandle = -1;
+
+ // does the handle belong to the aggregation ?
+ if (rPH.fillAggregatePropertyInfoByHandle(&aPropName, &nOriginalHandle, _nHandle))
+ if (m_xAggregateFastSet.is())
+ m_xAggregateFastSet->setFastPropertyValue(nOriginalHandle, _rValue);
+ else
+ m_xAggregateSet->setPropertyValue(aPropName, _rValue);
+ else
+ OPropertySetHelper::setFastPropertyValue(_nHandle, _rValue);
+}
+
+
+void OPropertySetAggregationHelper::getFastPropertyValue( css::uno::Any& rValue, sal_Int32 nHandle) const
+{
+ OPropertyArrayAggregationHelper& rPH = static_cast<OPropertyArrayAggregationHelper&>( const_cast<OPropertySetAggregationHelper*>(this)->getInfoHelper() );
+ OUString aPropName;
+ sal_Int32 nOriginalHandle = -1;
+
+ if (rPH.fillAggregatePropertyInfoByHandle(&aPropName, &nOriginalHandle, nHandle))
+ {
+ if (m_xAggregateFastSet.is())
+ rValue = m_xAggregateFastSet->getFastPropertyValue(nOriginalHandle);
+ else
+ rValue = m_xAggregateSet->getPropertyValue(aPropName);
+ }
+ else if ( m_pForwarder->isResponsibleFor( nHandle ) )
+ {
+ // this is a property which has been "overwritten" in our instance (thus
+ // fillAggregatePropertyInfoByHandle didn't find it)
+ rValue = m_xAggregateSet->getPropertyValue( getPropertyName( nHandle ) );
+ }
+}
+
+
+css::uno::Any SAL_CALL OPropertySetAggregationHelper::getFastPropertyValue(sal_Int32 nHandle)
+{
+ OPropertyArrayAggregationHelper& rPH = static_cast< OPropertyArrayAggregationHelper& >( getInfoHelper() );
+ OUString aPropName;
+ sal_Int32 nOriginalHandle = -1;
+ css::uno::Any aValue;
+
+ if (rPH.fillAggregatePropertyInfoByHandle(&aPropName, &nOriginalHandle, nHandle))
+ {
+ if (m_xAggregateFastSet.is())
+ aValue = m_xAggregateFastSet->getFastPropertyValue(nOriginalHandle);
+ else
+ aValue = m_xAggregateSet->getPropertyValue(aPropName);
+ }
+ else
+ aValue = OPropertySetHelper::getFastPropertyValue(nHandle);
+
+ return aValue;
+}
+
+
+void SAL_CALL OPropertySetAggregationHelper::setPropertyValues(
+ const Sequence< OUString >& _rPropertyNames, const Sequence< Any >& _rValues )
+{
+ OSL_ENSURE( !rBHelper.bInDispose, "OPropertySetAggregationHelper::setPropertyValues : do not use within the dispose call !");
+ OSL_ENSURE( !rBHelper.bDisposed, "OPropertySetAggregationHelper::setPropertyValues : object is disposed" );
+
+ // check where the properties come from
+ if (!m_xAggregateSet.is())
+ OPropertySetHelper::setPropertyValues(_rPropertyNames, _rValues);
+ else if (_rPropertyNames.getLength() == 1) // use the more efficient way
+ {
+ if (_rValues.getLength() != 1)
+ throw IllegalArgumentException("lengths do not match", static_cast<XPropertySet*>(this),
+ -1);
+ try
+ {
+ setPropertyValue( _rPropertyNames[0], _rValues[0] );
+ }
+ catch( const UnknownPropertyException& )
+ {
+ // by definition of XMultiPropertySet::setPropertyValues, unknown properties are to be ignored
+ SAL_WARN( "comphelper", "OPropertySetAggregationHelper::setPropertyValues: unknown property: '"
+ << _rPropertyNames[0] << "', implementation: " << typeid( *this ).name() );
+ }
+ }
+ else
+ {
+ OPropertyArrayAggregationHelper& rPH = static_cast< OPropertyArrayAggregationHelper& >( getInfoHelper() );
+
+ // determine which properties belong to the aggregate, and which ones to the delegator
+ sal_Int32 nAggCount(0);
+ sal_Int32 nLen(_rPropertyNames.getLength());
+
+ for ( const OUString& rName : _rPropertyNames )
+ {
+ OPropertyArrayAggregationHelper::PropertyOrigin ePropOrg = rPH.classifyProperty( rName );
+ if ( OPropertyArrayAggregationHelper::PropertyOrigin::Unknown == ePropOrg )
+ throw WrappedTargetException( OUString(), static_cast< XMultiPropertySet* >( this ), Any( UnknownPropertyException( ) ) );
+ // due to a flaw in the API design, this method is not allowed to throw an UnknownPropertyException
+ // so we wrap it into a WrappedTargetException
+
+ if ( OPropertyArrayAggregationHelper::PropertyOrigin::Aggregate == ePropOrg )
+ ++nAggCount;
+ }
+
+ // all properties belong to the aggregate
+ if (nAggCount == nLen)
+ m_xAggregateMultiSet->setPropertyValues(_rPropertyNames, _rValues);
+
+ // all properties belong to the aggregating object
+ else if (nAggCount == 0)
+ OPropertySetHelper::setPropertyValues(_rPropertyNames, _rValues);
+
+ // mixed
+ else
+ {
+ if (_rValues.getLength() != nLen)
+ throw IllegalArgumentException("lengths do not match",
+ static_cast<XPropertySet*>(this), -1);
+ const css::uno::Any* pValues = _rValues.getConstArray();
+
+ // dividing the Names and _rValues
+
+ // aggregate's names
+ Sequence< OUString > AggPropertyNames( nAggCount );
+ OUString* pAggNames = AggPropertyNames.getArray();
+ // aggregate's values
+ Sequence< Any > AggValues( nAggCount );
+ Any* pAggValues = AggValues.getArray();
+
+ // delegator names
+ Sequence< OUString > DelPropertyNames( nLen - nAggCount );
+ OUString* pDelNames = DelPropertyNames.getArray();
+
+ // delegator values
+ Sequence< Any > DelValues( nLen - nAggCount );
+ Any* pDelValues = DelValues.getArray();
+
+ for ( const OUString& rName : _rPropertyNames )
+ {
+ if ( OPropertyArrayAggregationHelper::PropertyOrigin::Aggregate == rPH.classifyProperty( rName ) )
+ {
+ *pAggNames++ = rName;
+ *pAggValues++ = *pValues++;
+ }
+ else
+ {
+ *pDelNames++ = rName;
+ *pDelValues++ = *pValues++;
+ }
+ }
+
+ // reset, needed below
+ pDelValues = DelValues.getArray();
+
+ std::unique_ptr<sal_Int32[]> pHandles(new sal_Int32[ nLen - nAggCount ]);
+
+ // get the map table
+ cppu::IPropertyArrayHelper& rPH2 = getInfoHelper();
+
+ // fill the handle array
+ sal_Int32 nHitCount = rPH2.fillHandles( pHandles.get(), DelPropertyNames );
+ if (nHitCount != 0)
+ {
+ std::unique_ptr< css::uno::Any[]> pConvertedValues(new css::uno::Any[ nHitCount ]);
+ std::unique_ptr< css::uno::Any[]> pOldValues(new css::uno::Any[ nHitCount ]);
+ nHitCount = 0;
+ sal_Int32 i;
+
+ {
+ // must lock the mutex outside the loop. So all values are consistent.
+ osl::MutexGuard aGuard( rBHelper.rMutex );
+ for( i = 0; i < (nLen - nAggCount); ++i )
+ {
+ if( pHandles[i] != -1 )
+ {
+ sal_Int16 nAttributes;
+ rPH2.fillPropertyMembersByHandle( nullptr, &nAttributes, pHandles[i] );
+ if( nAttributes & css::beans::PropertyAttribute::READONLY )
+ throw css::beans::PropertyVetoException();
+ // Will the property change?
+ if( convertFastPropertyValue( pConvertedValues[ nHitCount ], pOldValues[nHitCount],
+ pHandles[i], pDelValues[i] ) )
+ {
+ // only increment if the property really change
+ pHandles[nHitCount] = pHandles[i];
+ nHitCount++;
+ }
+ }
+ }
+ // release guard to fire events
+ }
+
+ // fire vetoable events
+ fire( pHandles.get(), pConvertedValues.get(), pOldValues.get(), nHitCount, true );
+
+ // setting the agg Properties
+ m_xAggregateMultiSet->setPropertyValues(AggPropertyNames, AggValues);
+
+ {
+ // must lock the mutex outside the loop.
+ osl::MutexGuard aGuard( rBHelper.rMutex );
+ // Loop over all changed properties
+ for( i = 0; i < nHitCount; i++ )
+ {
+ // Will the property change?
+ setFastPropertyValue_NoBroadcast( pHandles[i], pConvertedValues[i] );
+ }
+ // release guard to fire events
+ }
+
+ // fire change events
+ fire( pHandles.get(), pConvertedValues.get(), pOldValues.get(), nHitCount, false );
+ }
+ else
+ m_xAggregateMultiSet->setPropertyValues(AggPropertyNames, AggValues);
+ }
+ }
+}
+
+// XPropertyState
+
+css::beans::PropertyState SAL_CALL OPropertySetAggregationHelper::getPropertyState(const OUString& _rPropertyName)
+{
+ OPropertyArrayAggregationHelper& rPH = static_cast< OPropertyArrayAggregationHelper& >( getInfoHelper() );
+ sal_Int32 nHandle = rPH.getHandleByName( _rPropertyName );
+
+ if (nHandle == -1)
+ {
+ throw css::beans::UnknownPropertyException(_rPropertyName);
+ }
+
+ OUString aPropName;
+ sal_Int32 nOriginalHandle = -1;
+ if (rPH.fillAggregatePropertyInfoByHandle(&aPropName, &nOriginalHandle, nHandle))
+ {
+ if (m_xAggregateState.is())
+ return m_xAggregateState->getPropertyState(_rPropertyName);
+ else
+ return css::beans::PropertyState_DIRECT_VALUE;
+ }
+ else
+ return getPropertyStateByHandle(nHandle);
+}
+
+
+void SAL_CALL OPropertySetAggregationHelper::setPropertyToDefault(const OUString& _rPropertyName)
+{
+ OPropertyArrayAggregationHelper& rPH = static_cast< OPropertyArrayAggregationHelper& >( getInfoHelper() );
+ sal_Int32 nHandle = rPH.getHandleByName(_rPropertyName);
+ if (nHandle == -1)
+ {
+ throw css::beans::UnknownPropertyException(_rPropertyName);
+ }
+
+ OUString aPropName;
+ sal_Int32 nOriginalHandle = -1;
+ if (rPH.fillAggregatePropertyInfoByHandle(&aPropName, &nOriginalHandle, nHandle))
+ {
+ if (m_xAggregateState.is())
+ m_xAggregateState->setPropertyToDefault(_rPropertyName);
+ }
+ else
+ {
+ try
+ {
+ setPropertyToDefaultByHandle( nHandle );
+ }
+ catch( const UnknownPropertyException& ) { throw; }
+ catch( const RuntimeException& ) { throw; }
+ catch( const Exception& )
+ {
+ OSL_FAIL( "OPropertySetAggregationHelper::setPropertyToDefault: caught an exception which is not allowed to leave here!" );
+ }
+ }
+}
+
+
+css::uno::Any SAL_CALL OPropertySetAggregationHelper::getPropertyDefault(const OUString& aPropertyName)
+{
+ OPropertyArrayAggregationHelper& rPH = static_cast< OPropertyArrayAggregationHelper& >( getInfoHelper() );
+ sal_Int32 nHandle = rPH.getHandleByName( aPropertyName );
+
+ if ( nHandle == -1 )
+ throw css::beans::UnknownPropertyException(aPropertyName);
+
+ OUString aPropName;
+ sal_Int32 nOriginalHandle = -1;
+ if (rPH.fillAggregatePropertyInfoByHandle(&aPropName, &nOriginalHandle, nHandle))
+ {
+ if (m_xAggregateState.is())
+ return m_xAggregateState->getPropertyDefault(aPropertyName);
+ else
+ return css::uno::Any();
+ }
+ else
+ return getPropertyDefaultByHandle(nHandle);
+}
+
+sal_Bool SAL_CALL OPropertySetAggregationHelper::convertFastPropertyValue( Any& _rConvertedValue, Any& _rOldValue, sal_Int32 _nHandle, const Any& _rValue )
+{
+ bool bModified = false;
+
+ OSL_ENSURE( m_pForwarder->isResponsibleFor( _nHandle ), "OPropertySetAggregationHelper::convertFastPropertyValue: this is no forwarded property - did you use declareForwardedProperty for it?" );
+ if ( m_pForwarder->isResponsibleFor( _nHandle ) )
+ {
+ // need to determine the type of the property for conversion
+ OPropertyArrayAggregationHelper& rPH = static_cast< OPropertyArrayAggregationHelper& >( getInfoHelper() );
+ Property aProperty;
+ OSL_VERIFY( rPH.getPropertyByHandle( _nHandle, aProperty ) );
+
+ Any aCurrentValue;
+ getFastPropertyValue( aCurrentValue, _nHandle );
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, aCurrentValue, aProperty.Type );
+ }
+
+ return bModified;
+}
+
+void SAL_CALL OPropertySetAggregationHelper::setFastPropertyValue_NoBroadcast( sal_Int32 _nHandle, const Any& _rValue )
+{
+ OSL_ENSURE( m_pForwarder->isResponsibleFor( _nHandle ), "OPropertySetAggregationHelper::setFastPropertyValue_NoBroadcast: this is no forwarded property - did you use declareForwardedProperty for it?" );
+ if ( m_pForwarder->isResponsibleFor( _nHandle ) )
+ m_pForwarder->doForward( _nHandle, _rValue );
+}
+
+
+void OPropertySetAggregationHelper::declareForwardedProperty( sal_Int32 _nHandle )
+{
+ OSL_ENSURE( !m_pForwarder->isResponsibleFor( _nHandle ), "OPropertySetAggregationHelper::declareForwardedProperty: already declared!" );
+ m_pForwarder->takeResponsibilityFor( _nHandle );
+}
+
+
+void OPropertySetAggregationHelper::forwardingPropertyValue( sal_Int32 )
+{
+ // not interested in
+}
+
+
+void OPropertySetAggregationHelper::forwardedPropertyValue( sal_Int32 )
+{
+ // not interested in
+}
+
+
+bool OPropertySetAggregationHelper::isCurrentlyForwardingProperty( sal_Int32 _nHandle ) const
+{
+ return m_pForwarder->getCurrentlyForwardedProperty() == _nHandle;
+}
+
+
+} // namespace comphelper
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/property/property.cxx b/comphelper/source/property/property.cxx
new file mode 100644
index 0000000000..7d57baeb39
--- /dev/null
+++ b/comphelper/source/property/property.cxx
@@ -0,0 +1,206 @@
+/* -*- 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 <comphelper/property.hxx>
+#include <comphelper/sequence.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+#if OSL_DEBUG_LEVEL > 0
+ #include <cppuhelper/exc_hlp.hxx>
+ #include <com/sun/star/lang/XServiceInfo.hpp>
+ #include <typeinfo>
+#endif
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <rtl/ustrbuf.hxx>
+#include <algorithm>
+
+
+namespace comphelper
+{
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::beans::XPropertySet;
+ using ::com::sun::star::beans::XPropertySetInfo;
+ using ::com::sun::star::beans::Property;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::uno::Type;
+ using ::com::sun::star::uno::cpp_queryInterface;
+ using ::com::sun::star::uno::cpp_acquire;
+ using ::com::sun::star::uno::cpp_release;
+#if OSL_DEBUG_LEVEL > 0
+ using ::com::sun::star::lang::XServiceInfo;
+#endif
+ using ::com::sun::star::uno::UNO_QUERY;
+
+ namespace PropertyAttribute = ::com::sun::star::beans::PropertyAttribute;
+
+
+void copyProperties(const Reference<XPropertySet>& _rxSource,
+ const Reference<XPropertySet>& _rxDest)
+{
+ if (!_rxSource.is() || !_rxDest.is())
+ {
+ OSL_FAIL("copyProperties: invalid arguments !");
+ return;
+ }
+
+ Reference< XPropertySetInfo > xSourceProps = _rxSource->getPropertySetInfo();
+ Reference< XPropertySetInfo > xDestProps = _rxDest->getPropertySetInfo();
+
+ const Sequence< Property > aSourceProps = xSourceProps->getProperties();
+ Property aDestProp;
+ for (const Property& rSourceProp : aSourceProps)
+ {
+ if ( xDestProps->hasPropertyByName(rSourceProp.Name) )
+ {
+ try
+ {
+ aDestProp = xDestProps->getPropertyByName(rSourceProp.Name);
+ if (0 == (aDestProp.Attributes & PropertyAttribute::READONLY) )
+ {
+ const Any aSourceValue = _rxSource->getPropertyValue(rSourceProp.Name);
+ if ( 0 != (aDestProp.Attributes & PropertyAttribute::MAYBEVOID) || aSourceValue.hasValue() )
+ _rxDest->setPropertyValue(rSourceProp.Name, aSourceValue);
+ }
+ }
+ catch (Exception&)
+ {
+#if OSL_DEBUG_LEVEL > 0
+ TOOLS_WARN_EXCEPTION("comphelper", "Caught exception copying properties");
+
+ OUStringBuffer aBuffer(
+ "::comphelper::copyProperties: could not copy property '"
+ + rSourceProp.Name
+ + "' to the destination set (a '" );
+
+ Reference< XServiceInfo > xSI( _rxDest, UNO_QUERY );
+ if ( xSI.is() )
+ {
+ aBuffer.append( xSI->getImplementationName() );
+ }
+ else
+ {
+ aBuffer.appendAscii( typeid( *_rxDest ).name() );
+ }
+ aBuffer.append( "' implementation).\n" );
+
+ Any aException( ::cppu::getCaughtException() );
+ aBuffer.append( "Caught an exception of type '"
+ + aException.getValueTypeName()
+ + "'" );
+
+ Exception aBaseException;
+ if ( ( aException >>= aBaseException ) && !aBaseException.Message.isEmpty() )
+ {
+ aBuffer.append( ", saying '"
+ + aBaseException.Message
+ + "'" );
+ }
+ aBuffer.append( "." );
+
+ SAL_WARN( "comphelper", aBuffer.makeStringAndClear() );
+#endif
+ }
+ }
+ }
+}
+
+
+bool hasProperty(const OUString& _rName, const Reference<XPropertySet>& _rxSet)
+{
+ if (_rxSet.is())
+ {
+ // XPropertySetInfoRef xInfo(rxSet->getPropertySetInfo());
+ return _rxSet->getPropertySetInfo()->hasPropertyByName(_rName);
+ }
+ return false;
+}
+
+
+void RemoveProperty(Sequence<Property>& _rProps, const OUString& _rPropName)
+{
+ // binary search
+ Property aNameProp(_rPropName, 0, Type(), 0);
+ const Property* pResult = std::lower_bound(std::cbegin(_rProps), std::cend(_rProps), aNameProp, PropertyCompareByName());
+
+ if ( pResult != std::cend(_rProps) && pResult->Name == _rPropName)
+ {
+ removeElementAt(_rProps, pResult - std::cbegin(_rProps));
+ }
+}
+
+
+void ModifyPropertyAttributes(Sequence<Property>& seqProps, const OUString& sPropName, sal_Int16 nAddAttrib, sal_Int16 nRemoveAttrib)
+{
+ // binary search
+ auto [begin, end] = asNonConstRange(seqProps);
+ Property aNameProp(sPropName, 0, Type(), 0);
+ Property* pResult = std::lower_bound(begin, end, aNameProp, PropertyCompareByName());
+
+ if ( (pResult != end) && (pResult->Name == sPropName) )
+ {
+ pResult->Attributes |= nAddAttrib;
+ pResult->Attributes &= ~nRemoveAttrib;
+ }
+}
+
+
+bool tryPropertyValue(Any& _rConvertedValue, Any& _rOldValue, const Any& _rValueToSet, const Any& _rCurrentValue, const Type& _rExpectedType)
+{
+ bool bModified(false);
+ if (_rCurrentValue.getValue() != _rValueToSet.getValue())
+ {
+ if ( _rValueToSet.hasValue() && ( !_rExpectedType.equals( _rValueToSet.getValueType() ) ) )
+ {
+ _rConvertedValue = Any( nullptr, _rExpectedType.getTypeLibType() );
+
+ if ( !uno_type_assignData(
+ const_cast< void* >( _rConvertedValue.getValue() ), _rConvertedValue.getValueType().getTypeLibType(),
+ const_cast< void* >( _rValueToSet.getValue() ), _rValueToSet.getValueType().getTypeLibType(),
+ reinterpret_cast< uno_QueryInterfaceFunc >(
+ cpp_queryInterface),
+ reinterpret_cast< uno_AcquireFunc >(cpp_acquire),
+ reinterpret_cast< uno_ReleaseFunc >(cpp_release)
+ )
+ )
+ throw css::lang::IllegalArgumentException();
+ }
+ else
+ _rConvertedValue = _rValueToSet;
+
+ if ( _rCurrentValue != _rConvertedValue )
+ {
+ _rOldValue = _rCurrentValue;
+ bModified = true;
+ }
+ }
+ return bModified;
+}
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/property/propertybag.cxx b/comphelper/source/property/propertybag.cxx
new file mode 100644
index 0000000000..02e6f78c12
--- /dev/null
+++ b/comphelper/source/property/propertybag.cxx
@@ -0,0 +1,206 @@
+/* -*- 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 <comphelper/propertybag.hxx>
+#include <osl/diagnose.h>
+
+#include <com/sun/star/beans/IllegalTypeException.hpp>
+#include <com/sun/star/beans/PropertyExistException.hpp>
+#include <com/sun/star/container/ElementExistException.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/NotRemoveableException.hpp>
+#include <com/sun/star/beans/UnknownPropertyException.hpp>
+
+#include <map>
+#include <string_view>
+
+namespace comphelper
+{
+
+
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::uno::Type;
+ using ::com::sun::star::uno::TypeClass_VOID;
+ using ::com::sun::star::beans::IllegalTypeException;
+ using ::com::sun::star::beans::PropertyExistException;
+ using ::com::sun::star::container::ElementExistException;
+ using ::com::sun::star::lang::IllegalArgumentException;
+ using ::com::sun::star::beans::Property;
+ using ::com::sun::star::beans::NotRemoveableException;
+ using ::com::sun::star::beans::UnknownPropertyException;
+
+ namespace PropertyAttribute = ::com::sun::star::beans::PropertyAttribute;
+
+ PropertyBag::PropertyBag()
+ : m_bAllowEmptyPropertyName(false)
+ {
+ }
+
+ PropertyBag::~PropertyBag()
+ {
+ }
+
+
+ void PropertyBag::setAllowEmptyPropertyName( bool i_isAllowed )
+ {
+ m_bAllowEmptyPropertyName = i_isAllowed;
+ }
+
+
+ namespace
+ {
+ void lcl_checkForEmptyName( const bool _allowEmpty, std::u16string_view _name )
+ {
+ if ( !_allowEmpty && _name.empty() )
+ throw IllegalArgumentException(
+ "The property name must not be empty.",
+ // TODO: resource
+ nullptr,
+ 1
+ );
+ }
+
+ void lcl_checkNameAndHandle_PropertyExistException( const OUString& _name, const sal_Int32 _handle, const PropertyBag& _container )
+ {
+ if ( _container.hasPropertyByName( _name ) || _container.hasPropertyByHandle( _handle ) )
+ throw PropertyExistException(
+ "Property name or handle already used.",
+ nullptr );
+
+ }
+
+ void lcl_checkNameAndHandle_ElementExistException( const OUString& _name, const sal_Int32 _handle, const PropertyBag& _container )
+ {
+ if ( _container.hasPropertyByName( _name ) || _container.hasPropertyByHandle( _handle ) )
+ throw ElementExistException(
+ "Property name or handle already used.",
+ nullptr );
+
+ }
+
+ }
+
+
+ void PropertyBag::addVoidProperty( const OUString& _rName, const Type& _rType, sal_Int32 _nHandle, sal_Int32 _nAttributes )
+ {
+ if ( _rType.getTypeClass() == TypeClass_VOID )
+ throw IllegalArgumentException(
+ "Illegal property type: VOID",
+ // TODO: resource
+ nullptr,
+ 1
+ );
+
+ // check name/handle sanity
+ lcl_checkForEmptyName( m_bAllowEmptyPropertyName, _rName );
+ lcl_checkNameAndHandle_ElementExistException( _rName, _nHandle, *this );
+
+ // register the property
+ OSL_ENSURE( _nAttributes & PropertyAttribute::MAYBEVOID, "PropertyBag::addVoidProperty: this is for default-void properties only!" );
+ registerPropertyNoMember( _rName, _nHandle, _nAttributes | PropertyAttribute::MAYBEVOID, _rType, css::uno::Any() );
+
+ // remember the default
+ aDefaults.emplace( _nHandle, Any() );
+ }
+
+
+ void PropertyBag::addProperty( const OUString& _rName, sal_Int32 _nHandle, sal_Int32 _nAttributes, const Any& _rInitialValue )
+ {
+ // check type sanity
+ const Type& aPropertyType = _rInitialValue.getValueType();
+ if ( aPropertyType.getTypeClass() == TypeClass_VOID )
+ throw IllegalTypeException(
+ "The initial value must be non-NULL to determine the property type.",
+ // TODO: resource
+ nullptr );
+
+ // check name/handle sanity
+ lcl_checkForEmptyName( m_bAllowEmptyPropertyName, _rName );
+ lcl_checkNameAndHandle_PropertyExistException( _rName, _nHandle, *this );
+
+ // register the property
+ registerPropertyNoMember( _rName, _nHandle, _nAttributes, aPropertyType,
+ _rInitialValue );
+
+ // remember the default
+ aDefaults.emplace( _nHandle, _rInitialValue );
+ }
+
+
+ void PropertyBag::removeProperty( const OUString& _rName )
+ {
+ const Property& rProp = getProperty( _rName );
+ // will throw an UnknownPropertyException if necessary
+ if ( ( rProp.Attributes & PropertyAttribute::REMOVABLE ) == 0 )
+ throw NotRemoveableException( OUString(), nullptr );
+ const sal_Int32 nHandle = rProp.Handle;
+
+ revokeProperty( nHandle );
+
+ aDefaults.erase( nHandle );
+ }
+
+
+ void PropertyBag::getFastPropertyValue( sal_Int32 _nHandle, Any& _out_rValue ) const
+ {
+ if ( !hasPropertyByHandle( _nHandle ) )
+ throw UnknownPropertyException(OUString::number(_nHandle));
+
+ OPropertyContainerHelper::getFastPropertyValue( _out_rValue, _nHandle );
+ }
+
+
+ bool PropertyBag::convertFastPropertyValue( sal_Int32 _nHandle, const Any& _rNewValue, Any& _out_rConvertedValue, Any& _out_rCurrentValue ) const
+ {
+ if ( !hasPropertyByHandle( _nHandle ) )
+ throw UnknownPropertyException(OUString::number(_nHandle));
+
+ return const_cast< PropertyBag* >( this )->OPropertyContainerHelper::convertFastPropertyValue(
+ _out_rConvertedValue, _out_rCurrentValue, _nHandle, _rNewValue );
+ }
+
+
+ void PropertyBag::setFastPropertyValue( sal_Int32 _nHandle, const Any& _rValue )
+ {
+ if ( !hasPropertyByHandle( _nHandle ) )
+ throw UnknownPropertyException(OUString::number(_nHandle));
+
+ OPropertyContainerHelper::setFastPropertyValue( _nHandle, _rValue );
+ }
+
+
+ void PropertyBag::getPropertyDefaultByHandle( sal_Int32 _nHandle, Any& _out_rValue ) const
+ {
+ if ( !hasPropertyByHandle( _nHandle ) )
+ throw UnknownPropertyException(OUString::number(_nHandle));
+
+ auto pos = aDefaults.find( _nHandle );
+ OSL_ENSURE( pos != aDefaults.end(), "PropertyBag::getPropertyDefaultByHandle: inconsistency!" );
+ if ( pos != aDefaults.end() )
+ _out_rValue = pos->second;
+ else
+ _out_rValue.clear();
+ }
+
+
+} // namespace comphelper
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/property/propertycontainer.cxx b/comphelper/source/property/propertycontainer.cxx
new file mode 100644
index 0000000000..2b56854056
--- /dev/null
+++ b/comphelper/source/property/propertycontainer.cxx
@@ -0,0 +1,76 @@
+/* -*- 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 <comphelper/propertycontainer.hxx>
+#include <cppuhelper/typeprovider.hxx>
+
+
+namespace comphelper
+{
+
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::beans;
+
+OPropertyContainer::OPropertyContainer(::cppu::OBroadcastHelper& _rBHelper)
+ :OPropertySetHelper(_rBHelper)
+{
+}
+
+
+OPropertyContainer::~OPropertyContainer()
+{
+}
+
+
+Sequence< Type > OPropertyContainer::getBaseTypes()
+{
+ // just the types from our one and only base class
+ ::cppu::OTypeCollection aTypes(
+ cppu::UnoType<XPropertySet>::get(),
+ cppu::UnoType<XFastPropertySet>::get(),
+ cppu::UnoType<XMultiPropertySet>::get()
+ );
+ return aTypes.getTypes();
+}
+
+sal_Bool OPropertyContainer::convertFastPropertyValue(
+ Any& _rConvertedValue, Any& _rOldValue, sal_Int32 _nHandle, const Any& _rValue )
+{
+ return OPropertyContainerHelper::convertFastPropertyValue( _rConvertedValue, _rOldValue, _nHandle, _rValue );
+}
+
+
+void OPropertyContainer::setFastPropertyValue_NoBroadcast(sal_Int32 _nHandle, const Any& _rValue)
+{
+ OPropertyContainerHelper::setFastPropertyValue( _nHandle, _rValue );
+}
+
+
+void OPropertyContainer::getFastPropertyValue(Any& _rValue, sal_Int32 _nHandle) const
+{
+ OPropertyContainerHelper::getFastPropertyValue( _rValue, _nHandle );
+}
+
+
+} // namespace comphelper
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/property/propertycontainerhelper.cxx b/comphelper/source/property/propertycontainerhelper.cxx
new file mode 100644
index 0000000000..ee81100ae6
--- /dev/null
+++ b/comphelper/source/property/propertycontainerhelper.cxx
@@ -0,0 +1,494 @@
+/* -*- 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 <comphelper/propertycontainerhelper.hxx>
+#include <comphelper/property.hxx>
+#include <osl/diagnose.h>
+#include <uno/data.h>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/UnknownPropertyException.hpp>
+
+#include <algorithm>
+#include <utility>
+
+
+namespace comphelper
+{
+
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::beans;
+
+
+namespace
+{
+ // comparing two property descriptions
+ struct PropertyDescriptionHandleCompare
+ {
+ bool operator() (const PropertyDescription& x, const PropertyDescription& y) const
+ {
+ return x.aProperty.Handle < y.aProperty.Handle;
+ }
+ };
+ // comparing two property descriptions (by name)
+ struct PropertyDescriptionNameMatch
+ {
+ OUString const m_rCompare;
+ explicit PropertyDescriptionNameMatch( OUString _aCompare ) : m_rCompare(std::move( _aCompare )) { }
+
+ bool operator() (const PropertyDescription& x ) const
+ {
+ return x.aProperty.Name == m_rCompare;
+ }
+ };
+}
+
+OPropertyContainerHelper::OPropertyContainerHelper()
+{
+}
+
+
+OPropertyContainerHelper::~OPropertyContainerHelper()
+{
+}
+
+
+void OPropertyContainerHelper::registerProperty(const OUString& _rName, sal_Int32 _nHandle,
+ sal_Int32 _nAttributes, void* _pPointerToMember, const Type& _rMemberType)
+{
+ OSL_ENSURE((_nAttributes & PropertyAttribute::MAYBEVOID) == 0,
+ "OPropertyContainerHelper::registerProperty: don't use this for properties which may be void ! There is a method called \"registerMayBeVoidProperty\" for this !");
+ OSL_ENSURE(!_rMemberType.equals(cppu::UnoType<Any>::get()),
+ "OPropertyContainerHelper::registerProperty: don't give my the type of a uno::Any ! Really can't handle this !");
+ OSL_ENSURE(_pPointerToMember,
+ "OPropertyContainerHelper::registerProperty: you gave me nonsense : the pointer must be non-NULL");
+
+ PropertyDescription aNewProp;
+ aNewProp.aProperty = Property( _rName, _nHandle, _rMemberType, static_cast<sal_Int16>(_nAttributes) );
+ aNewProp.eLocated = PropertyDescription::LocationType::DerivedClassRealType;
+ aNewProp.aLocation.pDerivedClassMember = _pPointerToMember;
+
+ implPushBackProperty(aNewProp);
+}
+
+
+void OPropertyContainerHelper::revokeProperty( sal_Int32 _nHandle )
+{
+ PropertiesIterator aPos = searchHandle( _nHandle );
+ if ( aPos == m_aProperties.end() )
+ throw UnknownPropertyException(OUString::number(_nHandle));
+ m_aProperties.erase( aPos );
+}
+
+
+void OPropertyContainerHelper::registerMayBeVoidProperty(const OUString& _rName, sal_Int32 _nHandle, sal_Int32 _nAttributes,
+ Any* _pPointerToMember, const Type& _rExpectedType)
+{
+ OSL_ENSURE((_nAttributes & PropertyAttribute::MAYBEVOID) != 0,
+ "OPropertyContainerHelper::registerMayBeVoidProperty: why calling this when the attributes say nothing about may-be-void ?");
+ OSL_ENSURE(!_rExpectedType.equals(cppu::UnoType<Any>::get()),
+ "OPropertyContainerHelper::registerMayBeVoidProperty: don't give my the type of a uno::Any ! Really can't handle this !");
+ OSL_ENSURE(_pPointerToMember,
+ "OPropertyContainerHelper::registerMayBeVoidProperty: you gave me nonsense : the pointer must be non-NULL");
+
+ _nAttributes |= PropertyAttribute::MAYBEVOID;
+
+ PropertyDescription aNewProp;
+ aNewProp.aProperty = Property( _rName, _nHandle, _rExpectedType, static_cast<sal_Int16>(_nAttributes) );
+ aNewProp.eLocated = PropertyDescription::LocationType::DerivedClassAnyType;
+ aNewProp.aLocation.pDerivedClassMember = _pPointerToMember;
+
+ implPushBackProperty(aNewProp);
+}
+
+
+void OPropertyContainerHelper::registerPropertyNoMember(const OUString& _rName, sal_Int32 _nHandle, sal_Int32 _nAttributes,
+ const Type& _rType, css::uno::Any const & _pInitialValue)
+{
+ OSL_ENSURE(!_rType.equals(cppu::UnoType<Any>::get()),
+ "OPropertyContainerHelper::registerPropertyNoMember : don't give my the type of a uno::Any ! Really can't handle this !");
+ OSL_ENSURE(
+ (_pInitialValue.isExtractableTo(_rType)
+ || (!_pInitialValue.hasValue()
+ && (_nAttributes & PropertyAttribute::MAYBEVOID) != 0)),
+ "bad initial value");
+
+ PropertyDescription aNewProp;
+ aNewProp.aProperty = Property( _rName, _nHandle, _rType, static_cast<sal_Int16>(_nAttributes) );
+ aNewProp.eLocated = PropertyDescription::LocationType::HoldMyself;
+ aNewProp.aLocation.nOwnClassVectorIndex = m_aHoldProperties.size();
+ m_aHoldProperties.push_back(_pInitialValue);
+
+ implPushBackProperty(aNewProp);
+}
+
+
+bool OPropertyContainerHelper::isRegisteredProperty( sal_Int32 _nHandle ) const
+{
+ return const_cast< OPropertyContainerHelper* >( this )->searchHandle( _nHandle ) != m_aProperties.end();
+}
+
+
+bool OPropertyContainerHelper::isRegisteredProperty( const OUString& _rName ) const
+{
+ // TODO: the current structure is from a time where properties were
+ // static, not dynamic. Since we allow that properties are also dynamic,
+ // i.e. registered and revoked even though the XPropertySet has already been
+ // accessed, a vector is not really the best data structure anymore ...
+
+ return std::any_of(
+ m_aProperties.begin(),
+ m_aProperties.end(),
+ PropertyDescriptionNameMatch( _rName )
+ );
+}
+
+
+namespace
+{
+ struct ComparePropertyHandles
+ {
+ bool operator()( const PropertyDescription& _rLHS, const PropertyDescription& _nRHS ) const
+ {
+ return _rLHS.aProperty.Handle < _nRHS.aProperty.Handle;
+ }
+ };
+}
+
+
+void OPropertyContainerHelper::implPushBackProperty(const PropertyDescription& _rProp)
+{
+#ifdef DBG_UTIL
+ for (const auto& checkConflicts : m_aProperties)
+ {
+ OSL_ENSURE(checkConflicts.aProperty.Name != _rProp.aProperty.Name, "OPropertyContainerHelper::implPushBackProperty: name already exists!");
+ OSL_ENSURE(checkConflicts.aProperty.Handle != _rProp.aProperty.Handle, "OPropertyContainerHelper::implPushBackProperty: handle already exists!");
+ }
+#endif
+
+ PropertiesIterator pos = std::lower_bound(
+ m_aProperties.begin(), m_aProperties.end(),
+ _rProp, ComparePropertyHandles() );
+
+ m_aProperties.insert( pos, _rProp );
+}
+
+
+namespace
+{
+ void lcl_throwIllegalPropertyValueTypeException( const PropertyDescription& _rProperty, const Any& _rValue )
+ {
+ throw IllegalArgumentException(
+ "The given value cannot be converted to the required property type."
+ " (property name \"" + _rProperty.aProperty.Name
+ + "\", found value type \"" + _rValue.getValueType().getTypeName()
+ + "\", required property type \"" + _rProperty.aProperty.Type.getTypeName()
+ + "\")",
+ nullptr, 4 );
+ }
+}
+
+
+bool OPropertyContainerHelper::convertFastPropertyValue(
+ Any& _rConvertedValue, Any& _rOldValue, sal_Int32 _nHandle, const Any& _rValue )
+{
+ bool bModified = false;
+
+ // get the property somebody is asking for
+ PropertiesIterator aPos = searchHandle(_nHandle);
+ if (aPos == m_aProperties.end())
+ {
+ OSL_FAIL( "OPropertyContainerHelper::convertFastPropertyValue: unknown handle!" );
+ // should not happen if the derived class has built a correct property set info helper to be used by
+ // our base class OPropertySetHelper
+ return bModified;
+ }
+
+ switch (aPos->eLocated)
+ {
+ // similar handling for the two cases where the value is stored in an any
+ case PropertyDescription::LocationType::HoldMyself:
+ case PropertyDescription::LocationType::DerivedClassAnyType:
+ {
+ bool bMayBeVoid = ((aPos->aProperty.Attributes & PropertyAttribute::MAYBEVOID) != 0);
+
+
+ // non modifiable version of the value-to-be-set
+ Any aNewRequestedValue( _rValue );
+
+ // normalization
+ // #i29490#
+ if ( !aNewRequestedValue.getValueType().equals( aPos->aProperty.Type ) )
+ { // the actually given value is not of the same type as the one required
+ Any aProperlyTyped( nullptr, aPos->aProperty.Type.getTypeLibType() );
+
+ if ( uno_type_assignData(
+ const_cast< void* >( aProperlyTyped.getValue() ), aProperlyTyped.getValueType().getTypeLibType(),
+ const_cast< void* >( aNewRequestedValue.getValue() ), aNewRequestedValue.getValueType().getTypeLibType(),
+ reinterpret_cast< uno_QueryInterfaceFunc >( cpp_queryInterface ),
+ reinterpret_cast< uno_AcquireFunc >( cpp_acquire ),
+ reinterpret_cast< uno_ReleaseFunc >( cpp_release )
+ )
+ )
+ {
+ // we were able to query the given XInterface-derivee for the interface
+ // which is required for this property
+ aNewRequestedValue = aProperlyTyped;
+ }
+ }
+
+ // argument check
+ if ( ! ( (bMayBeVoid && !aNewRequestedValue.hasValue()) // void is allowed if the attribute says so
+ || (aNewRequestedValue.getValueType().equals(aPos->aProperty.Type)) // else the types have to be equal
+ )
+ )
+ {
+ lcl_throwIllegalPropertyValueTypeException( *aPos, _rValue );
+ }
+
+ Any* pPropContainer = nullptr;
+ // the pointer to the any which holds the property value, no matter if located in the derived class
+ // or in out vector
+
+ if (PropertyDescription::LocationType::HoldMyself == aPos->eLocated)
+ {
+ OSL_ENSURE(aPos->aLocation.nOwnClassVectorIndex < m_aHoldProperties.size(),
+ "OPropertyContainerHelper::convertFastPropertyValue: invalid position !");
+ auto aIter = m_aHoldProperties.begin() + aPos->aLocation.nOwnClassVectorIndex;
+ pPropContainer = &(*aIter);
+ }
+ else
+ pPropContainer = static_cast<Any*>(aPos->aLocation.pDerivedClassMember);
+
+ // check if the new value differs from the current one
+ if (!pPropContainer->hasValue() || !aNewRequestedValue.hasValue())
+ bModified = pPropContainer->hasValue() != aNewRequestedValue.hasValue();
+ else
+ bModified = !uno_type_equalData(
+ const_cast< void* >( pPropContainer->getValue() ), aPos->aProperty.Type.getTypeLibType(),
+ const_cast< void* >( aNewRequestedValue.getValue() ), aPos->aProperty.Type.getTypeLibType(),
+ reinterpret_cast< uno_QueryInterfaceFunc >( cpp_queryInterface ),
+ reinterpret_cast< uno_ReleaseFunc >( cpp_release )
+ );
+
+ if (bModified)
+ {
+ _rOldValue = *pPropContainer;
+ _rConvertedValue = aNewRequestedValue;
+ }
+ }
+ break;
+ case PropertyDescription::LocationType::DerivedClassRealType:
+ // let the UNO runtime library do any possible conversion
+ // this may include a change of the type - for instance, if a LONG is required,
+ // but a short is given, then this is valid, as it can be converted without any potential
+ // data loss
+
+ Any aProperlyTyped;
+ const Any* pNewValue = &_rValue;
+
+ if (!_rValue.getValueType().equals(aPos->aProperty.Type))
+ {
+ bool bConverted = false;
+
+ // a temporary any of the correct (required) type
+ aProperlyTyped = Any( nullptr, aPos->aProperty.Type.getTypeLibType() );
+ // (need this as we do not want to overwrite the derived class member here)
+
+ if ( uno_type_assignData(
+ const_cast<void*>(aProperlyTyped.getValue()), aProperlyTyped.getValueType().getTypeLibType(),
+ const_cast<void*>(_rValue.getValue()), _rValue.getValueType().getTypeLibType(),
+ reinterpret_cast< uno_QueryInterfaceFunc >( cpp_queryInterface ),
+ reinterpret_cast< uno_AcquireFunc >( cpp_acquire ),
+ reinterpret_cast< uno_ReleaseFunc >( cpp_release )
+ )
+ )
+ {
+ // could query for the requested interface
+ bConverted = true;
+ pNewValue = &aProperlyTyped;
+ }
+
+ if ( !bConverted )
+ lcl_throwIllegalPropertyValueTypeException( *aPos, _rValue );
+ }
+
+ // from here on, we should have the proper type
+ OSL_ENSURE( pNewValue->getValueType() == aPos->aProperty.Type,
+ "OPropertyContainerHelper::convertFastPropertyValue: conversion failed!" );
+ bModified = !uno_type_equalData(
+ aPos->aLocation.pDerivedClassMember, aPos->aProperty.Type.getTypeLibType(),
+ const_cast<void*>(pNewValue->getValue()), aPos->aProperty.Type.getTypeLibType(),
+ reinterpret_cast< uno_QueryInterfaceFunc >( cpp_queryInterface ),
+ reinterpret_cast< uno_ReleaseFunc >( cpp_release )
+ );
+
+ if (bModified)
+ {
+ _rOldValue.setValue(aPos->aLocation.pDerivedClassMember, aPos->aProperty.Type);
+ _rConvertedValue = *pNewValue;
+ }
+ break;
+ }
+
+ return bModified;
+}
+
+
+void OPropertyContainerHelper::setFastPropertyValue(sal_Int32 _nHandle, const Any& _rValue)
+{
+ // get the property somebody is asking for
+ PropertiesIterator aPos = searchHandle(_nHandle);
+ if (aPos == m_aProperties.end())
+ {
+ OSL_FAIL( "OPropertyContainerHelper::setFastPropertyValue: unknown handle!" );
+ // should not happen if the derived class has built a correct property set info helper to be used by
+ // our base class OPropertySetHelper
+ return;
+ }
+
+ bool bSuccess = true;
+
+ switch (aPos->eLocated)
+ {
+ case PropertyDescription::LocationType::HoldMyself:
+ m_aHoldProperties[aPos->aLocation.nOwnClassVectorIndex] = _rValue;
+ break;
+
+ case PropertyDescription::LocationType::DerivedClassAnyType:
+ *static_cast< Any* >(aPos->aLocation.pDerivedClassMember) = _rValue;
+ break;
+
+ case PropertyDescription::LocationType::DerivedClassRealType:
+ // copy the data from the to-be-set value
+ bSuccess = uno_type_assignData(
+ aPos->aLocation.pDerivedClassMember, aPos->aProperty.Type.getTypeLibType(),
+ const_cast< void* >( _rValue.getValue() ), _rValue.getValueType().getTypeLibType(),
+ reinterpret_cast< uno_QueryInterfaceFunc >( cpp_queryInterface ),
+ reinterpret_cast< uno_AcquireFunc >( cpp_acquire ),
+ reinterpret_cast< uno_ReleaseFunc >( cpp_release ) );
+
+ OSL_ENSURE( bSuccess,
+ "OPropertyContainerHelper::setFastPropertyValue: ooops... the value could not be assigned!");
+
+ break;
+ }
+}
+
+void OPropertyContainerHelper::getFastPropertyValue(Any& _rValue, sal_Int32 _nHandle) const
+{
+ // get the property somebody is asking for
+ PropertiesIterator aPos = const_cast<OPropertyContainerHelper*>(this)->searchHandle(_nHandle);
+ if (aPos == m_aProperties.end())
+ {
+ OSL_FAIL( "OPropertyContainerHelper::getFastPropertyValue: unknown handle!" );
+ // should not happen if the derived class has built a correct property set info helper to be used by
+ // our base class OPropertySetHelper
+ return;
+ }
+
+ switch (aPos->eLocated)
+ {
+ case PropertyDescription::LocationType::HoldMyself:
+ OSL_ENSURE(aPos->aLocation.nOwnClassVectorIndex < m_aHoldProperties.size(),
+ "OPropertyContainerHelper::convertFastPropertyValue: invalid position !");
+ _rValue = m_aHoldProperties[aPos->aLocation.nOwnClassVectorIndex];
+ break;
+ case PropertyDescription::LocationType::DerivedClassAnyType:
+ _rValue = *static_cast<Any*>(aPos->aLocation.pDerivedClassMember);
+ break;
+ case PropertyDescription::LocationType::DerivedClassRealType:
+ _rValue.setValue(aPos->aLocation.pDerivedClassMember, aPos->aProperty.Type);
+ break;
+ }
+}
+
+
+OPropertyContainerHelper::PropertiesIterator OPropertyContainerHelper::searchHandle(sal_Int32 _nHandle)
+{
+ PropertyDescription aHandlePropDesc;
+ aHandlePropDesc.aProperty.Handle = _nHandle;
+ // search a lower bound
+ PropertiesIterator aLowerBound = std::lower_bound(
+ m_aProperties.begin(),
+ m_aProperties.end(),
+ aHandlePropDesc,
+ PropertyDescriptionHandleCompare());
+
+ // check for identity
+ if ((aLowerBound != m_aProperties.end()) && aLowerBound->aProperty.Handle != _nHandle)
+ aLowerBound = m_aProperties.end();
+
+ return aLowerBound;
+}
+
+
+const Property& OPropertyContainerHelper::getProperty( const OUString& _rName ) const
+{
+ ConstPropertiesIterator pos = std::find_if(
+ m_aProperties.begin(),
+ m_aProperties.end(),
+ PropertyDescriptionNameMatch( _rName )
+ );
+ if ( pos == m_aProperties.end() )
+ throw UnknownPropertyException( _rName );
+
+ return pos->aProperty;
+}
+
+
+void OPropertyContainerHelper::describeProperties(Sequence< Property >& _rProps) const
+{
+ Sequence< Property > aOwnProps(m_aProperties.size());
+ Property* pOwnProps = aOwnProps.getArray();
+
+ for (const auto& rProp : m_aProperties)
+ {
+ pOwnProps->Name = rProp.aProperty.Name;
+ pOwnProps->Handle = rProp.aProperty.Handle;
+ pOwnProps->Attributes = rProp.aProperty.Attributes;
+ pOwnProps->Type = rProp.aProperty.Type;
+ ++pOwnProps;
+ }
+
+ // as our property vector is sorted by handles, not by name, we have to sort aOwnProps
+ auto [begin, end] = asNonConstRange(aOwnProps);
+ std::sort(begin, end, PropertyCompareByName());
+
+ // unfortunately the STL merge function does not allow the output range to overlap one of the input ranges,
+ // so we need an extra sequence
+ Sequence< Property > aOutput(_rProps.getLength() + aOwnProps.getLength());
+ // do the merge
+ std::merge( std::cbegin(_rProps), std::cend(_rProps), // input 1
+ std::cbegin(aOwnProps), std::cend(aOwnProps), // input 2
+ aOutput.getArray(), // output
+ PropertyCompareByName() // compare operator
+ );
+
+ // copy the output
+ _rProps = aOutput;
+}
+
+
+} // namespace comphelper
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/property/propertysethelper.cxx b/comphelper/source/property/propertysethelper.cxx
new file mode 100644
index 0000000000..519b0705fa
--- /dev/null
+++ b/comphelper/source/property/propertysethelper.cxx
@@ -0,0 +1,271 @@
+/* -*- 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 <comphelper/propertysetinfo.hxx>
+#include <comphelper/propertysethelper.hxx>
+#include <osl/diagnose.h>
+#include <rtl/ref.hxx>
+
+#include <memory>
+#include <utility>
+
+using namespace ::comphelper;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+
+static PropertyMapEntry const * find( const rtl::Reference<PropertySetInfo>& mxInfo, const OUString& aName ) noexcept
+{
+ PropertyMap::const_iterator aIter = mxInfo->getPropertyMap().find( aName );
+
+ if( mxInfo->getPropertyMap().end() != aIter )
+ return (*aIter).second;
+ else
+ return nullptr;
+}
+
+
+PropertySetHelper::PropertySetHelper( rtl::Reference<comphelper::PropertySetInfo> xInfo ) noexcept
+ : mxInfo(std::move(xInfo))
+{
+}
+
+PropertySetHelper::~PropertySetHelper() noexcept
+{
+}
+
+// XPropertySet
+Reference< XPropertySetInfo > SAL_CALL PropertySetHelper::getPropertySetInfo( )
+{
+ return mxInfo;
+}
+
+void SAL_CALL PropertySetHelper::setPropertyValue( const OUString& aPropertyName, const Any& aValue )
+{
+ PropertyMapEntry const * aEntries[2];
+ aEntries[0] = find( mxInfo, aPropertyName );
+
+ if( nullptr == aEntries[0] )
+ throw UnknownPropertyException( aPropertyName, static_cast< XPropertySet* >( this ) );
+
+ aEntries[1] = nullptr;
+
+ _setPropertyValues( aEntries, &aValue );
+}
+
+Any SAL_CALL PropertySetHelper::getPropertyValue( const OUString& PropertyName )
+{
+ PropertyMapEntry const * aEntries[2];
+ aEntries[0] = find( mxInfo, PropertyName );
+
+ if( nullptr == aEntries[0] )
+ throw UnknownPropertyException( PropertyName, static_cast< XPropertySet* >( this ) );
+
+ aEntries[1] = nullptr;
+
+ Any aAny;
+ _getPropertyValues( aEntries, &aAny );
+
+ return aAny;
+}
+
+void SAL_CALL PropertySetHelper::addPropertyChangeListener( const OUString&, const Reference< XPropertyChangeListener >& )
+{
+ // todo
+}
+
+void SAL_CALL PropertySetHelper::removePropertyChangeListener( const OUString&, const Reference< XPropertyChangeListener >& )
+{
+ // todo
+}
+
+void SAL_CALL PropertySetHelper::addVetoableChangeListener( const OUString&, const Reference< XVetoableChangeListener >& )
+{
+ // todo
+}
+
+void SAL_CALL PropertySetHelper::removeVetoableChangeListener( const OUString&, const Reference< XVetoableChangeListener >& )
+{
+ // todo
+}
+
+// XMultiPropertySet
+void SAL_CALL PropertySetHelper::setPropertyValues( const Sequence< OUString >& rPropertyNames, const Sequence< Any >& rValues )
+{
+ const sal_Int32 nCount = rPropertyNames.getLength();
+
+ if( nCount != rValues.getLength() )
+ throw IllegalArgumentException("lengths do not match", uno::Reference<uno::XInterface>(), -1);
+
+ if( !nCount )
+ return;
+
+ std::unique_ptr<PropertyMapEntry const *[]> pEntries(new PropertyMapEntry const *[nCount+1]);
+ pEntries[nCount] = nullptr;
+ const OUString* pNames = rPropertyNames.getConstArray();
+
+ bool bUnknown = false;
+ sal_Int32 n;
+ for( n = 0; !bUnknown && ( n < nCount ); n++, pNames++ )
+ {
+ pEntries[n] = find( mxInfo, *pNames );
+ bUnknown = nullptr == pEntries[n];
+ }
+
+ if( !bUnknown )
+ _setPropertyValues( pEntries.get(), rValues.getConstArray() );
+
+ if( bUnknown )
+ throw RuntimeException( *pNames, static_cast< XPropertySet* >( this ) );
+}
+
+Sequence< Any > SAL_CALL PropertySetHelper::getPropertyValues(const Sequence< OUString >& rPropertyNames)
+{
+ const sal_Int32 nCount = rPropertyNames.getLength();
+
+ if( !nCount )
+ return Sequence< Any >();
+
+ std::unique_ptr<PropertyMapEntry const *[]> pEntries(new PropertyMapEntry const *[nCount+1]);
+ const OUString* pNames = rPropertyNames.getConstArray();
+
+ bool bUnknown = false;
+ sal_Int32 n;
+ for( n = 0; !bUnknown && ( n < nCount ); n++, pNames++ )
+ {
+ pEntries[n] = find( mxInfo, *pNames );
+ bUnknown = nullptr == pEntries[n];
+ }
+
+ if( bUnknown )
+ throw RuntimeException( *pNames, static_cast< XPropertySet* >( this ) );
+
+ pEntries[nCount] = nullptr;
+ Sequence< Any > aValues(nCount);
+ aValues.realloc(nCount);
+ _getPropertyValues( pEntries.get(), aValues.getArray() );
+ return aValues;
+
+}
+
+void SAL_CALL PropertySetHelper::addPropertiesChangeListener( const Sequence< OUString >&, const Reference< XPropertiesChangeListener >& )
+{
+ // todo
+}
+
+void SAL_CALL PropertySetHelper::removePropertiesChangeListener( const Reference< XPropertiesChangeListener >& )
+{
+ // todo
+}
+
+void SAL_CALL PropertySetHelper::firePropertiesChangeEvent( const Sequence< OUString >&, const Reference< XPropertiesChangeListener >& )
+{
+ // todo
+}
+
+// XPropertyState
+PropertyState SAL_CALL PropertySetHelper::getPropertyState( const OUString& PropertyName )
+{
+ PropertyMapEntry const * aEntries[2];
+
+ aEntries[0] = find( mxInfo, PropertyName );
+ if( aEntries[0] == nullptr )
+ throw UnknownPropertyException( PropertyName, static_cast< XPropertySet* >( this ) );
+
+ aEntries[1] = nullptr;
+
+ PropertyState aState(PropertyState_AMBIGUOUS_VALUE);
+ _getPropertyStates( aEntries, &aState );
+
+ return aState;
+}
+
+Sequence< PropertyState > SAL_CALL PropertySetHelper::getPropertyStates( const Sequence< OUString >& aPropertyName )
+{
+ const sal_Int32 nCount = aPropertyName.getLength();
+
+ Sequence< PropertyState > aStates( nCount );
+
+ if( nCount )
+ {
+ const OUString* pNames = aPropertyName.getConstArray();
+
+ bool bUnknown = false;
+
+ std::unique_ptr<PropertyMapEntry const *[]> pEntries(new PropertyMapEntry const *[nCount+1]);
+
+ sal_Int32 n;
+ for( n = 0; !bUnknown && (n < nCount); n++, pNames++ )
+ {
+ pEntries[n] = find( mxInfo, *pNames );
+ bUnknown = nullptr == pEntries[n];
+ }
+
+ pEntries[nCount] = nullptr;
+
+ if( !bUnknown )
+ _getPropertyStates( pEntries.get(), aStates.getArray() );
+
+ if( bUnknown )
+ throw UnknownPropertyException( *pNames, static_cast< XPropertySet* >( this ) );
+ }
+
+ return aStates;
+}
+
+void SAL_CALL PropertySetHelper::setPropertyToDefault( const OUString& PropertyName )
+{
+ PropertyMapEntry const *pEntry = find(mxInfo, PropertyName );
+ if( nullptr == pEntry )
+ throw UnknownPropertyException( PropertyName, static_cast< XPropertySet* >( this ) );
+
+ _setPropertyToDefault( pEntry );
+}
+
+Any SAL_CALL PropertySetHelper::getPropertyDefault( const OUString& aPropertyName )
+{
+ PropertyMapEntry const * pEntry = find(mxInfo, aPropertyName );
+ if( nullptr == pEntry )
+ throw UnknownPropertyException( aPropertyName, static_cast< XPropertySet* >( this ) );
+
+ return _getPropertyDefault( pEntry );
+}
+
+void PropertySetHelper::_getPropertyStates(
+ const comphelper::PropertyMapEntry**, PropertyState*)
+{
+ OSL_FAIL( "you have to implement this yourself!");
+}
+
+void
+PropertySetHelper::_setPropertyToDefault(const comphelper::PropertyMapEntry*)
+{
+ OSL_FAIL( "you have to implement this yourself!");
+}
+
+Any PropertySetHelper::_getPropertyDefault(const comphelper::PropertyMapEntry*)
+{
+ OSL_FAIL( "you have to implement this yourself!");
+
+ Any aAny;
+ return aAny;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/property/propertysetinfo.cxx b/comphelper/source/property/propertysetinfo.cxx
new file mode 100644
index 0000000000..d288ae2126
--- /dev/null
+++ b/comphelper/source/property/propertysetinfo.cxx
@@ -0,0 +1,120 @@
+/* -*- 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 <comphelper/propertysetinfo.hxx>
+#include <comphelper/sequence.hxx>
+
+
+using namespace ::comphelper;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+
+PropertySetInfo::PropertySetInfo() noexcept
+{
+}
+
+PropertySetInfo::PropertySetInfo( std::span<const PropertyMapEntry> pMap ) noexcept
+{
+ maPropertyMap.reserve(pMap.size());
+ for (const auto & rEntry : pMap)
+ {
+ // check for duplicates
+ assert(maPropertyMap.find(rEntry.maName) == maPropertyMap.end());
+ // Make sure there are no accidental empty entries left at the end of the array from
+ // when this method used to take a empty-terminated array.
+ assert(!rEntry.maName.isEmpty());
+
+ maPropertyMap.emplace(rEntry.maName, &rEntry);
+ }
+}
+
+PropertySetInfo::~PropertySetInfo() noexcept
+{
+}
+
+void PropertySetInfo::add( std::span<PropertyMapEntry const> pMap ) noexcept
+{
+ maPropertyMap.reserve(maPropertyMap.size() + pMap.size());
+ for (const auto & rEntry : pMap)
+ {
+ // check for duplicates
+ assert(maPropertyMap.find(rEntry.maName) == maPropertyMap.end());
+ // Make sure there are no accidental empty entries left at the end of the array from
+ // when this method used to take a empty-terminated array.
+ assert(!rEntry.maName.isEmpty());
+
+ maPropertyMap.emplace(rEntry.maName, &rEntry);
+ }
+
+ // clear cache
+ maProperties.realloc(0);
+}
+
+void PropertySetInfo::remove( const OUString& aName ) noexcept
+{
+ maPropertyMap.erase( aName );
+ maProperties.realloc(0);
+}
+
+Sequence< css::beans::Property > SAL_CALL PropertySetInfo::getProperties()
+{
+ // maybe we have to generate the properties after
+ // a change in the property map or at first call
+ // to getProperties
+ if( maProperties.size() != maPropertyMap.size() )
+ {
+ maProperties.realloc( maPropertyMap.size() );
+ auto propIter = maProperties.getArray();
+
+ for( const auto& rProperty : maPropertyMap )
+ {
+ PropertyMapEntry const * pEntry = rProperty.second;
+
+ propIter->Name = pEntry->maName;
+ propIter->Handle = pEntry->mnHandle;
+ propIter->Type = pEntry->maType;
+ propIter->Attributes = pEntry->mnAttributes;
+
+ ++propIter;
+ }
+ }
+ return maProperties;
+}
+
+Property SAL_CALL PropertySetInfo::getPropertyByName( const OUString& aName )
+{
+ PropertyMap::iterator aIter = maPropertyMap.find( aName );
+
+ if( maPropertyMap.end() == aIter )
+ throw UnknownPropertyException( aName );
+
+ PropertyMapEntry const * pEntry = (*aIter).second;
+
+ return Property( aName, pEntry->mnHandle, pEntry->maType, pEntry->mnAttributes );
+}
+
+sal_Bool SAL_CALL PropertySetInfo::hasPropertyByName( const OUString& aName )
+{
+ return maPropertyMap.find( aName ) != maPropertyMap.end();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/property/propertystatecontainer.cxx b/comphelper/source/property/propertystatecontainer.cxx
new file mode 100644
index 0000000000..e19e787336
--- /dev/null
+++ b/comphelper/source/property/propertystatecontainer.cxx
@@ -0,0 +1,183 @@
+/* -*- 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 <string_view>
+
+#include <comphelper/propertystatecontainer.hxx>
+
+
+namespace comphelper
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::lang;
+
+ namespace
+ {
+ OUString lcl_getUnknownPropertyErrorMessage( std::u16string_view _rPropertyName )
+ {
+ // TODO: perhaps it's time to think about resources in the comphelper module?
+ // Would be nice to have localized exception strings (a simply resource file containing
+ // strings only would suffice, and could be realized with a UNO service, so we do not
+ // need the dependency to the Tools project)
+ return OUString::Concat("The property \"") + _rPropertyName + "\" is unknown.";
+ }
+ }
+
+ OPropertyStateContainer::OPropertyStateContainer( ::cppu::OBroadcastHelper& _rBHelper )
+ :OPropertyContainer( _rBHelper )
+ {
+ }
+
+
+ Any SAL_CALL OPropertyStateContainer::queryInterface( const Type& _rType )
+ {
+ Any aReturn = OPropertyContainer::queryInterface( _rType );
+ if ( !aReturn.hasValue() )
+ aReturn = OPropertyStateContainer_TBase::queryInterface( _rType );
+ return aReturn;
+ }
+
+
+ IMPLEMENT_FORWARD_XTYPEPROVIDER2( OPropertyStateContainer, OPropertyContainer, OPropertyStateContainer_TBase )
+
+
+ sal_Int32 OPropertyStateContainer::getHandleForName( const OUString& _rPropertyName )
+ {
+ // look up the handle for the name
+ ::cppu::IPropertyArrayHelper& rPH = getInfoHelper();
+ sal_Int32 nHandle = rPH.getHandleByName( _rPropertyName );
+
+ if ( -1 == nHandle )
+ throw UnknownPropertyException( lcl_getUnknownPropertyErrorMessage( _rPropertyName ), static_cast< XPropertyState* >( this ) );
+
+ return nHandle;
+ }
+
+
+ PropertyState SAL_CALL OPropertyStateContainer::getPropertyState( const OUString& _rPropertyName )
+ {
+ return getPropertyStateByHandle( getHandleForName( _rPropertyName ) );
+ }
+
+
+ Sequence< PropertyState > SAL_CALL OPropertyStateContainer::getPropertyStates( const Sequence< OUString >& _rPropertyNames )
+ {
+ sal_Int32 nProperties = _rPropertyNames.getLength();
+ Sequence< PropertyState> aStates( nProperties );
+ if ( !nProperties )
+ return aStates;
+
+#ifdef DBG_UTIL
+ // precondition: property sequence is sorted (the algorithm below relies on this)
+ {
+ const OUString* pNames = _rPropertyNames.getConstArray();
+ const OUString* pNamesCompare = pNames + 1;
+ const OUString* pNamesEnd = _rPropertyNames.getConstArray() + _rPropertyNames.getLength();
+ for ( ; pNamesCompare != pNamesEnd; ++pNames, ++pNamesCompare )
+ OSL_PRECOND( pNames->compareTo( *pNamesCompare ) < 0,
+ "OPropertyStateContainer::getPropertyStates: property sequence not sorted!" );
+ }
+#endif
+
+ const OUString* pLookup = _rPropertyNames.getConstArray();
+ const OUString* pLookupEnd = pLookup + nProperties;
+ PropertyState* pStates = aStates.getArray();
+
+ cppu::IPropertyArrayHelper& rHelper = getInfoHelper();
+ Sequence< Property> aAllProperties = rHelper.getProperties();
+ sal_Int32 nAllProperties = aAllProperties.getLength();
+ const Property* pAllProperties = aAllProperties.getConstArray();
+ const Property* pAllPropertiesEnd = pAllProperties + nAllProperties;
+
+ osl::MutexGuard aGuard( rBHelper.rMutex );
+ for ( ; ( pAllProperties != pAllPropertiesEnd ) && ( pLookup != pLookupEnd ); ++pAllProperties )
+ {
+#ifdef DBG_UTIL
+ if ( pAllProperties < pAllPropertiesEnd - 1 )
+ OSL_ENSURE( pAllProperties->Name.compareTo( (pAllProperties + 1)->Name ) < 0,
+ "OPropertyStateContainer::getPropertyStates: all-properties not sorted!" );
+#endif
+ if ( pAllProperties->Name == *pLookup )
+ {
+ *pStates++ = getPropertyState( *pLookup );
+ ++pLookup;
+ }
+ }
+
+ if ( pLookup != pLookupEnd )
+ // we run out of properties from the IPropertyArrayHelper, but still have properties to lookup
+ // -> we were asked for a nonexistent property
+ throw UnknownPropertyException( lcl_getUnknownPropertyErrorMessage( *pLookup ), static_cast< XPropertyState* >( this ) );
+
+ return aStates;
+ }
+
+
+ void SAL_CALL OPropertyStateContainer::setPropertyToDefault( const OUString& _rPropertyName )
+ {
+ setPropertyToDefaultByHandle( getHandleForName( _rPropertyName ) );
+ }
+
+
+ Any SAL_CALL OPropertyStateContainer::getPropertyDefault( const OUString& _rPropertyName )
+ {
+ Any aDefault;
+ getPropertyDefaultByHandle( getHandleForName( _rPropertyName ), aDefault );
+ return aDefault;
+ }
+
+
+ PropertyState OPropertyStateContainer::getPropertyStateByHandle( sal_Int32 _nHandle ) const
+ {
+ // simply compare the current and the default value
+ Any aCurrentValue;
+ getFastPropertyValue( aCurrentValue, _nHandle );
+ Any aDefaultValue;
+ getPropertyDefaultByHandle( _nHandle, aDefaultValue );
+
+ bool bEqual = uno_type_equalData(
+ const_cast< void* >( aCurrentValue.getValue() ), aCurrentValue.getValueType().getTypeLibType(),
+ const_cast< void* >( aDefaultValue.getValue() ), aDefaultValue.getValueType().getTypeLibType(),
+ reinterpret_cast< uno_QueryInterfaceFunc >(cpp_queryInterface),
+ reinterpret_cast< uno_ReleaseFunc >(cpp_release)
+ );
+ if ( bEqual )
+ return PropertyState_DEFAULT_VALUE;
+ else
+ return PropertyState_DIRECT_VALUE;
+ }
+
+
+ void OPropertyStateContainer::setPropertyToDefaultByHandle( sal_Int32 _nHandle )
+ {
+ Any aDefault;
+ getPropertyDefaultByHandle( _nHandle, aDefault );
+ setFastPropertyValue( _nHandle, aDefault );
+ }
+
+
+} // namespace comphelper
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/property/propmultiplex.cxx b/comphelper/source/property/propmultiplex.cxx
new file mode 100644
index 0000000000..66a1545f85
--- /dev/null
+++ b/comphelper/source/property/propmultiplex.cxx
@@ -0,0 +1,153 @@
+/* -*- 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 <com/sun/star/beans/XPropertySet.hpp>
+#include <comphelper/propmultiplex.hxx>
+#include <osl/diagnose.h>
+
+
+namespace comphelper
+{
+
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::beans;
+
+OPropertyChangeListener::~OPropertyChangeListener()
+{
+ if (m_xAdapter.is())
+ m_xAdapter->dispose();
+}
+
+
+void OPropertyChangeListener::_disposing(const EventObject&)
+{
+ // nothing to do here
+}
+
+
+void OPropertyChangeListener::disposeAdapter()
+{
+ if ( m_xAdapter.is() )
+ m_xAdapter->dispose();
+
+ // will automatically set a new adapter
+ OSL_ENSURE( !m_xAdapter.is(), "OPropertyChangeListener::disposeAdapter: what did dispose do?" );
+}
+
+
+void OPropertyChangeListener::setAdapter(OPropertyChangeMultiplexer* pAdapter)
+{
+ ::osl::MutexGuard aGuard(m_rMutex);
+ m_xAdapter = pAdapter;
+}
+
+OPropertyChangeMultiplexer::OPropertyChangeMultiplexer(OPropertyChangeListener* _pListener, const Reference< XPropertySet>& _rxSet, bool _bAutoReleaseSet)
+ :m_xSet(_rxSet)
+ ,m_pListener(_pListener)
+ ,m_nLockCount(0)
+ ,m_bListening(false)
+ ,m_bAutoSetRelease(_bAutoReleaseSet)
+{
+ m_pListener->setAdapter(this);
+}
+
+
+OPropertyChangeMultiplexer::~OPropertyChangeMultiplexer()
+{
+}
+
+
+void OPropertyChangeMultiplexer::lock()
+{
+ ++m_nLockCount;
+}
+
+
+void OPropertyChangeMultiplexer::unlock()
+{
+ --m_nLockCount;
+}
+
+
+void OPropertyChangeMultiplexer::dispose()
+{
+ if (!m_bListening)
+ return;
+
+ Reference< XPropertyChangeListener> xPreventDelete(this);
+
+ for (const OUString& rProp : m_aProperties)
+ m_xSet->removePropertyChangeListener(rProp, static_cast< XPropertyChangeListener*>(this));
+
+ m_pListener->setAdapter(nullptr);
+
+ m_pListener = nullptr;
+ m_bListening = false;
+
+ if (m_bAutoSetRelease)
+ m_xSet = nullptr;
+}
+
+// XEventListener
+
+void SAL_CALL OPropertyChangeMultiplexer::disposing( const EventObject& _rSource)
+{
+ if (m_pListener)
+ {
+ // tell the listener
+ if (!locked())
+ m_pListener->_disposing(_rSource);
+ // disconnect the listener
+ if (m_pListener) // may have been reset whilst calling into _disposing
+ m_pListener->setAdapter(nullptr);
+ }
+
+ m_pListener = nullptr;
+ m_bListening = false;
+
+ if (m_bAutoSetRelease)
+ m_xSet = nullptr;
+}
+
+// XPropertyChangeListener
+
+void SAL_CALL OPropertyChangeMultiplexer::propertyChange( const PropertyChangeEvent& _rEvent )
+{
+ if (m_pListener && !locked())
+ m_pListener->_propertyChanged(_rEvent);
+}
+
+
+void OPropertyChangeMultiplexer::addProperty(const OUString& _sPropertyName)
+{
+ if (m_xSet.is())
+ {
+ m_xSet->addPropertyChangeListener(_sPropertyName, static_cast< XPropertyChangeListener*>(this));
+ m_aProperties.push_back(_sPropertyName);
+ m_bListening = true;
+ }
+}
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/property/propmultiplex2.cxx b/comphelper/source/property/propmultiplex2.cxx
new file mode 100644
index 0000000000..b9d7719c4e
--- /dev/null
+++ b/comphelper/source/property/propmultiplex2.cxx
@@ -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 .
+ */
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <comphelper/propmultiplex2.hxx>
+#include <osl/diagnose.h>
+
+namespace comphelper
+{
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::beans;
+
+OPropertyChangeListener2::~OPropertyChangeListener2()
+{
+ if (m_xAdapter.is())
+ m_xAdapter->onListenerDestruction();
+}
+
+void OPropertyChangeListener2::disposeAdapter(std::unique_lock<std::mutex>& rGuard)
+{
+ if (m_xAdapter.is())
+ m_xAdapter->dispose(rGuard);
+
+ // will automatically set a new adapter
+ OSL_ENSURE(!m_xAdapter.is(), "OPropertyChangeListener::disposeAdapter: what did dispose do?");
+}
+
+void OPropertyChangeListener2::setAdapter(std::unique_lock<std::mutex>& /*rGuard*/,
+ OPropertyChangeMultiplexer2* pAdapter)
+{
+ m_xAdapter = pAdapter;
+}
+
+OPropertyChangeMultiplexer2::OPropertyChangeMultiplexer2(std::mutex& rMutex,
+ std::unique_lock<std::mutex>& rGuard,
+ OPropertyChangeListener2* _pListener,
+ const Reference<XPropertySet>& _rxSet)
+ : m_rMutex(rMutex)
+ , m_xSet(_rxSet)
+ , m_pListener(_pListener)
+ , m_nLockCount(0)
+ , m_bListening(false)
+{
+ m_pListener->setAdapter(rGuard, this);
+}
+
+OPropertyChangeMultiplexer2::~OPropertyChangeMultiplexer2() {}
+
+void OPropertyChangeMultiplexer2::lock() { ++m_nLockCount; }
+
+void OPropertyChangeMultiplexer2::unlock() { --m_nLockCount; }
+
+void OPropertyChangeMultiplexer2::dispose(std::unique_lock<std::mutex>& rGuard)
+{
+ if (!m_bListening)
+ return;
+
+ Reference<XPropertyChangeListener> xPreventDelete(this);
+
+ for (const OUString& rProp : m_aProperties)
+ m_xSet->removePropertyChangeListener(rProp, static_cast<XPropertyChangeListener*>(this));
+
+ m_pListener->setAdapter(rGuard, nullptr);
+
+ m_pListener = nullptr;
+ m_bListening = false;
+
+ m_xSet = nullptr;
+}
+
+void OPropertyChangeMultiplexer2::onListenerDestruction()
+{
+ if (!m_bListening)
+ return;
+
+ Reference<XPropertyChangeListener> xPreventDelete(this);
+
+ for (const OUString& rProp : m_aProperties)
+ m_xSet->removePropertyChangeListener(rProp, static_cast<XPropertyChangeListener*>(this));
+}
+
+// XEventListener
+
+void SAL_CALL OPropertyChangeMultiplexer2::disposing(const EventObject& /*_rSource*/)
+{
+ std::unique_lock g(m_rMutex);
+ if (m_pListener)
+ {
+ // disconnect the listener
+ if (m_pListener) // may have been reset whilst calling into _disposing
+ m_pListener->setAdapter(g, nullptr);
+ }
+
+ m_pListener = nullptr;
+ m_bListening = false;
+
+ m_xSet = nullptr;
+}
+
+// XPropertyChangeListener
+
+void SAL_CALL OPropertyChangeMultiplexer2::propertyChange(const PropertyChangeEvent& _rEvent)
+{
+ if (m_pListener && !locked())
+ m_pListener->_propertyChanged(_rEvent);
+}
+
+void OPropertyChangeMultiplexer2::addProperty(const OUString& _sPropertyName)
+{
+ if (m_xSet.is())
+ {
+ m_xSet->addPropertyChangeListener(_sPropertyName,
+ static_cast<XPropertyChangeListener*>(this));
+ m_aProperties.push_back(_sPropertyName);
+ m_bListening = true;
+ }
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/property/propshlp.cxx b/comphelper/source/property/propshlp.cxx
new file mode 100644
index 0000000000..0ae1b789bd
--- /dev/null
+++ b/comphelper/source/property/propshlp.cxx
@@ -0,0 +1,857 @@
+/* -*- 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 <osl/diagnose.h>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <comphelper/propshlp.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <memory>
+#include <sal/log.hxx>
+
+using namespace osl;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::lang;
+using namespace cppu;
+
+namespace comphelper
+{
+extern "C" {
+
+static int compare_OUString_Property_Impl(const void* arg1, const void* arg2) SAL_THROW_EXTERN_C()
+{
+ return static_cast<OUString const*>(arg1)->compareTo(static_cast<Property const*>(arg2)->Name);
+}
+}
+
+/**
+ * The class which implements the PropertySetInfo interface.
+ */
+
+namespace
+{
+class OPropertySetHelperInfo_Impl : public WeakImplHelper<css::beans::XPropertySetInfo>
+{
+ Sequence<Property> aInfos;
+
+public:
+ explicit OPropertySetHelperInfo_Impl(IPropertyArrayHelper& rHelper_);
+
+ // XPropertySetInfo-methods
+ virtual Sequence<Property> SAL_CALL getProperties() override;
+ virtual Property SAL_CALL getPropertyByName(const OUString& PropertyName) override;
+ virtual sal_Bool SAL_CALL hasPropertyByName(const OUString& PropertyName) override;
+};
+}
+
+/**
+ * Create an object that implements XPropertySetInfo IPropertyArrayHelper.
+ */
+OPropertySetHelperInfo_Impl::OPropertySetHelperInfo_Impl(IPropertyArrayHelper& rHelper_)
+ : aInfos(rHelper_.getProperties())
+{
+}
+
+/**
+ * Return the sequence of properties, which are provided through the constructor.
+ */
+Sequence<Property> OPropertySetHelperInfo_Impl::getProperties() { return aInfos; }
+
+/**
+ * Return the sequence of properties, which are provided through the constructor.
+ */
+Property OPropertySetHelperInfo_Impl::getPropertyByName(const OUString& PropertyName)
+{
+ Property* pR
+ = static_cast<Property*>(bsearch(&PropertyName, aInfos.getConstArray(), aInfos.getLength(),
+ sizeof(Property), compare_OUString_Property_Impl));
+ if (!pR)
+ throw UnknownPropertyException(PropertyName);
+
+ return *pR;
+}
+
+/**
+ * Return the sequence of properties, which are provided through the constructor.
+ */
+sal_Bool OPropertySetHelperInfo_Impl::hasPropertyByName(const OUString& PropertyName)
+{
+ Property* pR
+ = static_cast<Property*>(bsearch(&PropertyName, aInfos.getConstArray(), aInfos.getLength(),
+ sizeof(Property), compare_OUString_Property_Impl));
+ return pR != nullptr;
+}
+
+OPropertySetHelper::OPropertySetHelper() {}
+
+OPropertySetHelper::OPropertySetHelper(bool bIgnoreRuntimeExceptionsWhileFiring)
+ : m_bIgnoreRuntimeExceptionsWhileFiring(bIgnoreRuntimeExceptionsWhileFiring)
+{
+}
+
+/**
+ * You must call disposing before.
+ */
+OPropertySetHelper::~OPropertySetHelper() {}
+
+// XInterface
+Any OPropertySetHelper::queryInterface(const css::uno::Type& rType)
+{
+ return ::cppu::queryInterface(rType, static_cast<XPropertySet*>(this),
+ static_cast<XMultiPropertySet*>(this),
+ static_cast<XFastPropertySet*>(this));
+}
+
+/**
+ * called from the derivee's XTypeProvider::getTypes implementation
+ */
+css::uno::Sequence<css::uno::Type> OPropertySetHelper::getTypes()
+{
+ return { UnoType<css::beans::XPropertySet>::get(),
+ UnoType<css::beans::XMultiPropertySet>::get(),
+ UnoType<css::beans::XFastPropertySet>::get() };
+}
+
+// ComponentHelper
+void OPropertySetHelper::disposing(std::unique_lock<std::mutex>& rGuard)
+{
+ // Create an event with this as sender
+ Reference<XPropertySet> rSource = this;
+ EventObject aEvt;
+ aEvt.Source = rSource;
+
+ // inform all listeners to release this object
+ // The listener containers are automatically cleared
+ aBoundLC.disposeAndClear(rGuard, aEvt);
+ aVetoableLC.disposeAndClear(rGuard, aEvt);
+}
+
+Reference<XPropertySetInfo>
+OPropertySetHelper::createPropertySetInfo(IPropertyArrayHelper& rProperties)
+{
+ return new OPropertySetHelperInfo_Impl(rProperties);
+}
+
+// XPropertySet
+void OPropertySetHelper::setPropertyValue(const OUString& rPropertyName, const Any& rValue)
+{
+ // get the map table
+ IPropertyArrayHelper& rPH = getInfoHelper();
+ // map the name to the handle
+ sal_Int32 nHandle = rPH.getHandleByName(rPropertyName);
+ std::unique_lock aGuard(m_aMutex);
+ setFastPropertyValueImpl(aGuard, nHandle, rValue);
+}
+
+// XPropertySet
+Any OPropertySetHelper::getPropertyValue(const OUString& rPropertyName)
+{
+ std::unique_lock aGuard(m_aMutex);
+ return getPropertyValueImpl(aGuard, rPropertyName);
+}
+
+Any OPropertySetHelper::getPropertyValueImpl(std::unique_lock<std::mutex>& rGuard,
+ const OUString& rPropertyName)
+{
+ // get the map table
+ IPropertyArrayHelper& rPH = getInfoHelper();
+ // map the name to the handle
+ sal_Int32 nHandle = rPH.getHandleByName(rPropertyName);
+ // call the method of the XFastPropertySet interface
+ Any aAny;
+ getFastPropertyValue(rGuard, aAny, nHandle);
+ return aAny;
+}
+
+// XPropertySet
+void OPropertySetHelper::addPropertyChangeListener(
+ const OUString& rPropertyName, const Reference<XPropertyChangeListener>& rxListener)
+{
+ std::unique_lock aGuard(m_aMutex);
+ OSL_ENSURE(!m_bDisposed, "object is disposed");
+ if (m_bDisposed)
+ return;
+
+ // only add listeners if you are not disposed
+ // a listener with no name means all properties
+ if (!rPropertyName.isEmpty())
+ {
+ // get the map table
+ IPropertyArrayHelper& rPH = getInfoHelper();
+ // map the name to the handle
+ sal_Int32 nHandle = rPH.getHandleByName(rPropertyName);
+ if (nHandle == -1)
+ {
+ // property not known throw exception
+ throw UnknownPropertyException(rPropertyName);
+ }
+
+ sal_Int16 nAttributes;
+ rPH.fillPropertyMembersByHandle(nullptr, &nAttributes, nHandle);
+ if (!(nAttributes & css::beans::PropertyAttribute::BOUND))
+ {
+ OSL_FAIL("add listener to an unbound property");
+ // silent ignore this
+ return;
+ }
+ // add the change listener to the helper container
+ aBoundLC.addInterface(aGuard, nHandle, rxListener);
+ }
+ else
+ // add the change listener to the helper container
+ maPropertyChangeListeners.addInterface(aGuard, rxListener);
+}
+
+// XPropertySet
+void OPropertySetHelper::removePropertyChangeListener(
+ const OUString& rPropertyName, const Reference<XPropertyChangeListener>& rxListener)
+{
+ std::unique_lock aGuard(m_aMutex);
+ OSL_ENSURE(!m_bDisposed, "object is disposed");
+ // all listeners are automatically released in a dispose call
+ if (m_bDisposed)
+ return;
+
+ if (!rPropertyName.isEmpty())
+ {
+ // get the map table
+ IPropertyArrayHelper& rPH = getInfoHelper();
+ // map the name to the handle
+ sal_Int32 nHandle = rPH.getHandleByName(rPropertyName);
+ if (nHandle == -1)
+ // property not known throw exception
+ throw UnknownPropertyException(rPropertyName);
+ aBoundLC.removeInterface(aGuard, nHandle, rxListener);
+ }
+ else
+ {
+ // remove the change listener to the helper container
+ maPropertyChangeListeners.removeInterface(aGuard, rxListener);
+ }
+}
+
+// XPropertySet
+void OPropertySetHelper::addVetoableChangeListener(
+ const OUString& rPropertyName, const Reference<XVetoableChangeListener>& rxListener)
+{
+ std::unique_lock aGuard(m_aMutex);
+ OSL_ENSURE(!m_bDisposed, "object is disposed");
+ if (m_bDisposed)
+ return;
+
+ // only add listeners if you are not disposed
+ // a listener with no name means all properties
+ if (!rPropertyName.isEmpty())
+ {
+ // get the map table
+ IPropertyArrayHelper& rPH = getInfoHelper();
+ // map the name to the handle
+ sal_Int32 nHandle = rPH.getHandleByName(rPropertyName);
+ if (nHandle == -1)
+ {
+ // property not known throw exception
+ throw UnknownPropertyException(rPropertyName);
+ }
+
+ sal_Int16 nAttributes;
+ rPH.fillPropertyMembersByHandle(nullptr, &nAttributes, nHandle);
+ if (!(nAttributes & PropertyAttribute::CONSTRAINED))
+ {
+ OSL_FAIL("addVetoableChangeListener, and property is not constrained");
+ // silent ignore this
+ return;
+ }
+ // add the vetoable listener to the helper container
+ aVetoableLC.addInterface(aGuard, nHandle, rxListener);
+ }
+ else
+ // add the vetoable listener to the helper container
+ maVetoableChangeListeners.addInterface(aGuard, rxListener);
+}
+
+// XPropertySet
+void OPropertySetHelper::removeVetoableChangeListener(
+ const OUString& rPropertyName, const Reference<XVetoableChangeListener>& rxListener)
+{
+ std::unique_lock aGuard(m_aMutex);
+ OSL_ENSURE(!m_bDisposed, "object is disposed");
+ // all listeners are automatically released in a dispose call
+ if (m_bDisposed)
+ return;
+
+ if (!rPropertyName.isEmpty())
+ {
+ // get the map table
+ IPropertyArrayHelper& rPH = getInfoHelper();
+ // map the name to the handle
+ sal_Int32 nHandle = rPH.getHandleByName(rPropertyName);
+ if (nHandle == -1)
+ {
+ // property not known throw exception
+ throw UnknownPropertyException(rPropertyName);
+ }
+ // remove the vetoable listener to the helper container
+ aVetoableLC.removeInterface(aGuard, nHandle, rxListener);
+ }
+ else
+ // add the vetoable listener to the helper container
+ maVetoableChangeListeners.removeInterface(aGuard, rxListener);
+}
+
+void OPropertySetHelper::setDependentFastPropertyValue(std::unique_lock<std::mutex>& rGuard,
+ sal_Int32 i_handle,
+ const css::uno::Any& i_value)
+{
+ sal_Int16 nAttributes(0);
+ IPropertyArrayHelper& rInfo = getInfoHelper();
+ if (!rInfo.fillPropertyMembersByHandle(nullptr, &nAttributes, i_handle))
+ // unknown property
+ throw UnknownPropertyException(OUString::number(i_handle));
+
+ // no need to check for READONLY-ness of the property. The method is intended to be called internally, which
+ // implies it might be invoked for properties which are read-only to the instance's clients, but well allowed
+ // to change their value.
+
+ Any aConverted, aOld;
+ bool bChanged = convertFastPropertyValue(rGuard, aConverted, aOld, i_handle, i_value);
+ if (!bChanged)
+ return;
+
+ // don't fire vetoable events. This method is called with our mutex locked, so calling into listeners would not be
+ // a good idea. The caller is responsible for not invoking this for constrained properties.
+ OSL_ENSURE((nAttributes & PropertyAttribute::CONSTRAINED) == 0,
+ "OPropertySetHelper::setDependentFastPropertyValue: not to be used for constrained "
+ "properties!");
+
+ // actually set the new value
+ try
+ {
+ setFastPropertyValue_NoBroadcast(rGuard, i_handle, aConverted);
+ }
+ catch (const UnknownPropertyException&)
+ {
+ throw; /* allowed to leave */
+ }
+ catch (const PropertyVetoException&)
+ {
+ throw; /* allowed to leave */
+ }
+ catch (const IllegalArgumentException&)
+ {
+ throw; /* allowed to leave */
+ }
+ catch (const WrappedTargetException&)
+ {
+ throw; /* allowed to leave */
+ }
+ catch (const RuntimeException&)
+ {
+ throw; /* allowed to leave */
+ }
+ catch (const Exception&)
+ {
+ // not allowed to leave this method
+ WrappedTargetException aWrapped;
+ aWrapped.TargetException = ::cppu::getCaughtException();
+ aWrapped.Context = static_cast<XPropertySet*>(this);
+ throw aWrapped;
+ }
+
+ // remember the handle/values, for the events to be fired later
+ m_handles.push_back(i_handle);
+ m_newValues.push_back(
+ aConverted); // TODO: setFastPropertyValue notifies the unconverted value here ...?
+ m_oldValues.push_back(aOld);
+}
+
+// XFastPropertySet
+void OPropertySetHelper::setFastPropertyValue(sal_Int32 nHandle, const Any& rValue)
+{
+ std::unique_lock aGuard(m_aMutex);
+ setFastPropertyValueImpl(aGuard, nHandle, rValue);
+}
+
+void OPropertySetHelper::setFastPropertyValueImpl(std::unique_lock<std::mutex>& rGuard,
+ sal_Int32 nHandle, const Any& rValue)
+{
+ OSL_ENSURE(!m_bDisposed, "object is disposed");
+
+ IPropertyArrayHelper& rInfo = getInfoHelper();
+ sal_Int16 nAttributes;
+ if (!rInfo.fillPropertyMembersByHandle(nullptr, &nAttributes, nHandle))
+ {
+ // unknown property
+ throw UnknownPropertyException(OUString::number(nHandle));
+ }
+ if (nAttributes & PropertyAttribute::READONLY)
+ throw PropertyVetoException();
+
+ Any aConvertedVal;
+ Any aOldVal;
+
+ // Will the property change?
+ bool bChanged = convertFastPropertyValue(rGuard, aConvertedVal, aOldVal, nHandle, rValue);
+ if (!bChanged)
+ return;
+
+ // Is it a constrained property?
+ if (nAttributes & PropertyAttribute::CONSTRAINED)
+ {
+ // In aValue is the converted rValue
+ // fire a constrained event
+ // second parameter NULL means constrained
+ fire(rGuard, &nHandle, &rValue, &aOldVal, 1, true);
+ }
+
+ try
+ {
+ // set the property to the new value
+ setFastPropertyValue_NoBroadcast(rGuard, nHandle, aConvertedVal);
+ }
+ catch (const css::beans::UnknownPropertyException&)
+ {
+ throw; /* allowed to leave */
+ }
+ catch (const css::beans::PropertyVetoException&)
+ {
+ throw; /* allowed to leave */
+ }
+ catch (const css::lang::IllegalArgumentException&)
+ {
+ throw; /* allowed to leave */
+ }
+ catch (const css::lang::WrappedTargetException&)
+ {
+ throw; /* allowed to leave */
+ }
+ catch (const css::uno::RuntimeException&)
+ {
+ throw; /* allowed to leave */
+ }
+ catch (const css::uno::Exception& e)
+ {
+ // not allowed to leave this method
+ css::lang::WrappedTargetException aWrap;
+ aWrap.Context = static_cast<css::beans::XPropertySet*>(this);
+ aWrap.TargetException <<= e;
+
+ throw aWrap;
+ }
+
+ // file a change event, if the value changed
+ impl_fireAll(rGuard, &nHandle, &rValue, &aOldVal, 1);
+}
+
+// XFastPropertySet
+Any OPropertySetHelper::getFastPropertyValue(sal_Int32 nHandle)
+{
+ IPropertyArrayHelper& rInfo = getInfoHelper();
+ if (!rInfo.fillPropertyMembersByHandle(nullptr, nullptr, nHandle))
+ // unknown property
+ throw UnknownPropertyException(OUString::number(nHandle));
+
+ Any aRet;
+ std::unique_lock aGuard(m_aMutex);
+ getFastPropertyValue(aGuard, aRet, nHandle);
+ return aRet;
+}
+
+void OPropertySetHelper::impl_fireAll(std::unique_lock<std::mutex>& rGuard, sal_Int32* i_handles,
+ const Any* i_newValues, const Any* i_oldValues,
+ sal_Int32 i_count)
+{
+ if (m_handles.empty())
+ {
+ fire(rGuard, i_handles, i_newValues, i_oldValues, i_count, false);
+ return;
+ }
+
+ const size_t additionalEvents = m_handles.size();
+ OSL_ENSURE(additionalEvents == m_newValues.size() && additionalEvents == m_oldValues.size(),
+ "OPropertySetHelper::impl_fireAll: inconsistency!");
+
+ std::vector<sal_Int32> allHandles(additionalEvents + i_count);
+ std::copy(m_handles.begin(), m_handles.end(), allHandles.begin());
+ std::copy(i_handles, i_handles + i_count, allHandles.begin() + additionalEvents);
+
+ std::vector<Any> allNewValues(additionalEvents + i_count);
+ std::copy(m_newValues.begin(), m_newValues.end(), allNewValues.begin());
+ std::copy(i_newValues, i_newValues + i_count, allNewValues.begin() + additionalEvents);
+
+ std::vector<Any> allOldValues(additionalEvents + i_count);
+ std::copy(m_oldValues.begin(), m_oldValues.end(), allOldValues.begin());
+ std::copy(i_oldValues, i_oldValues + i_count, allOldValues.begin() + additionalEvents);
+
+ m_handles.clear();
+ m_newValues.clear();
+ m_oldValues.clear();
+
+ fire(rGuard, allHandles.data(), allNewValues.data(), allOldValues.data(),
+ additionalEvents + i_count, false);
+}
+
+void OPropertySetHelper::fire(std::unique_lock<std::mutex>& rGuard, sal_Int32* pnHandles,
+ const Any* pNewValues, const Any* pOldValues,
+ sal_Int32 nHandles, // This is the Count of the array
+ bool bVetoable)
+{
+ // Only fire, if one or more properties changed
+ if (!nHandles)
+ return;
+
+ // create the event sequence of all changed properties
+ Sequence<PropertyChangeEvent> aEvts(nHandles);
+ PropertyChangeEvent* pEvts = aEvts.getArray();
+ Reference<XInterface> xSource(static_cast<XPropertySet*>(this), UNO_QUERY);
+ sal_Int32 i;
+ sal_Int32 nChangesLen = 0;
+ // Loop over all changed properties to fill the event struct
+ for (i = 0; i < nHandles; i++)
+ {
+ // Vetoable fire and constrained attribute set or
+ // Change fire and Changed and bound attribute set
+ IPropertyArrayHelper& rInfo = getInfoHelper();
+ sal_Int16 nAttributes;
+ OUString aPropName;
+ rInfo.fillPropertyMembersByHandle(&aPropName, &nAttributes, pnHandles[i]);
+
+ if ((bVetoable && (nAttributes & PropertyAttribute::CONSTRAINED))
+ || (!bVetoable && (nAttributes & PropertyAttribute::BOUND)))
+ {
+ pEvts[nChangesLen].Source = xSource;
+ pEvts[nChangesLen].PropertyName = aPropName;
+ pEvts[nChangesLen].PropertyHandle = pnHandles[i];
+ pEvts[nChangesLen].OldValue = pOldValues[i];
+ pEvts[nChangesLen].NewValue = pNewValues[i];
+ nChangesLen++;
+ }
+ }
+
+ bool bIgnoreRuntimeExceptionsWhileFiring = m_bIgnoreRuntimeExceptionsWhileFiring;
+
+ // fire the events for all changed properties
+ for (i = 0; i < nChangesLen; i++)
+ {
+ if (bVetoable) // fire change Events?
+ fireVetoableChangeListeners(
+ rGuard, aVetoableLC.getContainer(rGuard, pEvts[i].PropertyHandle), pEvts[i]);
+ else
+ // get the listener container for the property name
+ firePropertyChangeListeners(
+ rGuard, aBoundLC.getContainer(rGuard, pEvts[i].PropertyHandle), pEvts[i]);
+
+ // broadcast to all listeners with "" property name
+ if (bVetoable)
+ // fire change Events?
+ fireVetoableChangeListeners(rGuard, &maVetoableChangeListeners, pEvts[i]);
+ else
+ firePropertyChangeListeners(rGuard, &maPropertyChangeListeners, pEvts[i]);
+ }
+
+ // reduce array to changed properties
+ aEvts.realloc(nChangesLen);
+
+ if (bVetoable)
+ return;
+
+ if (!maPropertiesChangeListeners.getLength(rGuard))
+ return;
+
+ // Here is a Bug, unbound properties are also fired
+ OInterfaceIteratorHelper4 aIt(rGuard, maPropertiesChangeListeners);
+ rGuard.unlock();
+ while (aIt.hasMoreElements())
+ {
+ XPropertiesChangeListener* pL = aIt.next().get();
+ try
+ {
+ try
+ {
+ // fire the whole event sequence to the
+ // XPropertiesChangeListener's
+ pL->propertiesChange(aEvts);
+ }
+ catch (DisposedException& exc)
+ {
+ OSL_ENSURE(exc.Context.is(), "DisposedException without Context!");
+ if (exc.Context == pL)
+ {
+ rGuard.lock();
+ aIt.remove(rGuard);
+ rGuard.unlock();
+ }
+ else
+ throw;
+ }
+ }
+ catch (RuntimeException& exc)
+ {
+ SAL_INFO("cppuhelper", "caught RuntimeException while firing listeners: " << exc);
+ if (!bIgnoreRuntimeExceptionsWhileFiring)
+ throw;
+ }
+ }
+ rGuard.lock();
+}
+
+void OPropertySetHelper::fireVetoableChangeListeners(
+ std::unique_lock<std::mutex>& rGuard,
+ comphelper::OInterfaceContainerHelper4<css::beans::XVetoableChangeListener>* pListeners,
+ const css::beans::PropertyChangeEvent& rChangeEvent)
+{
+ if (!pListeners || !pListeners->getLength(rGuard))
+ return;
+ // Iterate over all listeners and send events
+ OInterfaceIteratorHelper4 aIt(rGuard, *pListeners);
+ rGuard.unlock();
+ while (aIt.hasMoreElements())
+ {
+ XVetoableChangeListener* pL = aIt.next().get();
+ try
+ {
+ try
+ {
+ pL->vetoableChange(rChangeEvent);
+ }
+ catch (DisposedException& exc)
+ {
+ OSL_ENSURE(exc.Context.is(), "DisposedException without Context!");
+ if (exc.Context == pL)
+ {
+ rGuard.lock();
+ aIt.remove(rGuard);
+ rGuard.unlock();
+ }
+ else
+ throw;
+ }
+ }
+ catch (RuntimeException& exc)
+ {
+ SAL_INFO("cppuhelper", "caught RuntimeException while firing listeners: " << exc);
+ if (!m_bIgnoreRuntimeExceptionsWhileFiring)
+ throw;
+ }
+ }
+ rGuard.lock();
+}
+
+void OPropertySetHelper::firePropertyChangeListeners(
+ std::unique_lock<std::mutex>& rGuard,
+ comphelper::OInterfaceContainerHelper4<css::beans::XPropertyChangeListener>* pListeners,
+ const css::beans::PropertyChangeEvent& rChangeEvent)
+{
+ if (!pListeners || !pListeners->getLength(rGuard))
+ return;
+ // Iterate over all listeners and send events
+ OInterfaceIteratorHelper4 aIt(rGuard, *pListeners);
+ rGuard.unlock();
+ while (aIt.hasMoreElements())
+ {
+ XPropertyChangeListener* pL = aIt.next().get();
+ try
+ {
+ try
+ {
+ pL->propertyChange(rChangeEvent);
+ }
+ catch (DisposedException& exc)
+ {
+ OSL_ENSURE(exc.Context.is(), "DisposedException without Context!");
+ if (exc.Context == pL)
+ {
+ rGuard.lock();
+ aIt.remove(rGuard);
+ rGuard.unlock();
+ }
+ else
+ throw;
+ }
+ }
+ catch (RuntimeException& exc)
+ {
+ SAL_INFO("cppuhelper", "caught RuntimeException while firing listeners: " << exc);
+ if (!m_bIgnoreRuntimeExceptionsWhileFiring)
+ throw;
+ }
+ }
+ rGuard.lock();
+}
+
+// OPropertySetHelper
+void OPropertySetHelper::setFastPropertyValues(std::unique_lock<std::mutex>& rGuard,
+ sal_Int32 nSeqLen, sal_Int32* pHandles,
+ const Any* pValues, sal_Int32 nHitCount)
+{
+ OSL_ENSURE(!m_bDisposed, "object is disposed");
+
+ // get the map table
+ IPropertyArrayHelper& rPH = getInfoHelper();
+
+ std::unique_ptr<Any[]> pConvertedValues(new Any[nHitCount]);
+ std::unique_ptr<Any[]> pOldValues(new Any[nHitCount]);
+ sal_Int32 n = 0;
+ sal_Int32 i;
+
+ for (i = 0; i < nSeqLen; i++)
+ {
+ if (pHandles[i] != -1)
+ {
+ sal_Int16 nAttributes;
+ rPH.fillPropertyMembersByHandle(nullptr, &nAttributes, pHandles[i]);
+ if (nAttributes & PropertyAttribute::READONLY)
+ throw PropertyVetoException();
+ // Will the property change?
+ if (convertFastPropertyValue(rGuard, pConvertedValues[n], pOldValues[n], pHandles[i],
+ pValues[i]))
+ {
+ // only increment if the property really change
+ pHandles[n] = pHandles[i];
+ n++;
+ }
+ }
+ }
+
+ // fire vetoable events
+ fire(rGuard, pHandles, pConvertedValues.get(), pOldValues.get(), n, true);
+
+ // Loop over all changed properties
+ for (i = 0; i < n; i++)
+ {
+ // Will the property change?
+ setFastPropertyValue_NoBroadcast(rGuard, pHandles[i], pConvertedValues[i]);
+ }
+
+ // fire change events
+ impl_fireAll(rGuard, pHandles, pConvertedValues.get(), pOldValues.get(), n);
+}
+
+// XMultiPropertySet
+/**
+ * The sequence may be contain not known properties. The implementation
+ * must ignore these properties.
+ */
+void OPropertySetHelper::setPropertyValues(const Sequence<OUString>& rPropertyNames,
+ const Sequence<Any>& rValues)
+{
+ sal_Int32 nSeqLen = rPropertyNames.getLength();
+ if (nSeqLen != rValues.getLength())
+ throw IllegalArgumentException("lengths do not match", static_cast<XPropertySet*>(this),
+ -1);
+ std::unique_ptr<sal_Int32[]> pHandles(new sal_Int32[nSeqLen]);
+ // get the map table
+ IPropertyArrayHelper& rPH = getInfoHelper();
+ // fill the handle array
+ sal_Int32 nHitCount = rPH.fillHandles(pHandles.get(), rPropertyNames);
+ if (nHitCount == 0)
+ return;
+ std::unique_lock aGuard(m_aMutex);
+ setFastPropertyValues(aGuard, nSeqLen, pHandles.get(), rValues.getConstArray(), nHitCount);
+}
+
+// XMultiPropertySet
+Sequence<Any> OPropertySetHelper::getPropertyValues(const Sequence<OUString>& rPropertyNames)
+{
+ sal_Int32 nSeqLen = rPropertyNames.getLength();
+ std::unique_ptr<sal_Int32[]> pHandles(new sal_Int32[nSeqLen]);
+ Sequence<Any> aValues(nSeqLen);
+
+ // get the map table
+ IPropertyArrayHelper& rPH = getInfoHelper();
+ // fill the handle array
+ rPH.fillHandles(pHandles.get(), rPropertyNames);
+
+ Any* pValues = aValues.getArray();
+
+ std::unique_lock aGuard(m_aMutex);
+ // fill the sequence with the values
+ for (sal_Int32 i = 0; i < nSeqLen; i++)
+ getFastPropertyValue(aGuard, pValues[i], pHandles[i]);
+
+ return aValues;
+}
+
+// XMultiPropertySet
+void OPropertySetHelper::addPropertiesChangeListener(
+ const Sequence<OUString>&, const Reference<XPropertiesChangeListener>& rListener)
+{
+ std::unique_lock g(m_aMutex);
+ maPropertiesChangeListeners.addInterface(g, rListener);
+}
+
+// XMultiPropertySet
+void OPropertySetHelper::removePropertiesChangeListener(
+ const Reference<XPropertiesChangeListener>& rListener)
+{
+ std::unique_lock g(m_aMutex);
+ maPropertiesChangeListeners.removeInterface(g, rListener);
+}
+
+// XMultiPropertySet
+void OPropertySetHelper::firePropertiesChangeEvent(
+ const Sequence<OUString>& rPropertyNames, const Reference<XPropertiesChangeListener>& rListener)
+{
+ sal_Int32 nLen = rPropertyNames.getLength();
+ std::unique_ptr<sal_Int32[]> pHandles(new sal_Int32[nLen]);
+ IPropertyArrayHelper& rPH = getInfoHelper();
+ rPH.fillHandles(pHandles.get(), rPropertyNames);
+ const OUString* pNames = rPropertyNames.getConstArray();
+
+ // get the count of matching properties
+ sal_Int32 nFireLen = 0;
+ sal_Int32 i;
+ for (i = 0; i < nLen; i++)
+ if (pHandles[i] != -1)
+ nFireLen++;
+
+ Sequence<PropertyChangeEvent> aChanges(nFireLen);
+ PropertyChangeEvent* pChanges = aChanges.getArray();
+
+ {
+ // must lock the mutex outside the loop. So all values are consistent.
+ std::unique_lock aGuard(m_aMutex);
+ Reference<XInterface> xSource(static_cast<XPropertySet*>(this), UNO_QUERY);
+ sal_Int32 nFirePos = 0;
+ for (i = 0; i < nLen; i++)
+ {
+ if (pHandles[i] != -1)
+ {
+ pChanges[nFirePos].Source = xSource;
+ pChanges[nFirePos].PropertyName = pNames[i];
+ pChanges[nFirePos].PropertyHandle = pHandles[i];
+ getFastPropertyValue(aGuard, pChanges[nFirePos].OldValue, pHandles[i]);
+ pChanges[nFirePos].NewValue = pChanges[nFirePos].OldValue;
+ nFirePos++;
+ }
+ }
+ // release guard to fire events
+ }
+ if (nFireLen)
+ rListener->propertiesChange(aChanges);
+}
+
+UnoImplBase::~UnoImplBase() {}
+
+} // end namespace comphelper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/property/propstate.cxx b/comphelper/source/property/propstate.cxx
new file mode 100644
index 0000000000..183f51efce
--- /dev/null
+++ b/comphelper/source/property/propstate.cxx
@@ -0,0 +1,228 @@
+/* -*- 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 <comphelper/propstate.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <comphelper/sequence.hxx>
+
+namespace comphelper
+{
+
+
+ using ::com::sun::star::uno::Type;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::lang::XTypeProvider;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::uno::cpp_queryInterface;
+ using ::com::sun::star::uno::cpp_release;
+ using ::com::sun::star::beans::PropertyState_DEFAULT_VALUE;
+ using ::com::sun::star::beans::PropertyState_DIRECT_VALUE;
+
+
+ // OPropertyStateHelper
+
+
+ css::uno::Any SAL_CALL OPropertyStateHelper::queryInterface(const css::uno::Type& _rType)
+ {
+ css::uno::Any aReturn = OPropertySetHelper2::queryInterface(_rType);
+ // our own ifaces
+ if ( !aReturn.hasValue() )
+ aReturn = ::cppu::queryInterface(_rType, static_cast< css::beans::XPropertyState*>(this));
+
+ return aReturn;
+ }
+
+
+ css::uno::Sequence<css::uno::Type> OPropertyStateHelper::getTypes()
+ {
+ return {
+ cppu::UnoType<css::beans::XPropertySet>::get(),
+ cppu::UnoType<css::beans::XMultiPropertySet>::get(),
+ cppu::UnoType<css::beans::XFastPropertySet>::get(),
+ cppu::UnoType<css::beans::XPropertySetOption>::get(),
+ cppu::UnoType<css::beans::XPropertyState>::get()};
+ }
+
+ OPropertyStateHelper::OPropertyStateHelper(
+ ::cppu::OBroadcastHelper& rBHlp,
+ ::cppu::IEventNotificationHook *i_pFireEvents)
+ : ::cppu::OPropertySetHelper2(rBHlp, i_pFireEvents) { }
+
+ OPropertyStateHelper::~OPropertyStateHelper() {}
+
+
+ void OPropertyStateHelper::firePropertyChange(sal_Int32 nHandle, const css::uno::Any& aNewValue, const css::uno::Any& aOldValue)
+ {
+ fire(&nHandle, &aNewValue, &aOldValue, 1, false);
+ }
+
+ // XPropertyState
+
+ css::beans::PropertyState SAL_CALL OPropertyStateHelper::getPropertyState(const OUString& _rsName)
+ {
+ cppu::IPropertyArrayHelper& rPH = getInfoHelper();
+ sal_Int32 nHandle = rPH.getHandleByName(_rsName);
+
+ if (nHandle == -1)
+ throw css::beans::UnknownPropertyException(_rsName);
+
+ return getPropertyStateByHandle(nHandle);
+ }
+
+
+ void SAL_CALL OPropertyStateHelper::setPropertyToDefault(const OUString& _rsName)
+ {
+ cppu::IPropertyArrayHelper& rPH = getInfoHelper();
+ sal_Int32 nHandle = rPH.getHandleByName(_rsName);
+
+ if (nHandle == -1)
+ throw css::beans::UnknownPropertyException(_rsName);
+
+ setPropertyToDefaultByHandle(nHandle);
+ }
+
+
+ css::uno::Any SAL_CALL OPropertyStateHelper::getPropertyDefault(const OUString& _rsName)
+ {
+ cppu::IPropertyArrayHelper& rPH = getInfoHelper();
+ sal_Int32 nHandle = rPH.getHandleByName(_rsName);
+
+ if (nHandle == -1)
+ throw css::beans::UnknownPropertyException(_rsName);
+
+ return getPropertyDefaultByHandle(nHandle);
+ }
+
+
+ css::uno::Sequence< css::beans::PropertyState> SAL_CALL OPropertyStateHelper::getPropertyStates(const css::uno::Sequence< OUString >& _rPropertyNames)
+ {
+ sal_Int32 nLen = _rPropertyNames.getLength();
+ css::uno::Sequence< css::beans::PropertyState> aRet(nLen);
+ css::beans::PropertyState* pValues = aRet.getArray();
+ const OUString* pNames = _rPropertyNames.getConstArray();
+
+ cppu::IPropertyArrayHelper& rHelper = getInfoHelper();
+
+ css::uno::Sequence< css::beans::Property> aProps = rHelper.getProperties();
+ const css::beans::Property* pProps = aProps.getConstArray();
+ sal_Int32 nPropCount = aProps.getLength();
+
+ osl::MutexGuard aGuard(rBHelper.rMutex);
+ for (sal_Int32 i=0, j=0; i<nPropCount && j<nLen; ++i, ++pProps)
+ {
+ // get the values only for valid properties
+ if (pProps->Name == *pNames)
+ {
+ *pValues = getPropertyState(*pNames);
+ ++pValues;
+ ++pNames;
+ ++j;
+ }
+ }
+
+ return aRet;
+ }
+
+
+ css::beans::PropertyState OPropertyStateHelper::getPropertyStateByHandle( sal_Int32 _nHandle )
+ {
+ // simply compare the current and the default value
+ Any aCurrentValue = getPropertyDefaultByHandle( _nHandle );
+ Any aDefaultValue;
+ getFastPropertyValue( aDefaultValue, _nHandle );
+
+ bool bEqual = uno_type_equalData(
+ const_cast< void* >( aCurrentValue.getValue() ), aCurrentValue.getValueType().getTypeLibType(),
+ const_cast< void* >( aDefaultValue.getValue() ), aDefaultValue.getValueType().getTypeLibType(),
+ reinterpret_cast< uno_QueryInterfaceFunc >(cpp_queryInterface),
+ reinterpret_cast< uno_ReleaseFunc >(cpp_release)
+ );
+ return bEqual ? PropertyState_DEFAULT_VALUE : PropertyState_DIRECT_VALUE;
+ }
+
+
+ void OPropertyStateHelper::setPropertyToDefaultByHandle( sal_Int32 _nHandle )
+ {
+ setFastPropertyValue( _nHandle, getPropertyDefaultByHandle( _nHandle ) );
+ }
+
+
+ css::uno::Any OPropertyStateHelper::getPropertyDefaultByHandle( sal_Int32 ) const
+ {
+ return css::uno::Any();
+ }
+
+
+ // OStatefulPropertySet
+
+
+ OStatefulPropertySet::OStatefulPropertySet()
+ :OPropertyStateHelper( GetBroadcastHelper() )
+ {
+ }
+
+
+ OStatefulPropertySet::~OStatefulPropertySet()
+ {
+ }
+
+
+ Sequence< Type > SAL_CALL OStatefulPropertySet::getTypes()
+ {
+ return concatSequences(
+ Sequence {
+ cppu::UnoType<XWeak>::get(),
+ cppu::UnoType<XTypeProvider>::get() },
+ OPropertyStateHelper::getTypes()
+ );
+ }
+
+ Sequence< sal_Int8 > SAL_CALL OStatefulPropertySet::getImplementationId()
+ {
+ return css::uno::Sequence<sal_Int8>();
+ }
+
+
+ Any SAL_CALL OStatefulPropertySet::queryInterface( const Type& _rType )
+ {
+ Any aReturn = OWeakObject::queryInterface( _rType );
+ if ( !aReturn.hasValue() )
+ aReturn = ::cppu::queryInterface( _rType, static_cast< XTypeProvider* >( this ) );
+ if ( !aReturn.hasValue() )
+ aReturn = OPropertyStateHelper::queryInterface( _rType );
+ return aReturn;
+ }
+
+
+ void SAL_CALL OStatefulPropertySet::acquire() noexcept
+ {
+ ::cppu::OWeakObject::acquire();
+ }
+
+
+ void SAL_CALL OStatefulPropertySet::release() noexcept
+ {
+ ::cppu::OWeakObject::release();
+ }
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/streaming/basicio.cxx b/comphelper/source/streaming/basicio.cxx
new file mode 100644
index 0000000000..534d8b4cd0
--- /dev/null
+++ b/comphelper/source/streaming/basicio.cxx
@@ -0,0 +1,169 @@
+/* -*- 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 <comphelper/basicio.hxx>
+#include <comphelper/bytereader.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <com/sun/star/awt/FontDescriptor.hpp>
+
+namespace comphelper
+{
+
+
+const css::uno::Reference<css::io::XObjectOutputStream>& operator << (
+ const css::uno::Reference<css::io::XObjectOutputStream>& _rxOutStream,
+ const css::awt::FontDescriptor& _rFont)
+{
+ _rxOutStream->writeUTF( _rFont.Name );
+ _rxOutStream->writeShort( _rFont.Height );
+ _rxOutStream->writeShort( _rFont.Width );
+ _rxOutStream->writeUTF( _rFont.StyleName );
+ _rxOutStream->writeShort( _rFont.Family );
+ _rxOutStream->writeShort( _rFont.CharSet );
+ _rxOutStream->writeShort( _rFont.Pitch );
+ _rxOutStream->writeDouble( _rFont.CharacterWidth );
+ _rxOutStream->writeDouble( _rFont.Weight );
+ _rxOutStream->writeShort( static_cast< sal_Int16 >(_rFont.Slant) );
+ _rxOutStream->writeShort( _rFont.Underline );
+ _rxOutStream->writeShort( _rFont.Strikeout );
+ _rxOutStream->writeDouble( _rFont.Orientation );
+ _rxOutStream->writeBoolean( _rFont.Kerning );
+ _rxOutStream->writeBoolean( _rFont.WordLineMode );
+ _rxOutStream->writeShort( _rFont.Type );
+ return _rxOutStream;
+}
+
+// FontDescriptor
+
+const css::uno::Reference<css::io::XObjectInputStream>& operator >> (
+ const css::uno::Reference<css::io::XObjectInputStream>& _rxInStream,
+ css::awt::FontDescriptor& _rFont)
+{
+ // writing the FontDescriptor
+ _rFont.Name = _rxInStream->readUTF();
+ _rFont.Height = _rxInStream->readShort();
+ _rFont.Width = _rxInStream->readShort();
+ _rFont.StyleName = _rxInStream->readUTF();
+ _rFont.Family = _rxInStream->readShort();
+ _rFont.CharSet = _rxInStream->readShort();
+ _rFont.Pitch = _rxInStream->readShort();
+ _rFont.CharacterWidth = static_cast< float >(_rxInStream->readDouble());
+ _rFont.Weight = static_cast< float >(_rxInStream->readDouble());
+ _rFont.Slant = static_cast<css::awt::FontSlant>(_rxInStream->readShort());
+ _rFont.Underline = _rxInStream->readShort();
+ _rFont.Strikeout = _rxInStream->readShort();
+ _rFont.Orientation = static_cast< float >(_rxInStream->readDouble());
+ _rFont.Kerning = _rxInStream->readBoolean() != 0;
+ _rFont.WordLineMode = _rxInStream->readBoolean() != 0;
+ _rFont.Type = _rxInStream->readShort();
+ return _rxInStream;
+}
+
+
+const css::uno::Reference<css::io::XObjectInputStream>& operator >> (const css::uno::Reference<css::io::XObjectInputStream>& _rxInStream, bool& _rVal)
+{
+ _rVal = _rxInStream->readBoolean();
+ return _rxInStream;
+}
+
+
+const css::uno::Reference<css::io::XObjectOutputStream>& operator << (const css::uno::Reference<css::io::XObjectOutputStream>& _rxOutStream, bool _bVal)
+{
+ _rxOutStream->writeBoolean(_bVal);
+ return _rxOutStream;
+}
+
+
+const css::uno::Reference<css::io::XObjectInputStream>& operator >> (const css::uno::Reference<css::io::XObjectInputStream>& _rxInStream, OUString& rStr)
+{
+ rStr = _rxInStream->readUTF();
+ return _rxInStream;
+}
+
+
+const css::uno::Reference<css::io::XObjectOutputStream>& operator << (const css::uno::Reference<css::io::XObjectOutputStream>& _rxOutStream, const OUString& rStr)
+{
+ _rxOutStream->writeUTF(rStr);
+ return _rxOutStream;
+}
+
+
+const css::uno::Reference<css::io::XObjectInputStream>& operator >> (const css::uno::Reference<css::io::XObjectInputStream>& _rxInStream, sal_Int16& _rValue)
+{
+ _rValue = _rxInStream->readShort();
+ return _rxInStream;
+}
+
+
+const css::uno::Reference<css::io::XObjectOutputStream>& operator << (const css::uno::Reference<css::io::XObjectOutputStream>& _rxOutStream, sal_Int16 _nValue)
+{
+ _rxOutStream->writeShort(_nValue);
+ return _rxOutStream;
+}
+
+
+const css::uno::Reference<css::io::XObjectInputStream>& operator >> (const css::uno::Reference<css::io::XObjectInputStream>& _rxInStream, sal_uInt16& _rValue)
+{
+ _rValue = _rxInStream->readShort();
+ return _rxInStream;
+}
+
+
+const css::uno::Reference<css::io::XObjectOutputStream>& operator << (const css::uno::Reference<css::io::XObjectOutputStream>& _rxOutStream, sal_uInt16 _nValue)
+{
+ _rxOutStream->writeShort(_nValue);
+ return _rxOutStream;
+}
+
+
+const css::uno::Reference<css::io::XObjectInputStream>& operator >> (const css::uno::Reference<css::io::XObjectInputStream>& _rxInStream, sal_uInt32& _rValue)
+{
+ _rValue = _rxInStream->readLong();
+ return _rxInStream;
+}
+
+
+const css::uno::Reference<css::io::XObjectOutputStream>& operator << (const css::uno::Reference<css::io::XObjectOutputStream>& _rxOutStream, sal_uInt32 _nValue)
+{
+ _rxOutStream->writeLong(_nValue);
+ return _rxOutStream;
+}
+
+
+const css::uno::Reference<css::io::XObjectInputStream>& operator >> (const css::uno::Reference<css::io::XObjectInputStream>& _rxInStream, sal_Int32& _rValue)
+{
+ _rValue = _rxInStream->readLong();
+ return _rxInStream;
+}
+
+
+const css::uno::Reference<css::io::XObjectOutputStream>& operator << (const css::uno::Reference<css::io::XObjectOutputStream>& _rxOutStream, sal_Int32 _nValue)
+{
+ _rxOutStream->writeLong(_nValue);
+ return _rxOutStream;
+}
+
+ByteReader::~ByteReader() {}
+
+ByteWriter::~ByteWriter() {}
+
+} // namespace comphelper
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/streaming/memorystream.cxx b/comphelper/source/streaming/memorystream.cxx
new file mode 100644
index 0000000000..dc2a39d9e5
--- /dev/null
+++ b/comphelper/source/streaming/memorystream.cxx
@@ -0,0 +1,258 @@
+/* -*- 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 <algorithm>
+#include <cassert>
+#include <memory>
+
+#include <boost/core/noinit_adaptor.hpp>
+
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/io/XStream.hpp>
+#include <com/sun/star/io/XSeekableInputStream.hpp>
+#include <com/sun/star/io/XTruncate.hpp>
+//#include <com/sun/star/uno/XComponentContext.hpp>
+#include <comphelper/bytereader.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+
+#include <string.h>
+#include <vector>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+using ::cppu::OWeakObject;
+using ::cppu::WeakImplHelper;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::osl;
+
+namespace comphelper
+{
+
+namespace {
+
+class UNOMemoryStream :
+ public WeakImplHelper<XServiceInfo, XStream, XSeekableInputStream, XOutputStream, XTruncate>,
+ public comphelper::ByteWriter
+{
+public:
+ UNOMemoryStream();
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override;
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // XStream
+ virtual Reference< XInputStream > SAL_CALL getInputStream( ) override;
+ virtual Reference< XOutputStream > SAL_CALL getOutputStream( ) override;
+
+ // XInputStream
+ virtual sal_Int32 SAL_CALL readBytes( Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead ) override;
+ virtual sal_Int32 SAL_CALL readSomeBytes( Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead ) override;
+ virtual void SAL_CALL skipBytes( sal_Int32 nBytesToSkip ) override;
+ virtual sal_Int32 SAL_CALL available() override;
+ virtual void SAL_CALL closeInput() override;
+
+ // XSeekable
+ virtual void SAL_CALL seek( sal_Int64 location ) override;
+ virtual sal_Int64 SAL_CALL getPosition() override;
+ virtual sal_Int64 SAL_CALL getLength() override;
+
+ // XOutputStream
+ virtual void SAL_CALL writeBytes( const Sequence< sal_Int8 >& aData ) override;
+ virtual void SAL_CALL flush() override;
+ virtual void SAL_CALL closeOutput() override;
+
+ // XTruncate
+ virtual void SAL_CALL truncate() override;
+
+ // comphelper::ByteWriter
+ virtual void writeBytes(const sal_Int8* aData, sal_Int32 nBytesToWrite) override;
+
+private:
+ std::vector< sal_Int8, boost::noinit_adaptor<std::allocator<sal_Int8>> > maData;
+ sal_Int32 mnCursor;
+};
+
+}
+
+UNOMemoryStream::UNOMemoryStream()
+: mnCursor(0)
+{
+ maData.reserve(1 * 1024 * 1024);
+}
+
+// XServiceInfo
+OUString SAL_CALL UNOMemoryStream::getImplementationName()
+{
+ return "com.sun.star.comp.MemoryStream";
+}
+
+sal_Bool SAL_CALL UNOMemoryStream::supportsService(const OUString& ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence<OUString> SAL_CALL UNOMemoryStream::getSupportedServiceNames()
+{
+ return { "com.sun.star.comp.MemoryStream" };
+}
+
+// XStream
+Reference< XInputStream > SAL_CALL UNOMemoryStream::getInputStream( )
+{
+ return this;
+}
+
+Reference< XOutputStream > SAL_CALL UNOMemoryStream::getOutputStream( )
+{
+ return this;
+}
+
+// XInputStream
+sal_Int32 SAL_CALL UNOMemoryStream::readBytes( Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead )
+{
+ if( nBytesToRead < 0 )
+ throw IOException("nBytesToRead < 0");
+
+ nBytesToRead = std::min( nBytesToRead, available() );
+ aData.realloc( nBytesToRead );
+
+ if( nBytesToRead )
+ {
+ sal_Int8* pData = &(*maData.begin());
+ sal_Int8* pCursor = &(pData[mnCursor]);
+ memcpy( aData.getArray(), pCursor, nBytesToRead );
+
+ mnCursor += nBytesToRead;
+ }
+
+ return nBytesToRead;
+}
+
+sal_Int32 SAL_CALL UNOMemoryStream::readSomeBytes( Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead )
+{
+ return readBytes( aData, nMaxBytesToRead );
+}
+
+void SAL_CALL UNOMemoryStream::skipBytes( sal_Int32 nBytesToSkip )
+{
+ if( nBytesToSkip < 0 )
+ throw IOException("nBytesToSkip < 0");
+
+ mnCursor += std::min( nBytesToSkip, available() );
+}
+
+sal_Int32 SAL_CALL UNOMemoryStream::available()
+{
+ return std::min<sal_Int64>( SAL_MAX_INT32, maData.size() - mnCursor);
+}
+
+void SAL_CALL UNOMemoryStream::closeInput()
+{
+ mnCursor = 0;
+}
+
+// XSeekable
+void SAL_CALL UNOMemoryStream::seek( sal_Int64 location )
+{
+ if( (location < 0) || (location > SAL_MAX_INT32) )
+ throw IllegalArgumentException("this implementation does not support more than 2GB!", static_cast<OWeakObject*>(this), 0 );
+
+ // seek operation should be able to resize the stream
+ if ( o3tl::make_unsigned(location) > maData.size() )
+ maData.resize( static_cast< sal_Int32 >( location ) );
+
+ mnCursor = static_cast< sal_Int32 >( location );
+}
+
+sal_Int64 SAL_CALL UNOMemoryStream::getPosition()
+{
+ return static_cast< sal_Int64 >( mnCursor );
+}
+
+sal_Int64 SAL_CALL UNOMemoryStream::getLength()
+{
+ return static_cast< sal_Int64 >( maData.size() );
+}
+
+// XOutputStream
+void SAL_CALL UNOMemoryStream::writeBytes( const Sequence< sal_Int8 >& aData )
+{
+ writeBytes(aData.getConstArray(), aData.getLength());
+}
+
+void UNOMemoryStream::writeBytes( const sal_Int8* pInData, sal_Int32 nBytesToWrite )
+{
+ assert(nBytesToWrite >= 0);
+ if( !nBytesToWrite )
+ return;
+
+ sal_Int64 nNewSize = static_cast<sal_Int64>(mnCursor) + nBytesToWrite;
+ if( nNewSize > SAL_MAX_INT32 )
+ {
+ OSL_ASSERT(false);
+ throw IOException("this implementation does not support more than 2GB!", static_cast<OWeakObject*>(this) );
+ }
+
+ if( o3tl::make_unsigned( nNewSize ) > maData.size() )
+ maData.resize( nNewSize );
+
+ sal_Int8* pData = &(*maData.begin());
+ sal_Int8* pCursor = &(pData[mnCursor]);
+ memcpy(pCursor, pInData, nBytesToWrite);
+
+ mnCursor += nBytesToWrite;
+}
+
+void SAL_CALL UNOMemoryStream::flush()
+{
+}
+
+void SAL_CALL UNOMemoryStream::closeOutput()
+{
+ mnCursor = 0;
+}
+
+//XTruncate
+void SAL_CALL UNOMemoryStream::truncate()
+{
+ maData.clear();
+ mnCursor = 0;
+}
+
+} // namespace comphelper
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_MemoryStream(
+ css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ::comphelper::UNOMemoryStream());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/streaming/oslfile2streamwrap.cxx b/comphelper/source/streaming/oslfile2streamwrap.cxx
new file mode 100644
index 0000000000..243634610c
--- /dev/null
+++ b/comphelper/source/streaming/oslfile2streamwrap.cxx
@@ -0,0 +1,171 @@
+/* -*- 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/io/BufferSizeExceededException.hpp>
+#include <com/sun/star/io/NotConnectedException.hpp>
+#include <comphelper/oslfile2streamwrap.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/file.hxx>
+
+#include <algorithm>
+
+namespace comphelper
+{
+ using namespace osl;
+
+
+OSLInputStreamWrapper::OSLInputStreamWrapper( File& _rFile )
+ : m_pFile(&_rFile)
+{
+}
+
+
+OSLInputStreamWrapper::~OSLInputStreamWrapper()
+{
+}
+
+
+sal_Int32 SAL_CALL OSLInputStreamWrapper::readBytes(css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead)
+{
+ if (!m_pFile)
+ throw css::io::NotConnectedException(OUString(), static_cast<css::uno::XWeak*>(this));
+
+ if (nBytesToRead < 0)
+ throw css::io::BufferSizeExceededException(OUString(),static_cast<css::uno::XWeak*>(this));
+
+ aData.realloc(nBytesToRead);
+
+ std::scoped_lock aGuard( m_aMutex );
+
+ sal_uInt64 nRead = 0;
+ FileBase::RC eError = m_pFile->read(static_cast<void*>(aData.getArray()), nBytesToRead, nRead);
+ if (eError != FileBase::E_None)
+ throw css::io::BufferSizeExceededException(OUString(),static_cast<css::uno::XWeak*>(this));
+
+ // If the read character < MaxLength, adjust css::uno::Sequence
+ if (nRead < o3tl::make_unsigned(nBytesToRead))
+ aData.realloc( sal::static_int_cast< sal_Int32 >(nRead) );
+
+ return sal::static_int_cast< sal_Int32 >(nRead);
+}
+
+sal_Int32 SAL_CALL OSLInputStreamWrapper::readSomeBytes(css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead)
+{
+ if (!m_pFile)
+ throw css::io::NotConnectedException(OUString(), static_cast<css::uno::XWeak*>(this));
+
+ if (nMaxBytesToRead < 0)
+ throw css::io::BufferSizeExceededException(OUString(),static_cast<css::uno::XWeak*>(this));
+
+ return readBytes(aData, nMaxBytesToRead);
+}
+
+void SAL_CALL OSLInputStreamWrapper::skipBytes(sal_Int32 nBytesToSkip)
+{
+ std::scoped_lock aGuard( m_aMutex );
+ if (!m_pFile)
+ throw css::io::NotConnectedException(OUString(), static_cast<css::uno::XWeak*>(this));
+
+ sal_uInt64 nCurrentPos;
+ FileBase::RC eError = m_pFile->getPos(nCurrentPos);
+ if (eError != FileBase::E_None)
+ throw css::io::NotConnectedException(OUString(), static_cast<css::uno::XWeak*>(this));
+
+ sal_uInt64 nNewPos = nCurrentPos + nBytesToSkip;
+ eError = m_pFile->setPos(osl_Pos_Absolut, nNewPos);
+ if (eError != FileBase::E_None)
+ throw css::io::NotConnectedException(OUString(), static_cast<css::uno::XWeak*>(this));
+}
+
+sal_Int32 SAL_CALL OSLInputStreamWrapper::available()
+{
+ std::scoped_lock aGuard( m_aMutex );
+ if (!m_pFile)
+ throw css::io::NotConnectedException(OUString(), static_cast<css::uno::XWeak*>(this));
+
+ sal_uInt64 nPos;
+ FileBase::RC eError = m_pFile->getPos(nPos);
+ if (eError != FileBase::E_None)
+ throw css::io::NotConnectedException(OUString(), static_cast<css::uno::XWeak*>(this));
+
+ eError = m_pFile->setPos(osl_Pos_End, 0);
+ if (eError != FileBase::E_None)
+ throw css::io::NotConnectedException(OUString(),static_cast<css::uno::XWeak*>(this));
+
+ sal_uInt64 nAvailable;
+ eError = m_pFile->getPos(nAvailable);
+ if (eError != FileBase::E_None)
+ throw css::io::NotConnectedException(OUString(),static_cast<css::uno::XWeak*>(this));
+
+ nAvailable = nAvailable - nPos;
+ eError = m_pFile->setPos(osl_Pos_Absolut, nPos);
+ if (eError != FileBase::E_None)
+ throw css::io::NotConnectedException(OUString(),static_cast<css::uno::XWeak*>(this));
+ return std::min<sal_Int64>(nAvailable, SAL_MAX_INT32);
+}
+
+
+void SAL_CALL OSLInputStreamWrapper::closeInput()
+{
+ if (!m_pFile)
+ throw css::io::NotConnectedException(OUString(), static_cast<css::uno::XWeak*>(this));
+
+ m_pFile->close();
+
+ m_pFile = nullptr;
+}
+
+/*************************************************************************/
+// css::io::XOutputStream
+
+
+OSLOutputStreamWrapper::OSLOutputStreamWrapper(osl::File & _rFile):
+ rFile(_rFile)
+{}
+
+OSLOutputStreamWrapper::~OSLOutputStreamWrapper() {}
+
+void SAL_CALL OSLOutputStreamWrapper::writeBytes(const css::uno::Sequence< sal_Int8 >& aData)
+{
+ sal_uInt64 nWritten;
+ FileBase::RC eError = rFile.write(aData.getConstArray(),aData.getLength(), nWritten);
+ if (eError != FileBase::E_None
+ || nWritten != sal::static_int_cast< sal_uInt32 >(aData.getLength()))
+ {
+ throw css::io::BufferSizeExceededException(OUString(),static_cast<css::uno::XWeak*>(this));
+ }
+}
+
+
+void SAL_CALL OSLOutputStreamWrapper::flush()
+{
+}
+
+
+void SAL_CALL OSLOutputStreamWrapper::closeOutput()
+{
+ rFile.close();
+}
+
+} // namespace comphelper
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/streaming/seekableinput.cxx b/comphelper/source/streaming/seekableinput.cxx
new file mode 100644
index 0000000000..3508f933ee
--- /dev/null
+++ b/comphelper/source/streaming/seekableinput.cxx
@@ -0,0 +1,233 @@
+/* -*- 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/io/IOException.hpp>
+#include <com/sun/star/io/NotConnectedException.hpp>
+#include <com/sun/star/io/TempFile.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+
+
+#include <comphelper/seekableinput.hxx>
+#include <utility>
+
+using namespace ::com::sun::star;
+
+namespace comphelper
+{
+
+const sal_Int32 nConstBufferSize = 32000;
+
+
+static void copyInputToOutput_Impl( const uno::Reference< io::XInputStream >& xIn,
+ const uno::Reference< io::XOutputStream >& xOut )
+{
+ sal_Int32 nRead;
+ uno::Sequence< sal_Int8 > aSequence( nConstBufferSize );
+
+ do
+ {
+ nRead = xIn->readBytes( aSequence, nConstBufferSize );
+ if ( nRead < nConstBufferSize )
+ {
+ uno::Sequence< sal_Int8 > aTempBuf( aSequence.getConstArray(), nRead );
+ xOut->writeBytes( aTempBuf );
+ }
+ else
+ xOut->writeBytes( aSequence );
+ }
+ while ( nRead == nConstBufferSize );
+}
+
+
+OSeekableInputWrapper::OSeekableInputWrapper(
+ uno::Reference< io::XInputStream > xInStream,
+ uno::Reference< uno::XComponentContext > xContext )
+: m_xContext(std::move( xContext ))
+, m_xOriginalStream(std::move( xInStream ))
+{
+ if ( !m_xContext.is() )
+ throw uno::RuntimeException();
+}
+
+
+OSeekableInputWrapper::~OSeekableInputWrapper()
+{
+}
+
+
+uno::Reference< io::XInputStream > OSeekableInputWrapper::CheckSeekableCanWrap(
+ const uno::Reference< io::XInputStream >& xInStream,
+ const uno::Reference< uno::XComponentContext >& rxContext )
+{
+ // check that the stream is seekable and just wrap it if it is not
+ uno::Reference< io::XSeekable > xSeek( xInStream, uno::UNO_QUERY );
+ if ( xSeek.is() )
+ return xInStream;
+
+ return new OSeekableInputWrapper(xInStream, rxContext);
+}
+
+
+void OSeekableInputWrapper::PrepareCopy_Impl()
+{
+ if ( !m_xCopyInput.is() )
+ {
+ if ( !m_xContext.is() )
+ throw uno::RuntimeException();
+
+ uno::Reference< io::XOutputStream > xTempOut(
+ io::TempFile::create(m_xContext),
+ uno::UNO_QUERY_THROW );
+
+ copyInputToOutput_Impl( m_xOriginalStream, xTempOut );
+ xTempOut->closeOutput();
+
+ uno::Reference< io::XSeekable > xTempSeek( xTempOut, uno::UNO_QUERY );
+ if ( xTempSeek.is() )
+ {
+ xTempSeek->seek( 0 );
+ m_xCopyInput.set( xTempOut, uno::UNO_QUERY );
+ if ( m_xCopyInput.is() )
+ m_xCopySeek = xTempSeek;
+ }
+ }
+
+ if ( !m_xCopyInput.is() )
+ throw io::IOException("no m_xCopyInput");
+}
+
+// XInputStream
+
+sal_Int32 SAL_CALL OSeekableInputWrapper::readBytes( uno::Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead )
+{
+ std::scoped_lock aGuard( m_aMutex );
+
+ if ( !m_xOriginalStream.is() )
+ throw io::NotConnectedException();
+
+ PrepareCopy_Impl();
+
+ return m_xCopyInput->readBytes( aData, nBytesToRead );
+}
+
+
+sal_Int32 SAL_CALL OSeekableInputWrapper::readSomeBytes( uno::Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead )
+{
+ std::scoped_lock aGuard( m_aMutex );
+
+ if ( !m_xOriginalStream.is() )
+ throw io::NotConnectedException();
+
+ PrepareCopy_Impl();
+
+ return m_xCopyInput->readSomeBytes( aData, nMaxBytesToRead );
+}
+
+
+void SAL_CALL OSeekableInputWrapper::skipBytes( sal_Int32 nBytesToSkip )
+{
+ std::scoped_lock aGuard( m_aMutex );
+
+ if ( !m_xOriginalStream.is() )
+ throw io::NotConnectedException();
+
+ PrepareCopy_Impl();
+
+ m_xCopyInput->skipBytes( nBytesToSkip );
+}
+
+
+sal_Int32 SAL_CALL OSeekableInputWrapper::available()
+{
+ std::scoped_lock aGuard( m_aMutex );
+
+ if ( !m_xOriginalStream.is() )
+ throw io::NotConnectedException();
+
+ PrepareCopy_Impl();
+
+ return m_xCopyInput->available();
+}
+
+
+void SAL_CALL OSeekableInputWrapper::closeInput()
+{
+ std::scoped_lock aGuard( m_aMutex );
+
+ if ( !m_xOriginalStream.is() )
+ throw io::NotConnectedException();
+
+ m_xOriginalStream->closeInput();
+ m_xOriginalStream.clear();
+
+ if ( m_xCopyInput.is() )
+ {
+ m_xCopyInput->closeInput();
+ m_xCopyInput.clear();
+ }
+
+ m_xCopySeek.clear();
+}
+
+
+// XSeekable
+
+void SAL_CALL OSeekableInputWrapper::seek( sal_Int64 location )
+{
+ std::scoped_lock aGuard( m_aMutex );
+
+ if ( !m_xOriginalStream.is() )
+ throw io::NotConnectedException();
+
+ PrepareCopy_Impl();
+
+ m_xCopySeek->seek( location );
+}
+
+
+sal_Int64 SAL_CALL OSeekableInputWrapper::getPosition()
+{
+ std::scoped_lock aGuard( m_aMutex );
+
+ if ( !m_xOriginalStream.is() )
+ throw io::NotConnectedException();
+
+ PrepareCopy_Impl();
+
+ return m_xCopySeek->getPosition();
+}
+
+
+sal_Int64 SAL_CALL OSeekableInputWrapper::getLength()
+{
+ std::scoped_lock aGuard( m_aMutex );
+
+ if ( !m_xOriginalStream.is() )
+ throw io::NotConnectedException();
+
+ PrepareCopy_Impl();
+
+ return m_xCopySeek->getLength();
+}
+
+} // namespace comphelper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/streaming/seqinputstreamserv.cxx b/comphelper/source/streaming/seqinputstreamserv.cxx
new file mode 100644
index 0000000000..5d10029a50
--- /dev/null
+++ b/comphelper/source/streaming/seqinputstreamserv.cxx
@@ -0,0 +1,215 @@
+/* -*- 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 <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/seqstream.hxx>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/io/NotConnectedException.hpp>
+#include <com/sun/star/io/XSeekableInputStream.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/frame/DoubleInitializationException.hpp>
+#include <mutex>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+using namespace ::com::sun::star;
+
+namespace {
+
+class SequenceInputStreamService:
+ public ::cppu::WeakImplHelper<
+ lang::XServiceInfo,
+ io::XSeekableInputStream,
+ lang::XInitialization>
+{
+public:
+ explicit SequenceInputStreamService();
+
+ // noncopyable
+ SequenceInputStreamService(const SequenceInputStreamService&) = delete;
+ const SequenceInputStreamService& operator=(const SequenceInputStreamService&) = delete;
+
+ // css::lang::XServiceInfo:
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString & ServiceName ) override;
+ virtual uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // css::io::XInputStream:
+ virtual ::sal_Int32 SAL_CALL readBytes( uno::Sequence< ::sal_Int8 > & aData, ::sal_Int32 nBytesToRead ) override;
+ virtual ::sal_Int32 SAL_CALL readSomeBytes( uno::Sequence< ::sal_Int8 > & aData, ::sal_Int32 nMaxBytesToRead ) override;
+ virtual void SAL_CALL skipBytes( ::sal_Int32 nBytesToSkip ) override;
+ virtual ::sal_Int32 SAL_CALL available() override;
+ virtual void SAL_CALL closeInput() override;
+
+ // css::io::XSeekable:
+ virtual void SAL_CALL seek( ::sal_Int64 location ) override;
+ virtual ::sal_Int64 SAL_CALL getPosition() override;
+ virtual ::sal_Int64 SAL_CALL getLength() override;
+
+ // css::lang::XInitialization:
+ virtual void SAL_CALL initialize( const uno::Sequence< css::uno::Any > & aArguments ) override;
+
+private:
+ virtual ~SequenceInputStreamService() override {}
+
+
+ std::mutex m_aMutex;
+ bool m_bInitialized;
+ uno::Reference< io::XInputStream > m_xInputStream;
+ uno::Reference< io::XSeekable > m_xSeekable;
+};
+
+SequenceInputStreamService::SequenceInputStreamService()
+: m_bInitialized( false )
+{}
+
+// com.sun.star.uno.XServiceInfo:
+OUString SAL_CALL SequenceInputStreamService::getImplementationName()
+{
+ return "com.sun.star.comp.SequenceInputStreamService";
+}
+
+sal_Bool SAL_CALL SequenceInputStreamService::supportsService( OUString const & serviceName )
+{
+ return cppu::supportsService(this, serviceName);
+}
+
+uno::Sequence< OUString > SAL_CALL SequenceInputStreamService::getSupportedServiceNames()
+{
+ return { "com.sun.star.io.SequenceInputStream" };
+}
+
+// css::io::XInputStream:
+::sal_Int32 SAL_CALL SequenceInputStreamService::readBytes( uno::Sequence< ::sal_Int8 > & aData, ::sal_Int32 nBytesToRead )
+{
+ std::scoped_lock aGuard( m_aMutex );
+ if ( !m_xInputStream.is() )
+ throw io::NotConnectedException();
+
+ return m_xInputStream->readBytes( aData, nBytesToRead );
+}
+
+::sal_Int32 SAL_CALL SequenceInputStreamService::readSomeBytes( uno::Sequence< ::sal_Int8 > & aData, ::sal_Int32 nMaxBytesToRead )
+{
+ std::scoped_lock aGuard( m_aMutex );
+ if ( !m_xInputStream.is() )
+ throw io::NotConnectedException();
+
+ return m_xInputStream->readSomeBytes( aData, nMaxBytesToRead );
+}
+
+void SAL_CALL SequenceInputStreamService::skipBytes( ::sal_Int32 nBytesToSkip )
+{
+ std::scoped_lock aGuard( m_aMutex );
+ if ( !m_xInputStream.is() )
+ throw io::NotConnectedException();
+
+ return m_xInputStream->skipBytes( nBytesToSkip );
+}
+
+::sal_Int32 SAL_CALL SequenceInputStreamService::available()
+{
+ std::scoped_lock aGuard( m_aMutex );
+ if ( !m_xInputStream.is() )
+ throw io::NotConnectedException();
+
+ return m_xInputStream->available();
+}
+
+void SAL_CALL SequenceInputStreamService::closeInput()
+{
+ std::scoped_lock aGuard( m_aMutex );
+ if ( !m_xInputStream.is() )
+ throw io::NotConnectedException();
+
+ m_xInputStream->closeInput();
+ m_xInputStream.clear();
+ m_xSeekable.clear();
+}
+
+// css::io::XSeekable:
+void SAL_CALL SequenceInputStreamService::seek( ::sal_Int64 location )
+{
+ std::scoped_lock aGuard( m_aMutex );
+ if ( !m_xSeekable.is() )
+ throw io::NotConnectedException();
+
+ m_xSeekable->seek( location );
+}
+
+::sal_Int64 SAL_CALL SequenceInputStreamService::getPosition()
+{
+ std::scoped_lock aGuard( m_aMutex );
+ if ( !m_xSeekable.is() )
+ throw io::NotConnectedException();
+
+ return m_xSeekable->getPosition();
+}
+
+::sal_Int64 SAL_CALL SequenceInputStreamService::getLength()
+{
+ std::scoped_lock aGuard( m_aMutex );
+ if ( !m_xSeekable.is() )
+ throw io::NotConnectedException();
+
+ return m_xSeekable->getLength();
+}
+
+// css::lang::XInitialization:
+void SAL_CALL SequenceInputStreamService::initialize( const uno::Sequence< css::uno::Any > & aArguments )
+{
+ std::scoped_lock aGuard( m_aMutex );
+ if ( m_bInitialized )
+ throw frame::DoubleInitializationException();
+
+ if ( aArguments.getLength() != 1 )
+ throw lang::IllegalArgumentException( "Wrong number of arguments!",
+ static_cast< ::cppu::OWeakObject* >(this),
+ 1 );
+
+ uno::Sequence< sal_Int8 > aSeq;
+ if ( !(aArguments[0] >>= aSeq) )
+ throw lang::IllegalArgumentException( "Unexpected type of argument!",
+ static_cast< ::cppu::OWeakObject* >(this),
+ 1 );
+
+ uno::Reference< io::XInputStream > xInputStream(
+ static_cast< ::cppu::OWeakObject* >( new ::comphelper::SequenceInputStream( aSeq ) ),
+ uno::UNO_QUERY_THROW );
+ uno::Reference< io::XSeekable > xSeekable( xInputStream, uno::UNO_QUERY_THROW );
+ m_xInputStream = xInputStream;
+ m_xSeekable = xSeekable;
+ m_bInitialized = true;
+}
+
+} // anonymous namespace
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_SequenceInputStreamService(
+ css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SequenceInputStreamService());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/streaming/seqoutputstreamserv.cxx b/comphelper/source/streaming/seqoutputstreamserv.cxx
new file mode 100644
index 0000000000..19ef790029
--- /dev/null
+++ b/comphelper/source/streaming/seqoutputstreamserv.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/.
+ *
+ * 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 <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/seqstream.hxx>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/io/NotConnectedException.hpp>
+#include <com/sun/star/io/XSequenceOutputStream.hpp>
+#include <mutex>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+using namespace ::com::sun::star;
+
+
+namespace {
+
+class SequenceOutputStreamService:
+ public cppu::WeakImplHelper<lang::XServiceInfo, io::XSequenceOutputStream>
+{
+public:
+ explicit SequenceOutputStreamService();
+
+ // noncopyable
+ SequenceOutputStreamService(const SequenceOutputStreamService&) = delete;
+ const SequenceOutputStreamService& operator=(const SequenceOutputStreamService&) = delete;
+
+ // css::lang::XServiceInfo:
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString & ServiceName ) override;
+ virtual uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // css::io::XOutputStream:
+ virtual void SAL_CALL writeBytes( const uno::Sequence< ::sal_Int8 > & aData ) override;
+ virtual void SAL_CALL flush() override;
+ virtual void SAL_CALL closeOutput() override;
+
+ // css::io::XSequenceOutputStream:
+ virtual uno::Sequence< ::sal_Int8 > SAL_CALL getWrittenBytes( ) override;
+
+private:
+ virtual ~SequenceOutputStreamService() override {};
+
+
+ std::mutex m_aMutex;
+ // WARNING: dtor of m_xOutputStream writes into m_aSequence so that must live longer!
+ uno::Sequence< ::sal_Int8 > m_aSequence;
+ uno::Reference< io::XOutputStream > m_xOutputStream;
+};
+SequenceOutputStreamService::SequenceOutputStreamService()
+{
+ m_xOutputStream.set( static_cast < ::cppu::OWeakObject* >( new ::comphelper::OSequenceOutputStream( m_aSequence ) ), uno::UNO_QUERY_THROW );
+}
+
+// com.sun.star.uno.XServiceInfo:
+OUString SAL_CALL SequenceOutputStreamService::getImplementationName()
+{
+ return "com.sun.star.comp.SequenceOutputStreamService";
+}
+
+sal_Bool SAL_CALL SequenceOutputStreamService::supportsService( OUString const & serviceName )
+{
+ return cppu::supportsService(this, serviceName);
+}
+
+uno::Sequence< OUString > SAL_CALL SequenceOutputStreamService::getSupportedServiceNames()
+{
+ return { "com.sun.star.io.SequenceOutputStream" };
+}
+
+// css::io::XOutputStream:
+void SAL_CALL SequenceOutputStreamService::writeBytes( const uno::Sequence< ::sal_Int8 > & aData )
+{
+ std::scoped_lock aGuard( m_aMutex );
+ if ( !m_xOutputStream.is() )
+ throw io::NotConnectedException();
+
+ m_xOutputStream->writeBytes( aData );
+}
+
+void SAL_CALL SequenceOutputStreamService::flush()
+{
+ std::scoped_lock aGuard( m_aMutex );
+ if ( !m_xOutputStream.is() )
+ throw io::NotConnectedException();
+
+ m_xOutputStream->flush();
+};
+
+void SAL_CALL SequenceOutputStreamService::closeOutput()
+{
+ std::scoped_lock aGuard( m_aMutex );
+ if ( !m_xOutputStream.is() )
+ throw io::NotConnectedException();
+
+ m_xOutputStream->flush();
+ m_xOutputStream->closeOutput();
+ m_xOutputStream.clear();
+}
+
+// css::io::XSequenceOutputStream:
+uno::Sequence< ::sal_Int8 > SAL_CALL SequenceOutputStreamService::getWrittenBytes()
+{
+ std::scoped_lock aGuard( m_aMutex );
+
+ if (m_xOutputStream.is())
+ {
+ m_xOutputStream->flush();
+ }
+ // else: no exception, just return the finished sequence
+
+ return m_aSequence;
+}
+
+} // anonymous namespace
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_SequenceOutputStreamService(
+ css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SequenceOutputStreamService());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/streaming/seqstream.cxx b/comphelper/source/streaming/seqstream.cxx
new file mode 100644
index 0000000000..8aca6a6ea6
--- /dev/null
+++ b/comphelper/source/streaming/seqstream.cxx
@@ -0,0 +1,248 @@
+/* -*- 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/io/BufferSizeExceededException.hpp>
+#include <com/sun/star/io/NotConnectedException.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <comphelper/seqstream.hxx>
+
+#include <osl/diagnose.h>
+
+namespace comphelper
+{
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::uno;
+using namespace ::osl;
+
+
+
+
+MemoryInputStream::MemoryInputStream(
+ const sal_Int8* pData, sal_Int32 nDataLength)
+: m_pMemoryData(pData)
+, m_nMemoryDataLength(nDataLength)
+, m_nPos(0)
+{
+}
+
+// checks if closed, returns available size, not mutex-protected
+
+inline sal_Int32 MemoryInputStream::avail()
+{
+ if (m_nPos == -1)
+ throw NotConnectedException(OUString(), *this);
+
+ return m_nMemoryDataLength - m_nPos;
+}
+
+// css::io::XInputStream
+
+sal_Int32 SAL_CALL MemoryInputStream::readBytes( Sequence<sal_Int8>& aData, sal_Int32 nBytesToRead )
+{
+ if (nBytesToRead < 0)
+ throw BufferSizeExceededException(OUString(),*this);
+
+ std::scoped_lock aGuard( m_aMutex );
+
+ sal_Int32 nAvail = avail();
+
+ if (nAvail < nBytesToRead)
+ nBytesToRead = nAvail;
+
+ aData.realloc(nBytesToRead);
+ memcpy(aData.getArray(), m_pMemoryData + m_nPos, nBytesToRead);
+ m_nPos += nBytesToRead;
+
+ return nBytesToRead;
+}
+
+sal_Int32 MemoryInputStream::readSomeBytes( sal_Int8* pData, sal_Int32 nBytesToRead )
+{
+ if (nBytesToRead < 0)
+ throw BufferSizeExceededException(OUString(),*this);
+
+ std::scoped_lock aGuard( m_aMutex );
+
+ sal_Int32 nAvail = avail();
+
+ if (nAvail < nBytesToRead)
+ nBytesToRead = nAvail;
+
+ memcpy(pData, m_pMemoryData + m_nPos, nBytesToRead);
+ m_nPos += nBytesToRead;
+
+ return nBytesToRead;
+}
+
+sal_Int32 SAL_CALL MemoryInputStream::readSomeBytes( Sequence<sal_Int8>& aData, sal_Int32 nMaxBytesToRead )
+{
+ // all data is available at once
+ return readBytes(aData, nMaxBytesToRead);
+}
+
+
+void SAL_CALL MemoryInputStream::skipBytes( sal_Int32 nBytesToSkip )
+{
+ if (nBytesToSkip < 0)
+ throw BufferSizeExceededException(OUString(),*this);
+
+ std::scoped_lock aGuard( m_aMutex );
+
+ sal_Int32 nAvail = avail();
+
+ if (nAvail < nBytesToSkip)
+ nBytesToSkip = nAvail;
+
+ m_nPos += nBytesToSkip;
+}
+
+
+sal_Int32 SAL_CALL MemoryInputStream::available( )
+{
+ std::scoped_lock aGuard( m_aMutex );
+
+ return avail();
+}
+
+
+void SAL_CALL MemoryInputStream::closeInput( )
+{
+ std::scoped_lock aGuard( m_aMutex );
+
+ if (m_nPos == -1)
+ throw NotConnectedException(OUString(), *this);
+
+ m_nPos = -1;
+}
+
+void SAL_CALL MemoryInputStream::seek( sal_Int64 location )
+{
+ if ( location > m_nMemoryDataLength || location < 0 || location > SAL_MAX_INT32 )
+ throw IllegalArgumentException("bad location", static_cast<cppu::OWeakObject*>(this), 1);
+ std::scoped_lock aGuard( m_aMutex );
+ m_nPos = static_cast<sal_Int32>(location);
+}
+
+sal_Int64 SAL_CALL MemoryInputStream::getPosition()
+{
+ std::scoped_lock aGuard( m_aMutex );
+ return m_nPos;
+}
+
+sal_Int64 SAL_CALL MemoryInputStream::getLength( )
+{
+ return m_nMemoryDataLength;
+}
+
+
+SequenceInputStream::SequenceInputStream(
+ css::uno::Sequence<sal_Int8> const & rData)
+: MemoryInputStream(rData.getConstArray(), rData.getLength())
+, m_aData(rData)
+{
+}
+
+
+OSequenceOutputStream::OSequenceOutputStream(Sequence< sal_Int8 >& _rSeq, double _nResizeFactor, sal_Int32 _nMinimumResize)
+ :m_rSequence(_rSeq)
+ ,m_nResizeFactor(_nResizeFactor)
+ ,m_nMinimumResize(_nMinimumResize)
+ ,m_nSize(0) // starting at position 0
+ ,m_bConnected(true)
+{
+ OSL_ENSURE(m_nResizeFactor > 1, "OSequenceOutputStream::OSequenceOutputStream : invalid resize factor !");
+
+ if (m_nResizeFactor <= 1)
+ m_nResizeFactor = 1.3;
+}
+
+
+void SAL_CALL OSequenceOutputStream::writeBytes( const Sequence< sal_Int8 >& _rData )
+{
+ std::scoped_lock aGuard(m_aMutex);
+ if (!m_bConnected)
+ throw NotConnectedException();
+
+ // ensure the sequence has enough space left
+ if (m_nSize + _rData.getLength() > m_rSequence.getLength())
+ {
+ sal_Int32 nCurrentLength = m_rSequence.getLength();
+ sal_Int32 nNewLength = static_cast< sal_Int32 >(
+ nCurrentLength * m_nResizeFactor);
+
+ if (m_nMinimumResize > nNewLength - nCurrentLength)
+ // we have a minimum so it's not too inefficient for small sequences and small write requests
+ nNewLength = nCurrentLength + m_nMinimumResize;
+
+ if (nNewLength < m_nSize + _rData.getLength())
+ { // it's not enough... the data would not fit
+
+ // let's take the double amount of the length of the data to be written, as the next write
+ // request could be as large as this one
+ sal_Int32 nNewGrowth = _rData.getLength() * 2;
+ nNewLength = nCurrentLength + nNewGrowth;
+ }
+
+ // round it off to the next multiple of 4...
+ nNewLength = (nNewLength + 3) / 4 * 4;
+
+ m_rSequence.realloc(nNewLength);
+ }
+
+ OSL_ENSURE(m_rSequence.getLength() >= m_nSize + _rData.getLength(),
+ "ooops ... the realloc algorithm seems to be wrong :( !");
+
+ memcpy(m_rSequence.getArray() + m_nSize, _rData.getConstArray(), _rData.getLength());
+ m_nSize += _rData.getLength();
+}
+
+
+void SAL_CALL OSequenceOutputStream::flush( )
+{
+ std::scoped_lock aGuard(m_aMutex);
+ if (!m_bConnected)
+ throw NotConnectedException();
+
+ // cut the sequence to the real size
+ m_rSequence.realloc(m_nSize);
+}
+
+void OSequenceOutputStream::finalizeOutput()
+{
+ // cut the sequence to the real size
+ m_rSequence.realloc(m_nSize);
+ // and don't allow any further accesses
+ m_bConnected = false;
+}
+
+void SAL_CALL OSequenceOutputStream::closeOutput()
+{
+ std::scoped_lock aGuard(m_aMutex);
+ if (!m_bConnected)
+ throw NotConnectedException();
+
+ finalizeOutput();
+}
+
+} // namespace comphelper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/streaming/streamsection.cxx b/comphelper/source/streaming/streamsection.cxx
new file mode 100644
index 0000000000..b03df8ab78
--- /dev/null
+++ b/comphelper/source/streaming/streamsection.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 <comphelper/streamsection.hxx>
+#include <com/sun/star/io/XMarkableStream.hpp>
+#include <com/sun/star/io/XDataInputStream.hpp>
+#include <com/sun/star/io/XDataOutputStream.hpp>
+#include <osl/diagnose.h>
+
+namespace comphelper
+{
+
+
+OStreamSection::OStreamSection(const css::uno::Reference< css::io::XDataInputStream >& _rxInput)
+ :m_xMarkStream(_rxInput, css::uno::UNO_QUERY)
+ ,m_xInStream(_rxInput)
+ ,m_nBlockStart(-1)
+ ,m_nBlockLen(-1)
+{
+ OSL_ENSURE(m_xInStream.is() && m_xMarkStream.is(), "OStreamSection::OStreamSection : invalid argument !");
+ if (m_xInStream.is() && m_xMarkStream.is())
+ {
+ m_nBlockLen = _rxInput->readLong();
+ m_nBlockStart = m_xMarkStream->createMark();
+ }
+}
+
+
+OStreamSection::OStreamSection(const css::uno::Reference< css::io::XDataOutputStream >& _rxOutput)
+ :m_xMarkStream(_rxOutput, css::uno::UNO_QUERY)
+ ,m_xOutStream(_rxOutput)
+ ,m_nBlockStart(-1)
+ ,m_nBlockLen(-1)
+{
+ OSL_ENSURE(m_xOutStream.is() && m_xMarkStream.is(), "OStreamSection::OStreamSection : invalid argument !");
+ if (m_xOutStream.is() && m_xMarkStream.is())
+ {
+ m_nBlockStart = m_xMarkStream->createMark();
+ m_nBlockLen = 0;
+ m_xOutStream->writeLong(m_nBlockLen);
+ }
+}
+
+
+OStreamSection::~OStreamSection()
+{
+ try
+ { // don't allow any exceptions to leave this block, this may be called during the stack unwinding of an exception
+ // handling routing
+ if (m_xInStream.is() && m_xMarkStream.is())
+ { // we're working on an input stream
+ m_xMarkStream->jumpToMark(m_nBlockStart);
+ m_xInStream->skipBytes(m_nBlockLen);
+ m_xMarkStream->deleteMark(m_nBlockStart);
+ }
+ else if (m_xOutStream.is() && m_xMarkStream.is())
+ {
+ sal_Int32 nRealBlockLength = m_xMarkStream->offsetToMark(m_nBlockStart) - sizeof(m_nBlockLen);
+ m_nBlockLen = nRealBlockLength;
+ m_xMarkStream->jumpToMark(m_nBlockStart);
+ m_xOutStream->writeLong(m_nBlockLen);
+ m_xMarkStream->jumpToFurthest();
+ m_xMarkStream->deleteMark(m_nBlockStart);
+ }
+ }
+ catch(const css::uno::Exception&)
+ {
+ }
+}
+
+
+} // namespace comphelper
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/windows/windows_process.cxx b/comphelper/source/windows/windows_process.cxx
new file mode 100644
index 0000000000..7588bae027
--- /dev/null
+++ b/comphelper/source/windows/windows_process.cxx
@@ -0,0 +1,262 @@
+/* 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/. */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include <cstring>
+#include <wchar.h>
+
+#include <comphelper/windowsStart.hxx>
+
+// Needed for CreateEnvironmentBlock
+#include <userenv.h>
+#pragma comment(lib, "userenv.lib")
+
+/**
+ * Get the length that the string will take and takes into account the
+ * additional length if the string needs to be quoted and if characters need to
+ * be escaped.
+ */
+static int ArgStrLen(const wchar_t *s)
+{
+ int i = wcslen(s);
+ bool hasDoubleQuote = wcschr(s, L'"') != nullptr;
+ // Only add doublequotes if the string contains a space or a tab
+ bool addDoubleQuotes = wcspbrk(s, L" \t") != nullptr;
+
+ if (addDoubleQuotes)
+ {
+ i += 2; // initial and final doublequote
+ }
+
+ if (hasDoubleQuote)
+ {
+ int backslashes = 0;
+ while (*s)
+ {
+ if (*s == '\\')
+ {
+ ++backslashes;
+ }
+ else
+ {
+ if (*s == '"')
+ {
+ // Escape the doublequote and all backslashes preceding the doublequote
+ i += backslashes + 1;
+ }
+
+ backslashes = 0;
+ }
+
+ ++s;
+ }
+ }
+
+ return i;
+}
+
+/**
+ * Copy string "s" to string "d", quoting the argument as appropriate and
+ * escaping doublequotes along with any backslashes that immediately precede
+ * doublequotes.
+ * The CRT parses this to retrieve the original argc/argv that we meant,
+ * see STDARGV.C in the MSVC CRT sources.
+ *
+ * @return the end of the string
+ */
+static wchar_t* ArgToString(wchar_t *d, const wchar_t *s)
+{
+ bool hasDoubleQuote = wcschr(s, L'"') != nullptr;
+ // Only add doublequotes if the string contains a space or a tab
+ bool addDoubleQuotes = wcspbrk(s, L" \t") != nullptr;
+
+ if (addDoubleQuotes)
+ {
+ *d = '"'; // initial doublequote
+ ++d;
+ }
+
+ if (hasDoubleQuote)
+ {
+ int backslashes = 0;
+ while (*s)
+ {
+ if (*s == '\\')
+ {
+ ++backslashes;
+ }
+ else
+ {
+ if (*s == '"')
+ {
+ // Escape the doublequote and all backslashes preceding the doublequote
+ for (int i = 0; i <= backslashes; ++i)
+ {
+ *d = '\\';
+ ++d;
+ }
+ }
+
+ backslashes = 0;
+ }
+
+ *d = *s;
+ ++d;
+ ++s;
+ }
+ }
+ else
+ {
+ wcscpy(d, s);
+ d += wcslen(s);
+ }
+
+ if (addDoubleQuotes)
+ {
+ *d = '"'; // final doublequote
+ ++d;
+ }
+
+ return d;
+}
+
+/**
+ * Creates a command line from a list of arguments. The returned
+ * string is allocated with "malloc" and should be "free"d.
+ *
+ * argv is UTF8
+ */
+wchar_t*
+MakeCommandLine(int argc, wchar_t **argv)
+{
+ int i;
+ int len = 0;
+
+ // The + 1 of the last argument handles the allocation for null termination
+ for (i = 0; i < argc && argv[i]; ++i)
+ len += ArgStrLen(argv[i]) + 1;
+
+ // Protect against callers that pass 0 arguments
+ if (len == 0)
+ len = 1;
+
+ wchar_t *s = static_cast<wchar_t*>(malloc(len * sizeof(wchar_t)));
+ if (!s)
+ return nullptr;
+
+ wchar_t *c = s;
+ for (i = 0; i < argc && argv[i]; ++i)
+ {
+ c = ArgToString(c, argv[i]);
+ if (i + 1 != argc)
+ {
+ *c = ' ';
+ ++c;
+ }
+ }
+
+ *c = '\0';
+
+ return s;
+}
+
+BOOL
+WinLaunchChild(const wchar_t *exePath,
+ int argc,
+ wchar_t **argv,
+ HANDLE userToken,
+ HANDLE *hProcess)
+{
+ wchar_t *cl;
+ bool ok;
+
+ cl = MakeCommandLine(argc, argv);
+ if (!cl)
+ {
+ return FALSE;
+ }
+
+ STARTUPINFOW si;
+ std::memset(&si, 0, sizeof si);
+ si.cb = sizeof(STARTUPINFOW);
+ si.lpDesktop = const_cast<LPWSTR>(L"winsta0\\Default");
+ PROCESS_INFORMATION pi;
+ std::memset(&pi, 0, sizeof pi);
+
+ if (userToken == nullptr)
+ {
+ ok = CreateProcessW(exePath,
+ cl,
+ nullptr, // no special security attributes
+ nullptr, // no special thread attributes
+ FALSE, // don't inherit filehandles
+ 0, // creation flags
+ nullptr, // inherit my environment
+ nullptr, // use my current directory
+ &si,
+ &pi);
+ }
+ else
+ {
+ // Create an environment block for the process we're about to start using
+ // the user's token.
+ LPVOID environmentBlock = nullptr;
+ if (!CreateEnvironmentBlock(&environmentBlock, userToken, TRUE))
+ {
+ environmentBlock = nullptr;
+ }
+
+ ok = CreateProcessAsUserW(userToken,
+ exePath,
+ cl,
+ nullptr, // no special security attributes
+ nullptr, // no special thread attributes
+ FALSE, // don't inherit filehandles
+ 0, // creation flags
+ environmentBlock,
+ nullptr, // use my current directory
+ &si,
+ &pi);
+
+ if (environmentBlock)
+ {
+ DestroyEnvironmentBlock(environmentBlock);
+ }
+ }
+
+ if (ok)
+ {
+ if (hProcess)
+ {
+ *hProcess = pi.hProcess; // the caller now owns the HANDLE
+ }
+ else
+ {
+ CloseHandle(pi.hProcess);
+ }
+ CloseHandle(pi.hThread);
+ }
+ else
+ {
+ LPVOID lpMsgBuf = nullptr;
+ FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ nullptr,
+ GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ reinterpret_cast<LPWSTR>(&lpMsgBuf),
+ 0,
+ nullptr);
+ wprintf(L"Error restarting: %s\n", lpMsgBuf ? lpMsgBuf : L"(null)");
+ if (lpMsgBuf)
+ HeapFree(GetProcessHeap(), 0, lpMsgBuf);
+ }
+
+ free(cl);
+
+ return ok;
+}
diff --git a/comphelper/source/xml/attributelist.cxx b/comphelper/source/xml/attributelist.cxx
new file mode 100644
index 0000000000..664dcf5690
--- /dev/null
+++ b/comphelper/source/xml/attributelist.cxx
@@ -0,0 +1,130 @@
+/* -*- 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 <comphelper/attributelist.hxx>
+
+#include <algorithm>
+#include <cassert>
+
+using namespace osl;
+using namespace com::sun::star;
+
+
+namespace comphelper {
+
+OUString SAL_CALL AttributeList::getValueByName(const OUString& sName)
+{
+ for (auto const& attribute : mAttributes)
+ {
+ if( attribute.sName == sName ) {
+ return attribute.sValue;
+ }
+ }
+ return OUString();
+}
+
+AttributeList::AttributeList()
+{
+ // performance improvement during adding
+ mAttributes.reserve(20);
+}
+
+AttributeList::AttributeList(const uno::Reference< xml::sax::XAttributeList>& rAttrList)
+{
+ if (AttributeList* pImpl = dynamic_cast<AttributeList*>(rAttrList.get()))
+ mAttributes = pImpl->mAttributes;
+ else
+ AppendAttributeList(rAttrList);
+}
+
+AttributeList::~AttributeList()
+{
+}
+
+css::uno::Reference< css::util::XCloneable > AttributeList::createClone()
+{
+ return new AttributeList( *this );
+}
+
+void AttributeList::AddAttribute(const OUString& sName, const OUString& sValue)
+{
+ assert(!sName.isEmpty() && "empty attribute name is invalid");
+ // Either it's 'namespace_prefix:attribute_name',
+ // or as in XMLNamespaces::applyNSToAttributeName, it's 'namespace:full:uri^attribute_name'.
+ assert((std::count(sName.getStr(), sName.getStr() + sName.getLength(), u':') <= 1
+ || std::count(sName.getStr(), sName.getStr() + sName.getLength(), u'^') == 1)
+ && "too many colons");
+ // TODO: this assertion fails in tests!
+// assert(std::none_of(mAttributes.begin(), mAttributes.end(),
+// [&sName](const TagAttribute& a) { return a.sName == sName; }));
+ mAttributes.push_back({ sName, sValue });
+}
+
+void AttributeList::RemoveAttribute(const OUString& sName)
+{
+ auto ii = std::find_if(mAttributes.begin(), mAttributes.end(),
+ [&sName](const TagAttribute& rAttr) { return rAttr.sName == sName; });
+
+ if (ii != mAttributes.end())
+ mAttributes.erase(ii);
+}
+
+void AttributeList::AppendAttributeList(const uno::Reference<css::xml::sax::XAttributeList>& r)
+{
+ assert(r.is());
+
+ sal_Int16 nMax = r->getLength();
+ sal_Int16 nTotalSize = mAttributes.size() + nMax;
+ mAttributes.reserve(nTotalSize);
+
+ for (sal_Int16 i = 0; i < nMax; ++i)
+ AddAttribute(r->getNameByIndex(i), r->getValueByIndex(i));
+
+ assert(nTotalSize == getLength());
+}
+
+void AttributeList::SetValueByIndex(sal_Int16 i, const OUString& rValue)
+{
+ mAttributes[i].sValue = rValue;
+}
+
+void AttributeList::RemoveAttributeByIndex(sal_Int16 i)
+{
+ mAttributes.erase(mAttributes.begin() + i);
+}
+
+void AttributeList::RenameAttributeByIndex(sal_Int16 i, const OUString& rNewName)
+{
+ mAttributes[i].sName = rNewName;
+}
+
+sal_Int16 AttributeList::GetIndexByName(const OUString& rName) const
+{
+ auto ii = std::find_if(mAttributes.begin(), mAttributes.end(),
+ [&rName](const TagAttribute& rAttr) { return rAttr.sName == rName; });
+
+ if (ii != mAttributes.end())
+ return static_cast<sal_Int16>(std::distance(mAttributes.begin(), ii));
+
+ return -1;
+}
+
+} // namespace comphelper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/xml/ofopxmlhelper.cxx b/comphelper/source/xml/ofopxmlhelper.cxx
new file mode 100644
index 0000000000..a672bf57a6
--- /dev/null
+++ b/comphelper/source/xml/ofopxmlhelper.cxx
@@ -0,0 +1,489 @@
+/* -*- 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 <comphelper/ofopxmlhelper.hxx>
+#include <comphelper/attributelist.hxx>
+
+#include <cppuhelper/implbase.hxx>
+#include <rtl/ref.hxx>
+
+#include <com/sun/star/beans/StringPair.hpp>
+#include <com/sun/star/xml/sax/Parser.hpp>
+#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
+#include <com/sun/star/xml/sax/SAXException.hpp>
+#include <com/sun/star/xml/sax/Writer.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <vector>
+
+#define RELATIONINFO_FORMAT 0
+#define CONTENTTYPE_FORMAT 1
+#define FORMAT_MAX_ID CONTENTTYPE_FORMAT
+
+using namespace ::com::sun::star;
+
+namespace comphelper {
+
+namespace {
+
+// this helper class is designed to allow to parse ContentType- and Relationship-related information from OfficeOpenXML format
+class OFOPXMLHelper_Impl
+ : public cppu::WeakImplHelper< css::xml::sax::XDocumentHandler >
+{
+ sal_uInt16 const m_nFormat; // which format to parse
+
+ css::uno::Sequence< css::uno::Sequence< css::beans::StringPair > > m_aResultSeq;
+ std::vector< OUString > m_aElementsSeq; // stack of elements being parsed
+
+
+public:
+ css::uno::Sequence< css::uno::Sequence< css::beans::StringPair > > const & GetParsingResult() const;
+
+ explicit OFOPXMLHelper_Impl( sal_uInt16 nFormat ); // must not be created directly
+
+ // XDocumentHandler
+ virtual void SAL_CALL startDocument() override;
+ virtual void SAL_CALL endDocument() override;
+ virtual void SAL_CALL startElement( const OUString& aName, const css::uno::Reference< css::xml::sax::XAttributeList >& xAttribs ) override;
+ virtual void SAL_CALL endElement( const OUString& aName ) override;
+ virtual void SAL_CALL characters( const OUString& aChars ) override;
+ virtual void SAL_CALL ignorableWhitespace( const OUString& aWhitespaces ) override;
+ virtual void SAL_CALL processingInstruction( const OUString& aTarget, const OUString& aData ) override;
+ virtual void SAL_CALL setDocumentLocator( const css::uno::Reference< css::xml::sax::XLocator >& xLocator ) override;
+};
+
+}
+
+namespace OFOPXMLHelper {
+
+/// @throws css::uno::Exception
+static uno::Sequence<uno::Sequence< beans::StringPair>> ReadSequence_Impl(
+ const uno::Reference<io::XInputStream>& xInStream,
+ const OUString& aStringID, sal_uInt16 nFormat,
+ const uno::Reference<uno::XComponentContext>& xContext);
+
+uno::Sequence< uno::Sequence< beans::StringPair > > ReadRelationsInfoSequence(
+ const uno::Reference< io::XInputStream >& xInStream,
+ std::u16string_view aStreamName,
+ const uno::Reference< uno::XComponentContext >& rContext )
+{
+ OUString aStringID = OUString::Concat("_rels/") + aStreamName;
+ return ReadSequence_Impl( xInStream, aStringID, RELATIONINFO_FORMAT, rContext );
+}
+
+
+uno::Sequence< uno::Sequence< beans::StringPair > > ReadContentTypeSequence(
+ const uno::Reference< io::XInputStream >& xInStream,
+ const uno::Reference< uno::XComponentContext >& rContext )
+{
+ return ReadSequence_Impl( xInStream, "[Content_Types].xml", CONTENTTYPE_FORMAT, rContext );
+}
+
+OUString GetContentTypeByName(
+ const css::uno::Sequence<css::uno::Sequence<css::beans::StringPair>>& rContentTypes,
+ const OUString& rFilename)
+{
+ if (rContentTypes.getLength() < 2)
+ {
+ return OUString();
+ }
+
+ const uno::Sequence<beans::StringPair>& rDefaults = rContentTypes[0];
+ const uno::Sequence<beans::StringPair>& rOverrides = rContentTypes[1];
+
+ // Find the extension and use it to get the type.
+ const sal_Int32 nDotOffset = rFilename.lastIndexOf('.');
+ const OUString aExt = (nDotOffset >= 0 ? rFilename.copy(nDotOffset + 1) : rFilename); // Skip the dot.
+
+ const std::vector<OUString> aNames = { aExt, "/" + rFilename };
+ for (const OUString& aName : aNames)
+ {
+ const auto it1 = std::find_if(rOverrides.begin(), rOverrides.end(), [&aName](const beans::StringPair& rPair)
+ { return rPair.First == aName; });
+ if (it1 != rOverrides.end())
+ return it1->Second;
+
+ const auto it2 = std::find_if(rDefaults.begin(), rDefaults.end(), [&aName](const beans::StringPair& rPair)
+ { return rPair.First == aName; });
+ if (it2 != rDefaults.end())
+ return it2->Second;
+ }
+
+ return OUString();
+}
+
+void WriteRelationsInfoSequence(
+ const uno::Reference< io::XOutputStream >& xOutStream,
+ const uno::Sequence< uno::Sequence< beans::StringPair > >& aSequence,
+ const uno::Reference< uno::XComponentContext >& rContext )
+{
+ if ( !xOutStream.is() )
+ throw uno::RuntimeException();
+
+ uno::Reference< css::xml::sax::XWriter > xWriter = css::xml::sax::Writer::create(rContext);
+
+ xWriter->setOutputStream( xOutStream );
+
+ OUString aRelListElement( "Relationships" );
+ OUString aRelElement( "Relationship" );
+ OUString aWhiteSpace( " " );
+
+ // write the namespace
+ rtl::Reference<AttributeList> pRootAttrList = new AttributeList;
+ pRootAttrList->AddAttribute(
+ "xmlns",
+ "http://schemas.openxmlformats.org/package/2006/relationships" );
+
+ xWriter->startDocument();
+ xWriter->startElement( aRelListElement, pRootAttrList );
+
+ for ( const auto & i : aSequence )
+ {
+ rtl::Reference<AttributeList> pAttrList = new AttributeList;
+ for( const beans::StringPair & pair : i )
+ {
+ if ( !(pair.First == "Id"
+ || pair.First == "Type"
+ || pair.First == "TargetMode"
+ || pair.First == "Target") )
+ {
+ // TODO/LATER: should the extensions be allowed?
+ throw lang::IllegalArgumentException();
+ }
+ pAttrList->AddAttribute( pair.First, pair.Second );
+ }
+
+ xWriter->startElement( aRelElement, pAttrList );
+ xWriter->ignorableWhitespace( aWhiteSpace );
+ xWriter->endElement( aRelElement );
+ }
+
+ xWriter->ignorableWhitespace( aWhiteSpace );
+ xWriter->endElement( aRelListElement );
+ xWriter->endDocument();
+}
+
+
+void WriteContentSequence(
+ const uno::Reference< io::XOutputStream >& xOutStream,
+ const uno::Sequence< beans::StringPair >& aDefaultsSequence,
+ const uno::Sequence< beans::StringPair >& aOverridesSequence,
+ const uno::Reference< uno::XComponentContext >& rContext )
+{
+ if ( !xOutStream.is() )
+ throw uno::RuntimeException();
+
+ uno::Reference< css::xml::sax::XWriter > xWriter = css::xml::sax::Writer::create(rContext);
+
+ xWriter->setOutputStream( xOutStream );
+
+ static constexpr OUString aTypesElement(u"Types"_ustr);
+ static constexpr OUString aDefaultElement(u"Default"_ustr);
+ static constexpr OUString aOverrideElement(u"Override"_ustr);
+ static constexpr OUString aContentTypeAttr(u"ContentType"_ustr);
+ static constexpr OUString aWhiteSpace(u" "_ustr);
+
+ // write the namespace
+ rtl::Reference<AttributeList> pRootAttrList = new AttributeList;
+ pRootAttrList->AddAttribute(
+ "xmlns",
+ "http://schemas.openxmlformats.org/package/2006/content-types" );
+
+ xWriter->startDocument();
+ xWriter->startElement( aTypesElement, pRootAttrList );
+
+ for ( const beans::StringPair & pair : aDefaultsSequence )
+ {
+ rtl::Reference<AttributeList> pAttrList = new AttributeList;
+ pAttrList->AddAttribute( "Extension", pair.First );
+ pAttrList->AddAttribute( aContentTypeAttr, pair.Second );
+
+ xWriter->startElement( aDefaultElement, pAttrList );
+ xWriter->ignorableWhitespace( aWhiteSpace );
+ xWriter->endElement( aDefaultElement );
+ }
+
+ for ( const beans::StringPair & pair : aOverridesSequence )
+ {
+ rtl::Reference<AttributeList> pAttrList = new AttributeList;
+ pAttrList->AddAttribute( "PartName", pair.First );
+ pAttrList->AddAttribute( aContentTypeAttr, pair.Second );
+
+ xWriter->startElement( aOverrideElement, pAttrList );
+ xWriter->ignorableWhitespace( aWhiteSpace );
+ xWriter->endElement( aOverrideElement );
+ }
+
+ xWriter->ignorableWhitespace( aWhiteSpace );
+ xWriter->endElement( aTypesElement );
+ xWriter->endDocument();
+
+}
+
+uno::Sequence< uno::Sequence< beans::StringPair > > ReadSequence_Impl(
+ const uno::Reference< io::XInputStream >& xInStream,
+ const OUString& aStringID, sal_uInt16 nFormat,
+ const uno::Reference< uno::XComponentContext >& rContext )
+{
+ if ( !rContext.is() || !xInStream.is() || nFormat > FORMAT_MAX_ID )
+ throw uno::RuntimeException();
+
+ uno::Reference< css::xml::sax::XParser > xParser = css::xml::sax::Parser::create( rContext );
+
+ rtl::Reference<OFOPXMLHelper_Impl> pHelper = new OFOPXMLHelper_Impl( nFormat );
+ css::xml::sax::InputSource aParserInput;
+ aParserInput.aInputStream = xInStream;
+ aParserInput.sSystemId = aStringID;
+ xParser->setDocumentHandler( pHelper );
+ xParser->parseStream( aParserInput );
+ xParser->setDocumentHandler( uno::Reference < css::xml::sax::XDocumentHandler > () );
+
+ return pHelper->GetParsingResult();
+}
+
+} // namespace OFOPXMLHelper
+
+// Relations info related strings
+constexpr OUStringLiteral g_aRelListElement(u"Relationships");
+constexpr OUStringLiteral g_aRelElement( u"Relationship" );
+constexpr OUString g_aIDAttr( u"Id"_ustr );
+constexpr OUString g_aTypeAttr( u"Type"_ustr );
+constexpr OUString g_aTargetModeAttr( u"TargetMode"_ustr );
+constexpr OUString g_aTargetAttr( u"Target"_ustr );
+
+// ContentType related strings
+constexpr OUStringLiteral g_aTypesElement( u"Types" );
+constexpr OUStringLiteral g_aDefaultElement( u"Default" );
+constexpr OUStringLiteral g_aOverrideElement( u"Override" );
+constexpr OUStringLiteral g_aExtensionAttr( u"Extension" );
+constexpr OUStringLiteral g_aPartNameAttr( u"PartName" );
+constexpr OUString g_aContentTypeAttr( u"ContentType"_ustr );
+
+OFOPXMLHelper_Impl::OFOPXMLHelper_Impl( sal_uInt16 nFormat )
+: m_nFormat( nFormat )
+{
+}
+
+uno::Sequence< uno::Sequence< beans::StringPair > > const & OFOPXMLHelper_Impl::GetParsingResult() const
+{
+ if ( !m_aElementsSeq.empty() )
+ throw uno::RuntimeException(); // the parsing has still not finished!
+
+ return m_aResultSeq;
+}
+
+
+void SAL_CALL OFOPXMLHelper_Impl::startDocument()
+{
+}
+
+
+void SAL_CALL OFOPXMLHelper_Impl::endDocument()
+{
+}
+
+
+void SAL_CALL OFOPXMLHelper_Impl::startElement( const OUString& aName, const uno::Reference< css::xml::sax::XAttributeList >& xAttribs )
+{
+ if ( m_nFormat == RELATIONINFO_FORMAT )
+ {
+ if ( aName == g_aRelListElement )
+ {
+ sal_Int32 nNewLength = m_aElementsSeq.size() + 1;
+
+ if ( nNewLength != 1 )
+ throw css::xml::sax::SAXException(); // TODO: this element must be the first level element
+
+ m_aElementsSeq.push_back( aName );
+
+ return; // nothing to do
+ }
+ else if ( aName == g_aRelElement )
+ {
+ sal_Int32 nNewLength = m_aElementsSeq.size() + 1;
+ if ( nNewLength != 2 )
+ throw css::xml::sax::SAXException(); // TODO: this element must be the second level element
+
+ m_aElementsSeq.push_back( aName );
+
+ sal_Int32 nNewEntryNum = m_aResultSeq.getLength() + 1;
+ m_aResultSeq.realloc( nNewEntryNum );
+ auto pResultSeq = m_aResultSeq.getArray();
+ sal_Int32 nAttrNum = 0;
+ pResultSeq[nNewEntryNum-1].realloc( 4 ); // the maximal expected number of arguments is 4
+ auto pAttrs = pResultSeq[nNewEntryNum-1].getArray();
+
+ OUString aIDValue = xAttribs->getValueByName( g_aIDAttr );
+ if ( aIDValue.isEmpty() )
+ throw css::xml::sax::SAXException(); // TODO: the ID value must present
+
+ OUString aTypeValue = xAttribs->getValueByName( g_aTypeAttr );
+ OUString aTargetValue = xAttribs->getValueByName( g_aTargetAttr );
+ OUString aTargetModeValue = xAttribs->getValueByName( g_aTargetModeAttr );
+
+ pAttrs[++nAttrNum - 1].First = g_aIDAttr;
+ pAttrs[nAttrNum - 1].Second = aIDValue;
+
+ if ( !aTypeValue.isEmpty() )
+ {
+ pAttrs[++nAttrNum - 1].First = g_aTypeAttr;
+ pAttrs[nAttrNum - 1].Second = aTypeValue;
+ }
+
+ if ( !aTargetValue.isEmpty() )
+ {
+ pAttrs[++nAttrNum - 1].First = g_aTargetAttr;
+ pAttrs[nAttrNum - 1].Second = aTargetValue;
+ }
+
+ if ( !aTargetModeValue.isEmpty() )
+ {
+ pAttrs[++nAttrNum - 1].First = g_aTargetModeAttr;
+ pAttrs[nAttrNum - 1].Second = aTargetModeValue;
+ }
+
+ pResultSeq[nNewEntryNum-1].realloc( nAttrNum );
+ }
+ else
+ throw css::xml::sax::SAXException(); // TODO: no other elements expected!
+ }
+ else if ( m_nFormat == CONTENTTYPE_FORMAT )
+ {
+ if ( aName == g_aTypesElement )
+ {
+ sal_Int32 nNewLength = m_aElementsSeq.size() + 1;
+
+ if ( nNewLength != 1 )
+ throw css::xml::sax::SAXException(); // TODO: this element must be the first level element
+
+ m_aElementsSeq.push_back( aName );
+
+ if ( !m_aResultSeq.hasElements() )
+ m_aResultSeq.realloc( 2 );
+
+ return; // nothing to do
+ }
+ else if ( aName == g_aDefaultElement )
+ {
+ sal_Int32 nNewLength = m_aElementsSeq.size() + 1;
+ if ( nNewLength != 2 )
+ throw css::xml::sax::SAXException(); // TODO: this element must be the second level element
+
+ m_aElementsSeq.push_back( aName );
+
+ if ( !m_aResultSeq.hasElements() )
+ m_aResultSeq.realloc( 2 );
+
+ if ( m_aResultSeq.getLength() != 2 )
+ throw uno::RuntimeException();
+
+ auto pResultSeq = m_aResultSeq.getArray();
+
+ const OUString aExtensionValue = xAttribs->getValueByName( g_aExtensionAttr );
+ if ( aExtensionValue.isEmpty() )
+ throw css::xml::sax::SAXException(); // TODO: the Extension value must present
+
+ const OUString aContentTypeValue = xAttribs->getValueByName( g_aContentTypeAttr );
+ if ( aContentTypeValue.isEmpty() )
+ throw css::xml::sax::SAXException(); // TODO: the ContentType value must present
+
+ const sal_Int32 nNewResultLen = m_aResultSeq[0].getLength() + 1;
+ pResultSeq[0].realloc( nNewResultLen );
+ auto pSeq = pResultSeq[0].getArray();
+
+ pSeq[nNewResultLen-1].First = aExtensionValue;
+ pSeq[nNewResultLen-1].Second = aContentTypeValue;
+ }
+ else if ( aName == g_aOverrideElement )
+ {
+ sal_Int32 nNewLength = m_aElementsSeq.size() + 1;
+ if ( nNewLength != 2 )
+ throw css::xml::sax::SAXException(); // TODO: this element must be the second level element
+
+ m_aElementsSeq.push_back( aName );
+
+ if ( !m_aResultSeq.hasElements() )
+ m_aResultSeq.realloc( 2 );
+
+ if ( m_aResultSeq.getLength() != 2 )
+ throw uno::RuntimeException();
+
+ auto pResultSeq = m_aResultSeq.getArray();
+
+ OUString aPartNameValue = xAttribs->getValueByName( g_aPartNameAttr );
+ if ( aPartNameValue.isEmpty() )
+ throw css::xml::sax::SAXException(); // TODO: the PartName value must present
+
+ OUString aContentTypeValue = xAttribs->getValueByName( g_aContentTypeAttr );
+ if ( aContentTypeValue.isEmpty() )
+ throw css::xml::sax::SAXException(); // TODO: the ContentType value must present
+
+ sal_Int32 nNewResultLen = m_aResultSeq[1].getLength() + 1;
+ pResultSeq[1].realloc( nNewResultLen );
+ auto pSeq = pResultSeq[1].getArray();
+
+ pSeq[nNewResultLen-1].First = aPartNameValue;
+ pSeq[nNewResultLen-1].Second = aContentTypeValue;
+ }
+ else
+ throw css::xml::sax::SAXException(); // TODO: no other elements expected!
+ }
+ else
+ throw css::xml::sax::SAXException(); // TODO: no other elements expected!
+}
+
+
+void SAL_CALL OFOPXMLHelper_Impl::endElement( const OUString& aName )
+{
+ if ( m_nFormat == RELATIONINFO_FORMAT || m_nFormat == CONTENTTYPE_FORMAT )
+ {
+ sal_Int32 nLength = m_aElementsSeq.size();
+ if ( nLength <= 0 )
+ throw css::xml::sax::SAXException(); // TODO: no other end elements expected!
+
+ if ( m_aElementsSeq[nLength-1] != aName )
+ throw css::xml::sax::SAXException(); // TODO: unexpected element ended
+
+ m_aElementsSeq.resize( nLength - 1 );
+ }
+}
+
+
+void SAL_CALL OFOPXMLHelper_Impl::characters( const OUString& /*aChars*/ )
+{
+}
+
+
+void SAL_CALL OFOPXMLHelper_Impl::ignorableWhitespace( const OUString& /*aWhitespaces*/ )
+{
+}
+
+
+void SAL_CALL OFOPXMLHelper_Impl::processingInstruction( const OUString& /*aTarget*/, const OUString& /*aData*/ )
+{
+}
+
+
+void SAL_CALL OFOPXMLHelper_Impl::setDocumentLocator( const uno::Reference< css::xml::sax::XLocator >& /*xLocator*/ )
+{
+}
+
+} // namespace comphelper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/xml/xmltools.cxx b/comphelper/source/xml/xmltools.cxx
new file mode 100644
index 0000000000..1b10964b1a
--- /dev/null
+++ b/comphelper/source/xml/xmltools.cxx
@@ -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/.
+ */
+
+#include <comphelper/xmltools.hxx>
+#include <rtl/random.h>
+#include <tools/Guid.hxx>
+#include <vector>
+
+using namespace com::sun::star;
+
+namespace
+{
+ //Will be inside an xml comment, so can't use '-' in case '--' appears in
+ //output, etc. Despite what *is* legal in an xml comment, just using the
+ //base-64 subset to avoid pain with simplistic third-party parsers
+ const sal_uInt8 aChaffEncoder[] =
+ {
+ 'A', 'Q', 'g', 'w', 'B', 'R', 'h', 'x',
+ 'C', 'S', 'i', 'y', 'D', 'T', 'j', 'z',
+ 'E', 'U', 'k', '0', 'F', 'V', 'l', '1',
+ 'G', 'W', 'm', '2', 'H', 'X', 'n', '3',
+ 'I', 'Y', 'o', '4', 'J', 'Z', 'p', '5',
+ 'K', 'a', 'q', '6', 'L', 'b', 'r', '7',
+ 'M', 'c', 's', '8', 'N', 'd', 't', '9',
+ 'O', 'e', 'u', '+', 'P', 'f', 'v', '/',
+
+ 'A', 'Q', 'g', 'w', 'B', 'R', 'h', 'x',
+ 'C', 'S', 'i', 'y', 'D', 'T', 'j', 'z',
+ 'E', 'U', 'k', '0', 'F', 'V', 'l', '1',
+ 'G', 'W', 'm', '2', 'H', 'X', 'n', '3',
+ 'I', 'Y', 'o', '4', 'J', 'Z', 'p', '5',
+ 'K', 'a', 'q', '6', 'L', 'b', 'r', '7',
+ 'M', 'c', 's', '8', 'N', 'd', 't', '9',
+ 'O', 'e', 'u', '+', 'P', 'f', 'v', '/',
+
+ 'A', 'Q', 'g', 'w', 'B', 'R', 'h', 'x',
+ 'C', 'S', 'i', 'y', 'D', 'T', 'j', 'z',
+ 'E', 'U', 'k', '0', 'F', 'V', 'l', '1',
+ 'G', 'W', 'm', '2', 'H', 'X', 'n', '3',
+ 'I', 'Y', 'o', '4', 'J', 'Z', 'p', '5',
+ 'K', 'a', 'q', '6', 'L', 'b', 'r', '7',
+ 'M', 'c', 's', '8', 'N', 'd', 't', '9',
+ 'O', 'e', 'u', '+', 'P', 'f', 'v', '/',
+
+ 'A', 'Q', 'g', 'w', 'B', 'R', 'h', 'x',
+ 'C', 'S', 'i', 'y', 'D', 'T', 'j', 'z',
+ 'E', 'U', 'k', '0', 'F', 'V', 'l', '1',
+ 'G', 'W', 'm', '2', 'H', 'X', 'n', '3',
+ 'I', 'Y', 'o', '4', 'J', 'Z', 'p', '5',
+ 'K', 'a', 'q', '6', 'L', 'b', 'r', '7',
+ 'M', 'c', 's', '8', 'N', 'd', 't', '9',
+ 'O', 'e', 'u', '+', 'P', 'f', 'v', '/'
+ };
+
+ void encodeChaff(std::vector<sal_uInt8> &rChaff)
+ {
+ static_assert(sizeof(aChaffEncoder) == 256, "this has to cover all chars");
+
+ for (auto & elem : rChaff)
+ {
+ elem = aChaffEncoder[elem];
+ }
+ }
+}
+
+namespace comphelper::xml
+{
+ OString makeXMLChaff()
+ {
+ rtlRandomPool pool = rtl_random_createPool();
+
+ sal_Int8 n;
+ rtl_random_getBytes(pool, &n, 1);
+
+ sal_Int32 nLength = 1024+n;
+ // coverity[tainted_data] - 1024 deliberate random minus max -127/plus max 128
+ std::vector<sal_uInt8> aChaff(nLength);
+ rtl_random_getBytes(pool, aChaff.data(), nLength);
+
+ rtl_random_destroyPool(pool);
+
+ encodeChaff(aChaff);
+
+ return OString(reinterpret_cast<const char*>(aChaff.data()), nLength);
+ }
+
+ OString generateGUIDString()
+ {
+ tools::Guid aGuid(tools::Guid::Generate);
+ return aGuid.getString();
+ }
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/util/comphelp.component b/comphelper/util/comphelp.component
new file mode 100644
index 0000000000..1e4e0c8fbf
--- /dev/null
+++ b/comphelper/util/comphelp.component
@@ -0,0 +1,70 @@
+<?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="AnyCompareFactory"
+ constructor="AnyCompareFactory_get_implementation">
+ <service name="com.sun.star.ucb.AnyCompareFactory"/>
+ </implementation>
+ <implementation name="IndexedPropertyValuesContainer"
+ constructor="IndexedPropertyValuesContainer_get_implementation">
+ <service name="com.sun.star.document.IndexedPropertyValues"/>
+ </implementation>
+ <implementation name="NamedPropertyValuesContainer"
+ constructor="NamedPropertyValuesContainer_get_implementation">
+ <service name="com.sun.star.document.NamedPropertyValues"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.MemoryStream"
+ constructor="com_sun_star_comp_MemoryStream">
+ <service name="com.sun.star.comp.MemoryStream"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.SequenceInputStreamService"
+ constructor="com_sun_star_comp_SequenceInputStreamService">
+ <service name="com.sun.star.io.SequenceInputStream"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.SequenceOutputStreamService"
+ constructor="com_sun_star_comp_SequenceOutputStreamService">
+ <service name="com.sun.star.io.SequenceOutputStream"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.comphelper.OPropertyBag"
+ constructor="com_sun_star_comp_comphelper_OPropertyBag">
+ <service name="com.sun.star.beans.PropertyBag"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.embed.InstanceLocker"
+ constructor="com_sun_star_comp_embed_InstanceLocker">
+ <service name="com.sun.star.embed.InstanceLocker"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.task.OfficeRestartManager"
+ constructor="com_sun_star_comp_task_OfficeRestartManager"
+ single-instance="true">
+ <service name="com.sun.star.comp.task.OfficeRestartManager"/>
+ <singleton name="com.sun.star.task.OfficeRestartManager"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.util.OfficeInstallationDirectories"
+ constructor="com_sun_star_comp_util_OfficeInstallationDirectories"
+ single-instance="true">
+ <service name="com.sun.star.util.OfficeInstallationDirectories"/>
+ <singleton name="com.sun.star.util.theOfficeInstallationDirectories"/>
+ </implementation>
+ <implementation name="org.openoffice.comp.comphelper.EnumerableMap"
+ constructor="org_openoffice_comp_comphelper_EnumerableMap">
+ <service name="com.sun.star.container.EnumerableMap"/>
+ </implementation>
+</component>