summaryrefslogtreecommitdiffstats
path: root/comphelper/source/misc
diff options
context:
space:
mode:
Diffstat (limited to 'comphelper/source/misc')
-rw-r--r--comphelper/source/misc/AccessibleImplementationHelper.cxx43
-rw-r--r--comphelper/source/misc/DirectoryHelper.cxx213
-rw-r--r--comphelper/source/misc/SelectionMultiplex.cxx111
-rw-r--r--comphelper/source/misc/accessiblecomponenthelper.cxx177
-rw-r--r--comphelper/source/misc/accessiblecontexthelper.cxx248
-rw-r--r--comphelper/source/misc/accessibleeventnotifier.cxx274
-rw-r--r--comphelper/source/misc/accessiblekeybindinghelper.cxx97
-rw-r--r--comphelper/source/misc/accessibleselectionhelper.cxx172
-rw-r--r--comphelper/source/misc/accessibletexthelper.cxx800
-rw-r--r--comphelper/source/misc/accessiblewrapper.cxx616
-rw-r--r--comphelper/source/misc/accimplaccess.cxx54
-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.cxx220
-rw-r--r--comphelper/source/misc/compbase.cxx231
-rw-r--r--comphelper/source/misc/componentbase.cxx59
-rw-r--r--comphelper/source/misc/configuration.cxx279
-rw-r--r--comphelper/source/misc/configurationhelper.cxx170
-rw-r--r--comphelper/source/misc/debuggerinfo.cxx91
-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/evtlistenerhlp.cxx37
-rw-r--r--comphelper/source/misc/evtmethodhelper.cxx59
-rw-r--r--comphelper/source/misc/fileurl.cxx27
-rw-r--r--comphelper/source/misc/getexpandeduri.cxx32
-rw-r--r--comphelper/source/misc/graphicmimetype.cxx171
-rw-r--r--comphelper/source/misc/hash.cxx257
-rw-r--r--comphelper/source/misc/instancelocker.cxx445
-rw-r--r--comphelper/source/misc/instancelocker.hxx111
-rw-r--r--comphelper/source/misc/interaction.cxx71
-rw-r--r--comphelper/source/misc/logging.cxx159
-rw-r--r--comphelper/source/misc/lok.cxx287
-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.cxx378
-rw-r--r--comphelper/source/misc/servicedecl.cxx161
-rw-r--r--comphelper/source/misc/sharedmutex.cxx42
-rw-r--r--comphelper/source/misc/simplefileaccessinteraction.cxx120
-rw-r--r--comphelper/source/misc/solarmutex.cxx103
-rw-r--r--comphelper/source/misc/stillreadwriteinteraction.cxx156
-rw-r--r--comphelper/source/misc/storagehelper.cxx713
-rw-r--r--comphelper/source/misc/string.cxx623
-rw-r--r--comphelper/source/misc/synchronousdispatch.cxx81
-rw-r--r--comphelper/source/misc/syntaxhighlight.cxx740
-rw-r--r--comphelper/source/misc/threadpool.cxx394
-rw-r--r--comphelper/source/misc/traceevent.cxx144
-rw-r--r--comphelper/source/misc/typedescriptionref.hxx51
-rw-r--r--comphelper/source/misc/types.cxx146
-rw-r--r--comphelper/source/misc/weak.cxx57
-rw-r--r--comphelper/source/misc/weakeventlistener.cxx74
-rw-r--r--comphelper/source/misc/xmlsechelper.cxx319
64 files changed, 16950 insertions, 0 deletions
diff --git a/comphelper/source/misc/AccessibleImplementationHelper.cxx b/comphelper/source/misc/AccessibleImplementationHelper.cxx
new file mode 100644
index 000000000..a45b6e5f3
--- /dev/null
+++ b/comphelper/source/misc/AccessibleImplementationHelper.cxx
@@ -0,0 +1,43 @@
+/* -*- 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');
+ buf.append(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 000000000..badfe9b62
--- /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 000000000..438eaa80d
--- /dev/null
+++ b/comphelper/source/misc/SelectionMultiplex.cxx
@@ -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 .
+ */
+
+
+#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);
+ }
+ 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 000000000..2962bbac9
--- /dev/null
+++ b/comphelper/source/misc/accessiblecomponenthelper.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/accessiblecomponenthelper.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::~OCommonAccessibleComponent( )
+ {
+ }
+
+
+ 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( )
+ {
+ }
+
+
+ IMPLEMENT_FORWARD_XINTERFACE2( OAccessibleComponentHelper, OCommonAccessibleComponent, OAccessibleComponentHelper_Base )
+ IMPLEMENT_FORWARD_XTYPEPROVIDER2( OAccessibleComponentHelper, OCommonAccessibleComponent, OAccessibleComponentHelper_Base )
+ // (order matters: the first is the class name, the second is the class doing the ref counting)
+
+
+ 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( )
+ {
+ }
+
+
+ IMPLEMENT_FORWARD_XINTERFACE2( OAccessibleExtendedComponentHelper, OCommonAccessibleComponent, OAccessibleExtendedComponentHelper_Base )
+ IMPLEMENT_FORWARD_XTYPEPROVIDER2( OAccessibleExtendedComponentHelper, OCommonAccessibleComponent, OAccessibleExtendedComponentHelper_Base )
+ // (order matters: the first is the class name, the second is the class doing the ref counting)
+
+
+ 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/accessiblecontexthelper.cxx b/comphelper/source/misc/accessiblecontexthelper.cxx
new file mode 100644
index 000000000..ad2c63b1a
--- /dev/null
+++ b/comphelper/source/misc/accessiblecontexthelper.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 <comphelper/accessiblecontexthelper.hxx>
+#include <osl/diagnose.h>
+#include <cppuhelper/weakref.hxx>
+#include <com/sun/star/accessibility/IllegalAccessibleComponentStateException.hpp>
+#include <comphelper/accessibleeventnotifier.hxx>
+#include <comphelper/solarmutex.hxx>
+
+
+namespace comphelper
+{
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::accessibility;
+
+ OAccessibleContextHelper::OAccessibleContextHelper( )
+ :OAccessibleContextHelper_Base( GetMutex() )
+ ,m_nClientId( 0 )
+ {
+ }
+
+
+ OAccessibleContextHelper::~OAccessibleContextHelper( )
+ {
+ // this ensures that the lock, which may be already destroyed as part of the derivee,
+ // is not used anymore
+
+ ensureDisposed();
+ }
+
+
+ void SAL_CALL OAccessibleContextHelper::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 OAccessibleContextHelper::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 OAccessibleContextHelper::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 OAccessibleContextHelper::NotifyAccessibleEvent( const sal_Int16 _nEventId,
+ const Any& _rOldValue, const Any& _rNewValue )
+ {
+ 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;
+ aEvent.Source = *this;
+ aEvent.EventId = _nEventId;
+ aEvent.OldValue = _rOldValue;
+ aEvent.NewValue = _rNewValue;
+
+ // let the notifier handle this event
+ AccessibleEventNotifier::addEvent( m_nClientId, aEvent );
+ }
+
+
+ bool OAccessibleContextHelper::isAlive() const
+ {
+ return !rBHelper.bDisposed && !rBHelper.bInDispose;
+ }
+
+
+ void OAccessibleContextHelper::ensureAlive() const
+ {
+ if( !isAlive() )
+ throw DisposedException();
+ }
+
+
+ void OAccessibleContextHelper::ensureDisposed( )
+ {
+ if ( !rBHelper.bDisposed )
+ {
+ OSL_ENSURE( 0 == m_refCount, "OAccessibleContextHelper::ensureDisposed: this method _has_ to be called from without your dtor only!" );
+ acquire();
+ dispose();
+ }
+ }
+
+
+ void OAccessibleContextHelper::lateInit( const Reference< XAccessible >& _rxAccessible )
+ {
+ m_aCreator = _rxAccessible;
+ }
+
+
+ Reference< XAccessible > OAccessibleContextHelper::getAccessibleCreator( ) const
+ {
+ return m_aCreator;
+ }
+
+
+ OUString SAL_CALL OAccessibleContextHelper::getAccessibleId( )
+ {
+ return OUString();
+ }
+
+
+ sal_Int32 SAL_CALL OAccessibleContextHelper::getAccessibleIndexInParent( )
+ {
+ OExternalLockGuard aGuard( this );
+
+ // -1 for child not found/no parent (according to specification)
+ sal_Int32 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(), "OAccessibleContextHelper::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_Int32 nChildCount = xParentContext->getAccessibleChildCount();
+ for ( sal_Int32 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( "OAccessibleContextHelper::getAccessibleIndexInParent: caught an exception!" );
+ }
+
+ return nRet;
+ }
+
+
+ Locale SAL_CALL OAccessibleContextHelper::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 > OAccessibleContextHelper::implGetParentContext()
+ {
+ Reference< XAccessible > xParent = getAccessibleParent();
+ Reference< XAccessibleContext > xParentContext;
+ if ( xParent.is() )
+ xParentContext = xParent->getAccessibleContext();
+ return xParentContext;
+ }
+
+
+} // 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 000000000..f8bd5a117
--- /dev/null
+++ b/comphelper/source/misc/accessibleeventnotifier.cxx
@@ -0,0 +1,274 @@
+/* -*- 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 <map>
+#include <memory>
+#include <limits>
+
+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::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( );
+
+ // the event listeners for the new client
+ ListenerContainer * pNewListeners = new ListenerContainer();
+ // add the client
+ gaClients.emplace( nNewClientId, pNewListeners );
+
+ // 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
+ delete aClientPos->second;
+ 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
+ std::unique_ptr<ListenerContainer> pListeners(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
+ pListeners->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::vector< Reference< XAccessibleEventListener > > aListeners;
+
+ {
+ 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
+ aListeners = aClientPos->second->getElements(aGuard);
+ }
+
+ // default handling: loop through all listeners, and notify them
+ for ( const auto& rListener : aListeners )
+ {
+ try
+ {
+ rListener->notifyEvent( _rEvent );
+ }
+ catch( const Exception& )
+ {
+ // no assertion, because a broken access remote bridge or something like this
+ // can cause this exception
+ }
+ }
+}
+
+} // 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 000000000..d1db69b98
--- /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 000000000..967c1b079
--- /dev/null
+++ b/comphelper/source/misc/accessibleselectionhelper.cxx
@@ -0,0 +1,172 @@
+/* -*- 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/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_Int32 nChildIndex )
+ {
+ implSelect( nChildIndex, true );
+ }
+
+
+ bool OCommonAccessibleSelection::isAccessibleChildSelected( sal_Int32 nChildIndex )
+ {
+ return implIsSelected( nChildIndex );
+ }
+
+
+ void OCommonAccessibleSelection::clearAccessibleSelection( )
+ {
+ implSelect( ACCESSIBLE_SELECTION_CHILD_ALL, false );
+ }
+
+
+ void OCommonAccessibleSelection::selectAllAccessibleChildren( )
+ {
+ implSelect( ACCESSIBLE_SELECTION_CHILD_ALL, true );
+ }
+
+
+ sal_Int32 OCommonAccessibleSelection::getSelectedAccessibleChildCount( )
+ {
+ sal_Int32 nRet = 0;
+ Reference< XAccessibleContext > xParentContext( implGetAccessibleContext() );
+
+ OSL_ENSURE( xParentContext.is(), "OCommonAccessibleSelection::getSelectedAccessibleChildCount: no parent context!" );
+
+ if( xParentContext.is() )
+ {
+ for( sal_Int32 i = 0, nChildCount = xParentContext->getAccessibleChildCount(); i < nChildCount; i++ )
+ if( implIsSelected( i ) )
+ ++nRet;
+ }
+
+ return nRet;
+ }
+
+
+ Reference< XAccessible > OCommonAccessibleSelection::getSelectedAccessibleChild( sal_Int32 nSelectedChildIndex )
+ {
+ Reference< XAccessible > xRet;
+ Reference< XAccessibleContext > xParentContext( implGetAccessibleContext() );
+
+ OSL_ENSURE( xParentContext.is(), "OCommonAccessibleSelection::getSelectedAccessibleChildCount: no parent context!" );
+
+ if( xParentContext.is() )
+ {
+ for( sal_Int32 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_Int32 nSelectedChildIndex )
+ {
+ implSelect( nSelectedChildIndex, false );
+ }
+
+ OAccessibleSelectionHelper::OAccessibleSelectionHelper()
+ {
+ }
+
+
+ IMPLEMENT_FORWARD_XINTERFACE2( OAccessibleSelectionHelper, OAccessibleComponentHelper, OAccessibleSelectionHelper_Base )
+ IMPLEMENT_FORWARD_XTYPEPROVIDER2( OAccessibleSelectionHelper, OAccessibleComponentHelper, OAccessibleSelectionHelper_Base )
+ // (order matters: the first is the class name, the second is the class doing the ref counting)
+
+
+ Reference< XAccessibleContext > OAccessibleSelectionHelper::implGetAccessibleContext()
+ {
+ return this;
+ }
+
+
+ void SAL_CALL OAccessibleSelectionHelper::selectAccessibleChild( sal_Int32 nChildIndex )
+ {
+ OExternalLockGuard aGuard( this );
+ OCommonAccessibleSelection::selectAccessibleChild( nChildIndex );
+ }
+
+
+ sal_Bool SAL_CALL OAccessibleSelectionHelper::isAccessibleChildSelected( sal_Int32 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_Int32 SAL_CALL OAccessibleSelectionHelper::getSelectedAccessibleChildCount( )
+ {
+ OExternalLockGuard aGuard( this );
+ return OCommonAccessibleSelection::getSelectedAccessibleChildCount();
+ }
+
+
+ Reference< XAccessible > SAL_CALL OAccessibleSelectionHelper::getSelectedAccessibleChild( sal_Int32 nSelectedChildIndex )
+ {
+ OExternalLockGuard aGuard( this );
+ return OCommonAccessibleSelection::getSelectedAccessibleChild( nSelectedChildIndex );
+ }
+
+
+ void SAL_CALL OAccessibleSelectionHelper::deselectAccessibleChild( sal_Int32 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 000000000..27747b3bc
--- /dev/null
+++ b/comphelper/source/misc/accessibletexthelper.cxx
@@ -0,0 +1,800 @@
+/* -*- 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/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 ) )
+ {
+ aResult.SegmentText = sText.copy( nIndex, 1 );
+ aResult.SegmentStart = nIndex;
+ aResult.SegmentEnd = nIndex+1;
+ }
+ }
+ 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 ) )
+ {
+ aResult.SegmentText = sText.copy( nIndex - 1, 1 );
+ aResult.SegmentStart = nIndex-1;
+ aResult.SegmentEnd = nIndex;
+ }
+ }
+ 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 ) )
+ {
+ aResult.SegmentText = sText.copy( nIndex + 1, 1 );
+ aResult.SegmentStart = nIndex+1;
+ aResult.SegmentEnd = nIndex+2;
+ }
+ }
+ 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( )
+ {
+ }
+
+
+ // XInterface
+
+
+ IMPLEMENT_FORWARD_XINTERFACE2( OAccessibleTextHelper, OAccessibleExtendedComponentHelper, OAccessibleTextHelper_Base )
+
+
+ // XTypeProvider
+
+
+ IMPLEMENT_FORWARD_XTYPEPROVIDER2( OAccessibleTextHelper, OAccessibleExtendedComponentHelper, OAccessibleTextHelper_Base )
+
+
+ // 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 000000000..1167f5640
--- /dev/null
+++ b/comphelper/source/misc/accessiblewrapper.cxx
@@ -0,0 +1,616 @@
+/* -*- 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
+ AccessibleMap().swap(m_aChildrenMap);
+ }
+
+
+ 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
+ AccessibleMap().swap(m_aChildrenMap);
+ }
+
+
+ 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
+ Reference< XAccessibleStateSet > xStates( m_xInnerContext->getAccessibleStateSet( ) );
+ OSL_ENSURE( xStates.is(), "OAccessibleContextWrapperHelper::OAccessibleContextWrapperHelper: no inner state set!" );
+ m_xChildMapper->setTransientChildren( !xStates.is() || xStates->contains( 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_Int32 OAccessibleContextWrapperHelper::baseGetAccessibleChildCount( )
+ {
+ return m_xInnerContext->getAccessibleChildCount();
+ }
+
+
+ Reference< XAccessible > OAccessibleContextWrapperHelper::baseGetAccessibleChild( sal_Int32 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_Int16 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_Int32 SAL_CALL OAccessibleContextWrapper::getAccessibleChildCount( )
+ {
+ return baseGetAccessibleChildCount();
+ }
+
+
+ Reference< XAccessible > SAL_CALL OAccessibleContextWrapper::getAccessibleChild( sal_Int32 i )
+ {
+ return baseGetAccessibleChild( i );
+ }
+
+
+ Reference< XAccessible > SAL_CALL OAccessibleContextWrapper::getAccessibleParent( )
+ {
+ return m_xParentAccessible;
+ }
+
+
+ sal_Int32 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();
+ }
+
+
+ Reference< XAccessibleStateSet > 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 SAL_CALL OAccessibleContextWrapper::disposing()
+ {
+ 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
+ OAccessibleContextWrapperHelper::dispose();
+
+ // notify the disposal
+ if ( nClientId )
+ AccessibleEventNotifier::revokeClientNotifyDisposing( nClientId, *this );
+ }
+} // namespace accessibility
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/misc/accimplaccess.cxx b/comphelper/source/misc/accimplaccess.cxx
new file mode 100644
index 000000000..bcf05c926
--- /dev/null
+++ b/comphelper/source/misc/accimplaccess.cxx
@@ -0,0 +1,54 @@
+/* -*- 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/accimplaccess.hxx>
+#include <comphelper/servicehelper.hxx>
+
+
+namespace comphelper
+{
+
+
+ using ::com::sun::star::uno::Sequence;
+
+ OAccessibleImplementationAccess::OAccessibleImplementationAccess( )
+ {
+ }
+
+
+ OAccessibleImplementationAccess::~OAccessibleImplementationAccess( )
+ {
+ }
+
+ const Sequence<sal_Int8> & OAccessibleImplementationAccess::getUnoTunnelId()
+ {
+ static const comphelper::UnoIdInit implID;
+ return implID.getSeq();
+ }
+
+
+ sal_Int64 SAL_CALL OAccessibleImplementationAccess::getSomething( const Sequence< sal_Int8 >& _rIdentifier )
+ {
+ return comphelper::getSomethingImpl(_rIdentifier, this);
+ }
+
+} // namespace comphelper
+
+
+/* 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 000000000..8a2387723
--- /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 000000000..4e97ea124
--- /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 000000000..ebc338b0b
--- /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 000000000..cb8a2f251
--- /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 000000000..a04534ec9
--- /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 000000000..4a71c4a51
--- /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 000000000..e9b173a47
--- /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 OUStringLiteral gaRegPath { u"/registry/com.sun.star.comp.deployment.bundle.PackageRegistryBackend/backenddb.xml" };
+
+ 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 const OUStringLiteral aRegPathFront(u"/uno_packages/cache/registry/com.sun.star.comp.deployment.");
+ static const OUStringLiteral aRegPathBack(u".PackageRegistryBackend/backenddb.xml");
+ // 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 const OUStringLiteral aTokenUser(u"user:");
+ 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 const OUString aSafeMode("SafeMode");
+
+ 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 000000000..5e3e3ca0a
--- /dev/null
+++ b/comphelper/source/misc/base64.cxx
@@ -0,0 +1,220 @@
+/* -*- 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
+
+
+static void ThreeByteToFourByte(const sal_Int8* pBuffer, const sal_Int32 nStart, const sal_Int32 nFullLen, char* aCharBuffer)
+{
+ sal_Int32 nLen(nFullLen - nStart);
+ if (nLen > 3)
+ nLen = 3;
+ if (nLen == 0)
+ {
+ return;
+ }
+
+ 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[0] = aCharBuffer[1] = 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];
+ }
+ }
+}
+
+void Base64::encode(OStringBuffer& aStrBuffer, const uno::Sequence<sal_Int8>& aPass)
+{
+ sal_Int32 i(0);
+ sal_Int32 nBufferLength(aPass.getLength());
+ const sal_Int8* pBuffer = aPass.getConstArray();
+ while (i < nBufferLength)
+ {
+ char aCharBuffer[4];
+ ThreeByteToFourByte(pBuffer, i, nBufferLength, aCharBuffer);
+ aStrBuffer.append(aCharBuffer, std::size(aCharBuffer));
+ i += 3;
+ }
+}
+
+void Base64::encode(OUStringBuffer& aStrBuffer, const uno::Sequence<sal_Int8>& aPass)
+{
+ sal_Int32 i(0);
+ sal_Int32 nBufferLength(aPass.getLength());
+ const sal_Int8* pBuffer = aPass.getConstArray();
+ while (i < nBufferLength)
+ {
+ char aCharBuffer[4];
+ ThreeByteToFourByte(pBuffer, i, nBufferLength, aCharBuffer);
+ aStrBuffer.appendAscii(aCharBuffer, std::size(aCharBuffer));
+ i += 3;
+ }
+}
+
+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 000000000..f8a8897b7
--- /dev/null
+++ b/comphelper/source/misc/compbase.cxx
@@ -0,0 +1,231 @@
+/* -*- 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 <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 000000000..9baec2363
--- /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 000000000..59631dbcc
--- /dev/null
+++ b/comphelper/source/misc/configuration.cxx
@@ -0,0 +1,279 @@
+/* -*- 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 <map>
+#include <memory>
+#include <mutex>
+#include <string_view>
+
+#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/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 <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;
+}
+
+comphelper::detail::ConfigurationWrapper::ConfigurationWrapper():
+ context_(comphelper::getProcessComponentContext()),
+ access_(css::configuration::ReadWriteAccess::create(context_, "*"))
+{}
+
+comphelper::detail::ConfigurationWrapper::~ConfigurationWrapper() {}
+
+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(std::u16string_view path) const
+{
+ // Cache the configuration access, since some of the keys are used in hot code.
+ // Note that this cache is only used by the officecfg:: auto-generated code, using it for anything
+ // else would be unwise because the cache could end up containing stale entries.
+ static std::mutex gMutex;
+ static std::map<OUString, css::uno::Reference< css::container::XNameAccess >> gAccessMap;
+
+ sal_Int32 idx = path.rfind('/');
+ assert(idx!=-1);
+ OUString parentPath(path.substr(0, idx));
+ OUString childName(path.substr(idx+1));
+
+ std::scoped_lock aGuard(gMutex);
+
+ // check cache
+ auto it = gAccessMap.find(parentPath);
+ if (it == gAccessMap.end())
+ {
+ // not in the cache, look it up
+ css::uno::Reference<css::container::XNameAccess> access(
+ access_->getByHierarchicalName(parentPath), css::uno::UNO_QUERY_THROW);
+ it = gAccessMap.emplace(parentPath, access).first;
+ }
+ return it->second->getByName(childName);
+}
+
+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();
+ 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 000000000..f3853baef
--- /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/debuggerinfo.cxx b/comphelper/source/misc/debuggerinfo.cxx
new file mode 100644
index 000000000..1e7116a55
--- /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/dispatchcommand.cxx b/comphelper/source/misc/dispatchcommand.cxx
new file mode 100644
index 000000000..d7b723c72
--- /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 000000000..5edb3949c
--- /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 ::com::sun::star::task::XInteractionRequest;
+
+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-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::find_if(std::cbegin(aEncData), std::cend(aEncData),
+ [](const css::beans::NamedValue& val) {
+ return val.Name == PACKAGE_ENCRYPTIONDATA_SHA256UTF8;
+ })
+ == std::cend(aEncData))
+ {
+ // 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 000000000..6f644336e
--- /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 000000000..21425524e
--- /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/evtlistenerhlp.cxx b/comphelper/source/misc/evtlistenerhlp.cxx
new file mode 100644
index 000000000..2eac31580
--- /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 000000000..5f60d92d6
--- /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 000000000..4f361ab4b
--- /dev/null
+++ b/comphelper/source/misc/fileurl.cxx
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * 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>
+
+bool comphelper::isFileUrl(OUString const& url) { return url.startsWithIgnoreAsciiCase("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 000000000..853e5dbd3
--- /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 000000000..bfa7e5c6a
--- /dev/null
+++ b/comphelper/source/misc/graphicmimetype.cxx
@@ -0,0 +1,171 @@
+/* -*- 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 <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;
+
+ tools::Long const nCount = std::size(aMapper);
+ for (tools::Long 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;
+}
+}
+
+/* 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 000000000..50fd8cd0f
--- /dev/null
+++ b/comphelper/source/misc/hash.cxx
@@ -0,0 +1,257 @@
+/* -*- 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 <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::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::SHA512:
+ return EVP_sha512();
+ }
+
+ return nullptr;
+ }
+#endif
+
+ HashType const meType;
+
+ HashImpl(HashType eType):
+ meType(eType)
+ {
+
+#if USE_TLS_NSS
+ auto const e = NSS_NoDB_Init(nullptr);
+ if (e != SECSuccess) {
+ throw css::uno::RuntimeException("NSS_NoDB_Init failed with " + OUString::number(e));
+ }
+ 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::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 000000000..84c8054ec
--- /dev/null
+++ b/comphelper/source/misc/instancelocker.cxx
@@ -0,0 +1,445 @@
+/* -*- 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 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 000000000..6a050c7f4
--- /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 000000000..9e7b1706d
--- /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 000000000..b656389de
--- /dev/null
+++ b/comphelper/source/misc/logging.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/logging.hxx>
+
+#include <com/sun/star/logging/LoggerPool.hpp>
+
+#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& )
+ {
+ OSL_FAIL( "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& )
+ {
+ OSL_FAIL( "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& )
+ {
+ OSL_FAIL( "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 000000000..b11bf4e83
--- /dev/null
+++ b/comphelper/source/misc/lok.cxx
@@ -0,0 +1,287 @@
+/* -*- 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 <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 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.c_str(), 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
+}
+
+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 000000000..7f402b635
--- /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 000000000..11ef15b30
--- /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 000000000..80d33776c
--- /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 OUStringLiteral ERRMSG_INVALID_COMPONENT_PARAM = u"NULL as component reference not allowed.";
+
+
+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 000000000..f3b609392
--- /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 000000000..39c61a4e5
--- /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();
+
+ {
+ ::osl::MutexGuard 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 )
+{
+ ::osl::MutexGuard 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 000000000..bc95668b9
--- /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 <osl/mutex.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <utility>
+
+namespace comphelper
+{
+
+class OOfficeRestartManager : public ::cppu::WeakImplHelper< css::task::XRestartManager
+ , css::awt::XCallback
+ , css::lang::XServiceInfo >
+{
+ ::osl::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 000000000..db580bea4
--- /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 000000000..96d466641
--- /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 000000000..34a6a0c8a
--- /dev/null
+++ b/comphelper/source/misc/sequenceashashmap.cxx
@@ -0,0 +1,378 @@
+/* -*- 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>
+
+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.c_str()));
+ 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.c_str()).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.c_str()).toFloat();
+ else if (aTypeClass == uno::TypeClass_DOUBLE)
+ aAny <<= o3tl::toDouble(rValue);
+ else if (aTypeClass == uno::TypeClass_STRING)
+ aAny <<= OUString::fromUtf8(rValue.c_str());
+ }
+ }
+ 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(rJson.getStr());
+ 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.c_str());
+ if (rType == "string")
+ aValue.Value <<= OUString::fromUtf8(rValue.c_str());
+ else if (rType == "boolean")
+ aValue.Value <<= OString(rValue.c_str()).toBoolean();
+ else if (rType == "float")
+ aValue.Value <<= OString(rValue.c_str()).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.c_str()).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
+ 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/servicedecl.cxx b/comphelper/source/misc/servicedecl.cxx
new file mode 100644
index 000000000..2b16539d1
--- /dev/null
+++ b/comphelper/source/misc/servicedecl.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/servicedecl.hxx>
+#include <o3tl/string_view.hxx>
+#include <rtl/string.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <comphelper/sequence.hxx>
+#include <com/sun/star/lang/XSingleComponentFactory.hpp>
+#include <cassert>
+#include <vector>
+
+using namespace com::sun::star;
+
+namespace comphelper::service_decl {
+
+const char cDelim = ';';
+
+class ServiceDecl::Factory :
+ public cppu::WeakImplHelper<lang::XSingleComponentFactory,
+ lang::XServiceInfo>
+{
+public:
+ explicit Factory( ServiceDecl const& rServiceDecl )
+ : m_rServiceDecl(rServiceDecl) {}
+ // noncopyable
+ Factory(const Factory&) = delete;
+ const Factory& operator=(const Factory&) = delete;
+
+ // XServiceInfo:
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( OUString const& name ) override;
+ virtual uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+ // XSingleComponentFactory:
+ virtual uno::Reference<uno::XInterface> SAL_CALL createInstanceWithContext(
+ uno::Reference<uno::XComponentContext> const& xContext ) override;
+ virtual uno::Reference<uno::XInterface> SAL_CALL
+ createInstanceWithArgumentsAndContext(
+ uno::Sequence<uno::Any> const& args,
+ uno::Reference<uno::XComponentContext> const& xContext ) override;
+
+private:
+ virtual ~Factory() override;
+
+ ServiceDecl const& m_rServiceDecl;
+};
+
+ServiceDecl::Factory::~Factory()
+{
+}
+
+// XServiceInfo:
+OUString ServiceDecl::Factory::getImplementationName()
+{
+ return m_rServiceDecl.getImplementationName();
+}
+
+sal_Bool ServiceDecl::Factory::supportsService( OUString const& name )
+{
+ return m_rServiceDecl.supportsService(name);
+}
+
+uno::Sequence<OUString> ServiceDecl::Factory::getSupportedServiceNames()
+{
+ return m_rServiceDecl.getSupportedServiceNames();
+}
+
+// XSingleComponentFactory:
+uno::Reference<uno::XInterface> ServiceDecl::Factory::createInstanceWithContext(
+ uno::Reference<uno::XComponentContext> const& xContext )
+{
+ return m_rServiceDecl.m_createFunc(
+ m_rServiceDecl, uno::Sequence<uno::Any>(), xContext );
+}
+
+uno::Reference<uno::XInterface>
+ServiceDecl::Factory::createInstanceWithArgumentsAndContext(
+ uno::Sequence<uno::Any > const& args,
+ uno::Reference<uno::XComponentContext> const& xContext )
+{
+ return m_rServiceDecl.m_createFunc(
+ m_rServiceDecl, args, xContext );
+}
+
+void * ServiceDecl::getFactory( char const* pImplName ) const
+{
+ if (rtl_str_compare(m_pImplName, pImplName) == 0) {
+ lang::XSingleComponentFactory * const pFac( new Factory(*this) );
+ pFac->acquire();
+ return pFac;
+ }
+ return nullptr;
+}
+
+uno::Sequence<OUString> ServiceDecl::getSupportedServiceNames() const
+{
+ std::vector<OUString> vec;
+
+ OString const str(m_pServiceNames);
+ sal_Int32 nIndex = 0;
+ do {
+ std::string_view const token( o3tl::getToken(str, 0, cDelim, nIndex ) );
+ vec.emplace_back( token.data(), token.size(),
+ RTL_TEXTENCODING_ASCII_US );
+ }
+ while (nIndex >= 0);
+
+ return comphelper::containerToSequence(vec);
+}
+
+bool ServiceDecl::supportsService( std::u16string_view name ) const
+{
+ OString const str(m_pServiceNames);
+ sal_Int32 nIndex = 0;
+ do {
+ std::string_view const token( o3tl::getToken(str, 0, cDelim, nIndex ) );
+ if (o3tl::equalsAscii(name, token))
+ return true;
+ }
+ while (nIndex >= 0);
+ return false;
+}
+
+OUString ServiceDecl::getImplementationName() const
+{
+ return OUString::createFromAscii(m_pImplName);
+}
+
+void* component_getFactoryHelper( const char* pImplName,
+ std::initializer_list<ServiceDecl const *> args )
+{
+ for (auto const i: args) {
+ assert(i != nullptr);
+ void * fac = i->getFactory(pImplName);
+ if (fac != nullptr) {
+ return fac;
+ }
+ }
+ return nullptr;
+}
+
+} // namespace
+
+/* 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 000000000..58ae35df5
--- /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 000000000..8cd77af7d
--- /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 000000000..5d1052327
--- /dev/null
+++ b/comphelper/source/misc/solarmutex.cxx
@@ -0,0 +1,103 @@
+/* -*- 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_nThreadId( 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 = osl::Thread::getCurrentIdentifier();
+ 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 = 0;
+ }
+
+ for ( sal_uInt32 n = nCount ; n ; --n )
+ m_aMutex.release();
+
+ return nCount;
+}
+
+bool SolarMutex::IsCurrentThread() const
+{
+ return m_nThreadId == osl::Thread::getCurrentIdentifier();
+}
+
+bool SolarMutex::tryToAcquire()
+{
+ if ( m_aMutex.tryToAcquire() )
+ {
+ m_nThreadId = osl::Thread::getCurrentIdentifier();
+ 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 000000000..88bc25bc4
--- /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 000000000..5f8527bcf
--- /dev/null
+++ b/comphelper/source/misc/storagehelper.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 <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/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;
+
+ uno::Reference< css::lang::XUnoTunnel > xInputTunnel( xInput, uno::UNO_QUERY );
+ comphelper::ByteReader* pByteReader = nullptr;
+ comphelper::ByteWriter* pByteWriter = nullptr;
+ if (xInputTunnel)
+ pByteReader = reinterpret_cast< comphelper::ByteReader* >( xInputTunnel->getSomething( comphelper::ByteReader::getUnoTunnelId() ) );
+ if (pByteReader)
+ {
+ uno::Reference< css::lang::XUnoTunnel > xOutputTunnel( xOutput, uno::UNO_QUERY );
+ if (xOutputTunnel)
+ pByteWriter = reinterpret_cast< comphelper::ByteWriter* >( xOutputTunnel->getSomething( comphelper::ByteWriter::getUnoTunnelId() ) );
+ }
+
+ if (pByteWriter)
+ {
+ sal_Int32 nRead;
+ sal_Int8 aTempBuf[ nConstBufferSize ];
+ do
+ {
+ nRead = pByteReader->readSomeBytes ( aTempBuf, nConstBufferSize );
+ pByteWriter->writeSomeBytes ( aTempBuf, nRead );
+ }
+ while ( nRead == nConstBufferSize );
+ }
+ else
+ {
+ sal_Int32 nRead;
+ uno::Sequence < sal_Int8 > aSequence ( nConstBufferSize );
+
+ do
+ {
+ nRead = xInput->readBytes ( aSequence, nConstBufferSize );
+ if ( nRead < nConstBufferSize )
+ {
+ uno::Sequence < sal_Int8 > aTempBuf ( aSequence.getConstArray(), nRead );
+ xOutput->writeBytes ( aTempBuf );
+ }
+ else
+ 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& )
+ {
+ OSL_ENSURE( false, "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 )
+{
+ return IsValidZipEntryFileName( aName.data(), aName.size(), bSlashAllowed );
+}
+
+
+bool OStorageHelper::IsValidZipEntryFileName(
+ const sal_Unicode *pChar, sal_Int32 nLength, bool bSlashAllowed )
+{
+ for ( sal_Int32 i = 0; i < nLength; i++ )
+ {
+ switch ( pChar[i] )
+ {
+ case '\\':
+ case '?':
+ case '<':
+ case '>':
+ case '\"':
+ case '|':
+ case ':':
+ return false;
+ case '/':
+ if ( !bSlashAllowed )
+ return false;
+ break;
+ default:
+ if ( pChar[i] < 32 || (pChar[i] >= 0xD800 && pChar[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 000000000..d40b9136b
--- /dev/null
+++ b/comphelper/source/misc/string.cxx
@@ -0,0 +1,623 @@
+/* -*- 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 <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/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);
+}
+
+OString strip(const OString& rIn, char c)
+{
+ auto x = tmpl_stripStartString<OString, char>(rIn, c);
+ return stripEnd(x, c);
+}
+
+std::string_view strip(std::string_view rIn, char c)
+{
+ auto x = tmpl_stripStart<std::string_view, char>(rIn, c);
+ return stripEnd(x, c);
+}
+
+OUString strip(const OUString& rIn, sal_Unicode c)
+{
+ auto x = tmpl_stripStartString<OUString, sal_Unicode>(rIn, c);
+ return stripEnd(x, c);
+}
+
+std::u16string_view strip(std::u16string_view rIn, sal_Unicode c)
+{
+ auto x = tmpl_stripStart<std::u16string_view, sal_Unicode>(rIn, c);
+ return stripEnd(x, 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);
+}
+
+static sal_uInt32 decimalStringToNumber(
+ OUString const & str, sal_Int32 nStart, sal_Int32 nLength )
+{
+ sal_uInt32 result = 0;
+ for( sal_Int32 i = nStart; i < nStart + nLength; )
+ {
+ sal_uInt32 c = str.iterateCodePoints(&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;
+}
+
+sal_uInt32 decimalStringToNumber(
+ OUString const & str )
+{
+ return decimalStringToNumber(str, 0, str.getLength());
+}
+
+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, 0, nStartsDigitLHS);
+ sal_uInt32 nRHS = comphelper::string::decimalStringToNumber(rRHS, 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, nLHSFirstDigitPos, nLHSChunkLen);
+ sal_uInt32 nRHS = comphelper::string::decimalStringToNumber(rRHS, 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); });
+}
+
+namespace
+{
+ template <typename T, typename I, typename O> T tmpl_reverseString(I rIn)
+ {
+ if (rIn.empty())
+ return T();
+
+ typename I::size_type i = rIn.size();
+ O sBuf(static_cast<sal_Int32>(i));
+ while (i)
+ sBuf.append(rIn[--i]);
+ return sBuf.makeStringAndClear();
+ }
+}
+
+OUString reverseString(std::u16string_view rStr)
+{
+ return tmpl_reverseString<OUString, std::u16string_view, OUStringBuffer>(rStr);
+}
+
+OString reverseString(std::string_view rStr)
+{
+ return tmpl_reverseString<OString, std::string_view, OStringBuffer>(rStr);
+}
+
+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);
+}
+
+}
+
+/* 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 000000000..1602c8963
--- /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 000000000..3ce8086e6
--- /dev/null
+++ b/comphelper/source/misc/syntaxhighlight.cxx
@@ -0,0 +1,740 @@
+/* -*- 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 <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 000000000..f0a71eb05
--- /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 000000000..fb07e1caa
--- /dev/null
+++ b/comphelper/source/misc/traceevent.cxx
@@ -0,0 +1,144 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sal/config.h>
+
+#include <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)
+{
+ std::lock_guard aGuard(g_aMutex);
+
+ g_aRecording.emplace_back(sObject);
+
+ if (s_nBufferSize > 0 && g_aRecording.size() >= s_nBufferSize)
+ {
+ if (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 000000000..f4580cb2e
--- /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 000000000..8887d5b5a
--- /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/weak.cxx b/comphelper/source/misc/weak.cxx
new file mode 100644
index 000000000..02cf40e2b
--- /dev/null
+++ b/comphelper/source/misc/weak.cxx
@@ -0,0 +1,57 @@
+/* -*- 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/weak.hxx>
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+
+namespace comphelper
+{
+
+OWeakTypeObject::OWeakTypeObject()
+{
+}
+
+OWeakTypeObject::~OWeakTypeObject()
+{
+}
+
+Any SAL_CALL OWeakTypeObject::queryInterface(const Type & rType )
+{
+ if( rType == cppu::UnoType<XTypeProvider>::get() )
+ return Any( Reference< XTypeProvider >(this) );
+ else
+ return ::cppu::OWeakObject::queryInterface( rType );
+}
+
+Sequence< Type > SAL_CALL OWeakTypeObject::getTypes( )
+{
+ return Sequence< Type >();
+}
+
+Sequence< ::sal_Int8 > SAL_CALL OWeakTypeObject::getImplementationId( )
+{
+ return Sequence< ::sal_Int8 >();
+}
+
+}
+
+/* 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 000000000..e8ce46590
--- /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 SAL_CALL OWeakEventListenerAdapter::disposing( )
+ {
+ 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 000000000..5b1a438ab
--- /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: */