summaryrefslogtreecommitdiffstats
path: root/comphelper/source/container
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /comphelper/source/container
parentInitial commit. (diff)
downloadlibreoffice-cb75148ebd0135178ff46f89a30139c44f8d2040.tar.xz
libreoffice-cb75148ebd0135178ff46f89a30139c44f8d2040.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'comphelper/source/container')
-rw-r--r--comphelper/source/container/IndexedPropertyValuesContainer.cxx127
-rw-r--r--comphelper/source/container/NamedPropertyValuesContainer.cxx169
-rw-r--r--comphelper/source/container/container.cxx140
-rw-r--r--comphelper/source/container/containermultiplexer.cxx159
-rw-r--r--comphelper/source/container/embeddedobjectcontainer.cxx1508
-rw-r--r--comphelper/source/container/enumerablemap.cxx713
-rw-r--r--comphelper/source/container/enumhelper.cxx280
-rw-r--r--comphelper/source/container/interfacecontainer2.cxx421
-rw-r--r--comphelper/source/container/namecontainer.cxx166
9 files changed, 3683 insertions, 0 deletions
diff --git a/comphelper/source/container/IndexedPropertyValuesContainer.cxx b/comphelper/source/container/IndexedPropertyValuesContainer.cxx
new file mode 100644
index 000000000..f5b7358d6
--- /dev/null
+++ b/comphelper/source/container/IndexedPropertyValuesContainer.cxx
@@ -0,0 +1,127 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <comphelper/indexedpropertyvalues.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <o3tl/safeint.hxx>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+using namespace com::sun::star;
+
+
+namespace comphelper {
+
+
+IndexedPropertyValuesContainer::IndexedPropertyValuesContainer() noexcept
+{
+}
+
+// XIndexContainer
+void SAL_CALL IndexedPropertyValuesContainer::insertByIndex( sal_Int32 nIndex, const css::uno::Any& aElement )
+{
+ sal_Int32 nSize(maProperties.size());
+ if ((nSize < nIndex) || (nIndex < 0))
+ throw lang::IndexOutOfBoundsException();
+
+ uno::Sequence<beans::PropertyValue> aProps;
+ if (!(aElement >>= aProps))
+ throw lang::IllegalArgumentException("element is not beans::PropertyValue", static_cast<cppu::OWeakObject*>(this), 2);
+ if (nSize == nIndex)
+ maProperties.push_back(aProps);
+ else
+ maProperties.insert(maProperties.begin() + nIndex, aProps);
+}
+
+void SAL_CALL IndexedPropertyValuesContainer::removeByIndex( sal_Int32 nIndex )
+{
+ if ((nIndex < 0) || (o3tl::make_unsigned(nIndex) >= maProperties.size()))
+ throw lang::IndexOutOfBoundsException();
+
+ maProperties.erase(maProperties.begin() + nIndex);
+}
+
+// XIndexReplace
+void SAL_CALL IndexedPropertyValuesContainer::replaceByIndex( sal_Int32 nIndex, const css::uno::Any& aElement )
+{
+ sal_Int32 nSize(maProperties.size());
+ if ((nIndex >= nSize) || (nIndex < 0))
+ throw lang::IndexOutOfBoundsException();
+
+ uno::Sequence<beans::PropertyValue> aProps;
+ if (!(aElement >>= aProps))
+ throw lang::IllegalArgumentException("element is not beans::PropertyValue", static_cast<cppu::OWeakObject*>(this), 2);
+ maProperties[nIndex] = aProps;
+}
+
+// XIndexAccess
+sal_Int32 SAL_CALL IndexedPropertyValuesContainer::getCount( )
+{
+ return maProperties.size();
+}
+
+css::uno::Any SAL_CALL IndexedPropertyValuesContainer::getByIndex( sal_Int32 nIndex )
+{
+ sal_Int32 nSize(maProperties.size());
+ if ((nIndex >= nSize) || (nIndex < 0))
+ throw lang::IndexOutOfBoundsException();
+
+ return uno::Any( maProperties[nIndex] );
+}
+
+// XElementAccess
+css::uno::Type SAL_CALL IndexedPropertyValuesContainer::getElementType( )
+{
+ return cppu::UnoType<uno::Sequence<beans::PropertyValue>>::get();
+}
+
+sal_Bool SAL_CALL IndexedPropertyValuesContainer::hasElements( )
+{
+ return !maProperties.empty();
+}
+
+//XServiceInfo
+OUString SAL_CALL IndexedPropertyValuesContainer::getImplementationName( )
+{
+ return "IndexedPropertyValuesContainer";
+}
+
+sal_Bool SAL_CALL IndexedPropertyValuesContainer::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL IndexedPropertyValuesContainer::getSupportedServiceNames( )
+{
+ return { "com.sun.star.document.IndexedPropertyValues" };
+}
+
+} // namespace comphelper
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+IndexedPropertyValuesContainer_get_implementation(
+ css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new comphelper::IndexedPropertyValuesContainer());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/container/NamedPropertyValuesContainer.cxx b/comphelper/source/container/NamedPropertyValuesContainer.cxx
new file mode 100644
index 000000000..a44837f11
--- /dev/null
+++ b/comphelper/source/container/NamedPropertyValuesContainer.cxx
@@ -0,0 +1,169 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/uno/Sequence.h>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <map>
+
+
+namespace com::sun::star::uno { class XComponentContext; }
+using namespace com::sun::star;
+
+typedef std::map< OUString, uno::Sequence<beans::PropertyValue> > NamedPropertyValues;
+
+namespace {
+
+class NamedPropertyValuesContainer : public cppu::WeakImplHelper< container::XNameContainer, lang::XServiceInfo >
+{
+public:
+ NamedPropertyValuesContainer() noexcept;
+
+ // XNameContainer
+ virtual void SAL_CALL insertByName( const OUString& aName, const css::uno::Any& aElement ) override;
+ virtual void SAL_CALL removeByName( const OUString& Name ) override;
+
+ // XNameReplace
+ virtual void SAL_CALL replaceByName( const OUString& aName, const css::uno::Any& aElement ) override;
+
+ // XNameAccess
+ virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getElementNames( ) override;
+ virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override;
+
+ // XElementAccess
+ virtual css::uno::Type SAL_CALL getElementType( ) override;
+ virtual sal_Bool SAL_CALL hasElements( ) override;
+
+ //XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+private:
+ NamedPropertyValues maProperties;
+};
+
+}
+
+NamedPropertyValuesContainer::NamedPropertyValuesContainer() noexcept
+{
+}
+
+// XNameContainer
+void SAL_CALL NamedPropertyValuesContainer::insertByName( const OUString& aName, const uno::Any& aElement )
+{
+ if( maProperties.find( aName ) != maProperties.end() )
+ throw container::ElementExistException();
+
+ uno::Sequence<beans::PropertyValue> aProps;
+ if( !(aElement >>= aProps ) )
+ throw lang::IllegalArgumentException("element is not beans::PropertyValue", static_cast<cppu::OWeakObject*>(this), 2);
+
+ maProperties.emplace( aName, aProps );
+}
+
+void SAL_CALL NamedPropertyValuesContainer::removeByName( const OUString& Name )
+{
+ NamedPropertyValues::iterator aIter = maProperties.find( Name );
+ if( aIter == maProperties.end() )
+ throw container::NoSuchElementException();
+
+ maProperties.erase( aIter );
+}
+
+// XNameReplace
+void SAL_CALL NamedPropertyValuesContainer::replaceByName( const OUString& aName, const css::uno::Any& aElement )
+{
+ NamedPropertyValues::iterator aIter = maProperties.find( aName );
+ if( aIter == maProperties.end() )
+ throw container::NoSuchElementException();
+
+ uno::Sequence<beans::PropertyValue> aProps;
+ if( !(aElement >>= aProps) )
+ throw lang::IllegalArgumentException("element is not beans::PropertyValue", static_cast<cppu::OWeakObject*>(this), 2);
+
+ (*aIter).second = aProps;
+}
+
+// XNameAccess
+css::uno::Any SAL_CALL NamedPropertyValuesContainer::getByName( const OUString& aName )
+{
+ NamedPropertyValues::iterator aIter = maProperties.find( aName );
+ if( aIter == maProperties.end() )
+ throw container::NoSuchElementException();
+
+ uno::Any aElement;
+
+ aElement <<= (*aIter).second;
+
+ return aElement;
+}
+
+css::uno::Sequence< OUString > SAL_CALL NamedPropertyValuesContainer::getElementNames( )
+{
+ return comphelper::mapKeysToSequence(maProperties);
+}
+
+sal_Bool SAL_CALL NamedPropertyValuesContainer::hasByName( const OUString& aName )
+{
+ NamedPropertyValues::iterator aIter = maProperties.find( aName );
+ return aIter != maProperties.end();
+}
+
+// XElementAccess
+css::uno::Type SAL_CALL NamedPropertyValuesContainer::getElementType( )
+{
+ return cppu::UnoType<uno::Sequence<beans::PropertyValue>>::get();
+}
+
+sal_Bool SAL_CALL NamedPropertyValuesContainer::hasElements( )
+{
+ return !maProperties.empty();
+}
+
+//XServiceInfo
+OUString SAL_CALL NamedPropertyValuesContainer::getImplementationName( )
+{
+ return "NamedPropertyValuesContainer";
+}
+
+sal_Bool SAL_CALL NamedPropertyValuesContainer::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL NamedPropertyValuesContainer::getSupportedServiceNames( )
+{
+ return { "com.sun.star.document.NamedPropertyValues" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+NamedPropertyValuesContainer_get_implementation(
+ css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new NamedPropertyValuesContainer());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/container/container.cxx b/comphelper/source/container/container.cxx
new file mode 100644
index 000000000..7b2432723
--- /dev/null
+++ b/comphelper/source/container/container.cxx
@@ -0,0 +1,140 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/uno/XInterface.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/container/XChild.hpp>
+#include <comphelper/container.hxx>
+#include <o3tl/any.hxx>
+#include <utility>
+#include <osl/diagnose.h>
+
+
+namespace comphelper
+{
+
+
+IndexAccessIterator::IndexAccessIterator(css::uno::Reference< css::uno::XInterface> xStartingPoint)
+ :m_xStartingPoint(std::move(xStartingPoint))
+{
+ OSL_ENSURE(m_xStartingPoint.is(), "IndexAccessIterator::IndexAccessIterator : no starting point !");
+}
+
+IndexAccessIterator::~IndexAccessIterator() {}
+
+
+css::uno::Reference< css::uno::XInterface> const & IndexAccessIterator::Next()
+{
+ bool bCheckingStartingPoint = !m_xCurrentObject.is();
+ // Is the current node the starting point?
+ bool bAlreadyCheckedCurrent = m_xCurrentObject.is();
+ // Have I already tested the current node through ShouldHandleElement?
+ if (!m_xCurrentObject.is())
+ m_xCurrentObject = m_xStartingPoint;
+
+ css::uno::Reference< css::uno::XInterface> xSearchLoop( m_xCurrentObject);
+ bool bHasMoreToSearch = true;
+ bool bFoundSomething = false;
+ while (!bFoundSomething && bHasMoreToSearch)
+ {
+ // Priming loop
+ if (!bAlreadyCheckedCurrent && ShouldHandleElement(xSearchLoop))
+ {
+ m_xCurrentObject = xSearchLoop;
+ bFoundSomething = true;
+ }
+ else
+ {
+ // First, check to see if there's a match below
+ css::uno::Reference< css::container::XIndexAccess> xContainerAccess(xSearchLoop, css::uno::UNO_QUERY);
+ if (xContainerAccess.is() && xContainerAccess->getCount() && ShouldStepInto(xContainerAccess))
+ {
+ css::uno::Any aElement(xContainerAccess->getByIndex(0));
+ xSearchLoop = *o3tl::doAccess<css::uno::Reference<css::uno::XInterface>>(aElement);
+ bCheckingStartingPoint = false;
+
+ m_arrChildIndizies.push_back(sal_Int32(0));
+ }
+ else
+ { // otherwise, look above and to the right, if possible
+ while (!m_arrChildIndizies.empty())
+ { // If the list isn't empty and there's nothing above
+ css::uno::Reference< css::container::XChild> xChild(xSearchLoop, css::uno::UNO_QUERY);
+ OSL_ENSURE(xChild.is(), "IndexAccessIterator::Next : a content has no appropriate interface !");
+
+ css::uno::Reference< css::uno::XInterface> xParent( xChild->getParent());
+ xContainerAccess.set(xParent, css::uno::UNO_QUERY);
+ OSL_ENSURE(xContainerAccess.is(), "IndexAccessIterator::Next : a content has an invalid parent !");
+
+ // Remove the index that SearchLoop had within this parent from my stack
+ sal_Int32 nOldSearchChildIndex = m_arrChildIndizies[m_arrChildIndizies.size() - 1];
+ m_arrChildIndizies.pop_back();
+
+ if (nOldSearchChildIndex < xContainerAccess->getCount() - 1)
+ { // Move to the right in this row
+ ++nOldSearchChildIndex;
+ // and check the next child
+ css::uno::Any aElement(xContainerAccess->getByIndex(nOldSearchChildIndex));
+ xSearchLoop = *o3tl::doAccess<css::uno::Reference<css::uno::XInterface>>(aElement);
+ bCheckingStartingPoint = false;
+ // and update its position in the list.
+ m_arrChildIndizies.push_back(nOldSearchChildIndex);
+
+ break;
+ }
+ // Finally, if there's nothing more to do in this row (to the right), we'll move on to the next row.
+ xSearchLoop = xParent;
+ bCheckingStartingPoint = false;
+ }
+
+ if (m_arrChildIndizies.empty() && !bCheckingStartingPoint)
+ { //This is the case if there is nothing to the right in the original search loop
+ bHasMoreToSearch = false;
+ }
+ }
+
+ if (bHasMoreToSearch)
+ { // If there is still a node in the tree which can be tested
+ if (ShouldHandleElement(xSearchLoop))
+ {
+ m_xCurrentObject = xSearchLoop;
+ bFoundSomething = true;
+ }
+ else
+ if (bCheckingStartingPoint)
+ bHasMoreToSearch = false;
+ bAlreadyCheckedCurrent = true;
+ }
+ }
+ }
+
+ if (!bFoundSomething)
+ {
+ OSL_ENSURE(m_arrChildIndizies.empty(), "IndexAccessIterator::Next : items left on stack ! how this ?");
+ Invalidate();
+ }
+
+ return m_xCurrentObject;
+}
+
+
+} // namespace comphelper
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/container/containermultiplexer.cxx b/comphelper/source/container/containermultiplexer.cxx
new file mode 100644
index 000000000..c687e7279
--- /dev/null
+++ b/comphelper/source/container/containermultiplexer.cxx
@@ -0,0 +1,159 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <comphelper/containermultiplexer.hxx>
+#include <com/sun/star/container/XContainer.hpp>
+#include <osl/diagnose.h>
+
+namespace comphelper
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::container;
+
+ OContainerListener::OContainerListener(::osl::Mutex& _rMutex)
+ :m_rMutex(_rMutex)
+ {
+ }
+
+
+ OContainerListener::~OContainerListener()
+ {
+ if (m_xAdapter.is())
+ {
+ m_xAdapter->dispose();
+ }
+ }
+
+
+ void OContainerListener::_elementInserted( const ContainerEvent& /*_rEvent*/ )
+ {
+ }
+
+
+ void OContainerListener::_elementRemoved( const ContainerEvent& )
+ {
+ }
+
+
+ void OContainerListener::_elementReplaced( const ContainerEvent& /*_rEvent*/ )
+ {
+ }
+
+
+ void OContainerListener::_disposing(const EventObject& )
+ {
+ }
+
+
+ void OContainerListener::setAdapter(OContainerListenerAdapter* pAdapter)
+ {
+ ::osl::MutexGuard aGuard(m_rMutex);
+ m_xAdapter = pAdapter;
+ }
+
+ OContainerListenerAdapter::OContainerListenerAdapter(OContainerListener* _pListener,
+ const Reference< XContainer >& _rxContainer)
+ :m_xContainer(_rxContainer)
+ ,m_pListener(_pListener)
+ {
+ if (m_pListener)
+ m_pListener->setAdapter(this);
+
+ osl_atomic_increment(&m_refCount);
+ try
+ {
+ m_xContainer->addContainerListener(this);
+ }
+ catch(const Exception&)
+ {
+ OSL_FAIL("Exception caught!");
+ }
+ osl_atomic_decrement(&m_refCount);
+ }
+
+
+ OContainerListenerAdapter::~OContainerListenerAdapter()
+ {
+ }
+
+
+ void OContainerListenerAdapter::dispose()
+ {
+ if (!m_xContainer.is())
+ return;
+
+ try
+ {
+ Reference< XContainerListener > xPreventDelete(this);
+ m_xContainer->removeContainerListener(xPreventDelete);
+ m_pListener->setAdapter(nullptr);
+ }
+ catch(const Exception&)
+ {
+ OSL_FAIL("Exception caught!");
+ }
+ m_xContainer = nullptr;
+ m_pListener = nullptr;
+ }
+
+
+ void SAL_CALL OContainerListenerAdapter::disposing( const EventObject& _rSource)
+ {
+ if (m_pListener)
+ {
+ // tell the listener
+ m_pListener->_disposing(_rSource);
+ // disconnect the listener
+ if ( m_pListener )
+ m_pListener->setAdapter(nullptr);
+ }
+
+ m_xContainer = nullptr;
+ m_pListener = nullptr;
+ }
+
+
+ void SAL_CALL OContainerListenerAdapter::elementInserted( const ContainerEvent& _rEvent )
+ {
+ if (m_pListener)
+ m_pListener->_elementInserted(_rEvent);
+ }
+
+
+ void SAL_CALL OContainerListenerAdapter::elementRemoved( const ContainerEvent& _rEvent )
+ {
+ if (m_pListener)
+ m_pListener->_elementRemoved(_rEvent);
+ }
+
+
+ void SAL_CALL OContainerListenerAdapter::elementReplaced( const ContainerEvent& _rEvent )
+ {
+ if (m_pListener)
+ m_pListener->_elementReplaced(_rEvent);
+ }
+
+
+} // namespace comphelper
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/container/embeddedobjectcontainer.cxx b/comphelper/source/container/embeddedobjectcontainer.cxx
new file mode 100644
index 000000000..094597fe6
--- /dev/null
+++ b/comphelper/source/container/embeddedobjectcontainer.cxx
@@ -0,0 +1,1508 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/embed/EmbeddedObjectCreator.hpp>
+#include <com/sun/star/embed/WrongStateException.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <com/sun/star/embed/XEmbedPersist.hpp>
+#include <com/sun/star/embed/XLinkageSupport.hpp>
+#include <com/sun/star/embed/XTransactedObject.hpp>
+#include <com/sun/star/embed/XOptimizedStorage.hpp>
+#include <com/sun/star/embed/EntryInitModes.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/util/XCloseable.hpp>
+#include <com/sun/star/util/XModifiable.hpp>
+#include <com/sun/star/embed/EmbedStates.hpp>
+#include <com/sun/star/beans/XPropertySetInfo.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/embed/Aspects.hpp>
+#include <com/sun/star/embed/EmbedMisc.hpp>
+
+#include <comphelper/seqstream.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <comphelper/embeddedobjectcontainer.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <cppuhelper/weakref.hxx>
+#include <sal/log.hxx>
+
+#include <algorithm>
+#include <unordered_map>
+
+
+using namespace ::com::sun::star;
+
+namespace comphelper {
+
+typedef std::unordered_map<OUString, uno::Reference<embed::XEmbeddedObject>> EmbeddedObjectContainerNameMap;
+struct EmbedImpl
+{
+ // TODO/LATER: remove objects from temp. Container storage when object is disposed
+ EmbeddedObjectContainerNameMap maNameToObjectMap;
+ // to speed up lookup by Reference
+ std::unordered_map<uno::Reference<embed::XEmbeddedObject>, OUString> maObjectToNameMap;
+ uno::Reference < embed::XStorage > mxStorage;
+ EmbeddedObjectContainer* mpTempObjectContainer;
+ uno::Reference < embed::XStorage > mxImageStorage;
+ uno::WeakReference < uno::XInterface > m_xModel;
+
+ bool mbOwnsStorage : 1;
+ bool mbUserAllowsLinkUpdate : 1;
+
+ const uno::Reference < embed::XStorage >& GetReplacements();
+};
+
+const uno::Reference < embed::XStorage >& EmbedImpl::GetReplacements()
+{
+ if ( !mxImageStorage.is() )
+ {
+ try
+ {
+ mxImageStorage = mxStorage->openStorageElement(
+ "ObjectReplacements", embed::ElementModes::READWRITE );
+ }
+ catch (const uno::Exception&)
+ {
+ mxImageStorage = mxStorage->openStorageElement(
+ "ObjectReplacements", embed::ElementModes::READ );
+ }
+ }
+
+ if ( !mxImageStorage.is() )
+ throw io::IOException("No ObjectReplacements");
+
+ return mxImageStorage;
+}
+
+EmbeddedObjectContainer::EmbeddedObjectContainer()
+ : pImpl(new EmbedImpl)
+{
+ pImpl->mxStorage = ::comphelper::OStorageHelper::GetTemporaryStorage();
+ pImpl->mbOwnsStorage = true;
+ pImpl->mbUserAllowsLinkUpdate = true;
+ pImpl->mpTempObjectContainer = nullptr;
+}
+
+EmbeddedObjectContainer::EmbeddedObjectContainer( const uno::Reference < embed::XStorage >& rStor )
+ : pImpl(new EmbedImpl)
+{
+ pImpl->mxStorage = rStor;
+ pImpl->mbOwnsStorage = false;
+ pImpl->mbUserAllowsLinkUpdate = true;
+ pImpl->mpTempObjectContainer = nullptr;
+}
+
+EmbeddedObjectContainer::EmbeddedObjectContainer( const uno::Reference < embed::XStorage >& rStor, const uno::Reference < uno::XInterface >& xModel )
+ : pImpl(new EmbedImpl)
+{
+ pImpl->mxStorage = rStor;
+ pImpl->mbOwnsStorage = false;
+ pImpl->mbUserAllowsLinkUpdate = true;
+ pImpl->mpTempObjectContainer = nullptr;
+ pImpl->m_xModel = xModel;
+}
+
+void EmbeddedObjectContainer::SwitchPersistence( const uno::Reference < embed::XStorage >& rStor )
+{
+ ReleaseImageSubStorage();
+
+ if ( pImpl->mbOwnsStorage )
+ pImpl->mxStorage->dispose();
+
+ pImpl->mxStorage = rStor;
+ pImpl->mbOwnsStorage = false;
+}
+
+bool EmbeddedObjectContainer::CommitImageSubStorage()
+{
+ if ( !pImpl->mxImageStorage )
+ return true;
+
+ try
+ {
+ bool bReadOnlyMode = true;
+ uno::Reference < beans::XPropertySet > xSet(pImpl->mxImageStorage,uno::UNO_QUERY);
+ if ( xSet.is() )
+ {
+ // get the open mode from the parent storage
+ sal_Int32 nMode = 0;
+ uno::Any aAny = xSet->getPropertyValue("OpenMode");
+ if ( aAny >>= nMode )
+ bReadOnlyMode = !(nMode & embed::ElementModes::WRITE );
+ } // if ( xSet.is() )
+ if ( !bReadOnlyMode )
+ {
+ uno::Reference< embed::XTransactedObject > xTransact( pImpl->mxImageStorage, uno::UNO_QUERY_THROW );
+ xTransact->commit();
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+void EmbeddedObjectContainer::ReleaseImageSubStorage()
+{
+ CommitImageSubStorage();
+
+ if ( pImpl->mxImageStorage.is() )
+ {
+ try
+ {
+ pImpl->mxImageStorage->dispose();
+ pImpl->mxImageStorage.clear();
+ }
+ catch (const uno::Exception&)
+ {
+ SAL_WARN( "comphelper.container", "Problems releasing image substorage!" );
+ }
+ }
+}
+
+EmbeddedObjectContainer::~EmbeddedObjectContainer()
+{
+ ReleaseImageSubStorage();
+
+ if ( pImpl->mbOwnsStorage )
+ pImpl->mxStorage->dispose();
+
+ delete pImpl->mpTempObjectContainer;
+}
+
+void EmbeddedObjectContainer::CloseEmbeddedObjects()
+{
+ for( const auto& rObj : pImpl->maNameToObjectMap )
+ {
+ uno::Reference < util::XCloseable > const & xClose = rObj.second;
+ if( xClose.is() )
+ {
+ try
+ {
+ xClose->close( true );
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ }
+ }
+}
+
+OUString EmbeddedObjectContainer::CreateUniqueObjectName()
+{
+ OUString aStr;
+ sal_Int32 i=1;
+ do
+ {
+ aStr = "Object " + OUString::number( i++ );
+ }
+ while( HasEmbeddedObject( aStr ) );
+ // TODO/LATER: should we consider deleted objects?
+
+ return aStr;
+}
+
+uno::Sequence < OUString > EmbeddedObjectContainer::GetObjectNames() const
+{
+ return comphelper::mapKeysToSequence(pImpl->maNameToObjectMap);
+}
+
+bool EmbeddedObjectContainer::HasEmbeddedObjects() const
+{
+ return !pImpl->maNameToObjectMap.empty();
+}
+
+bool EmbeddedObjectContainer::HasEmbeddedObject( const OUString& rName )
+{
+ auto aIt = pImpl->maNameToObjectMap.find( rName );
+ if (aIt != pImpl->maNameToObjectMap.end())
+ return true;
+ if (!pImpl->mxStorage.is())
+ return false;
+ return pImpl->mxStorage->hasByName(rName);
+}
+
+bool EmbeddedObjectContainer::HasEmbeddedObject( const uno::Reference < embed::XEmbeddedObject >& xObj ) const
+{
+ return pImpl->maObjectToNameMap.find(xObj) != pImpl->maObjectToNameMap.end();
+}
+
+bool EmbeddedObjectContainer::HasInstantiatedEmbeddedObject( const OUString& rName )
+{
+ // allows to detect whether the object was already instantiated
+ // currently the filter instantiate it on loading, so this method allows
+ // to avoid objects pointing to the same persistence
+ auto aIt = pImpl->maNameToObjectMap.find( rName );
+ return ( aIt != pImpl->maNameToObjectMap.end() );
+}
+
+OUString EmbeddedObjectContainer::GetEmbeddedObjectName( const css::uno::Reference < css::embed::XEmbeddedObject >& xObj ) const
+{
+ auto it = pImpl->maObjectToNameMap.find(xObj);
+ if (it == pImpl->maObjectToNameMap.end())
+ {
+ SAL_WARN( "comphelper.container", "Unknown object!" );
+ return OUString();
+ }
+ return it->second;
+}
+
+uno::Reference< embed::XEmbeddedObject>
+EmbeddedObjectContainer::GetEmbeddedObject(
+ const OUString& rName, OUString const*const pBaseURL)
+{
+ SAL_WARN_IF( rName.isEmpty(), "comphelper.container", "Empty object name!");
+
+ uno::Reference < embed::XEmbeddedObject > xObj;
+ auto aIt = pImpl->maNameToObjectMap.find( rName );
+
+#if OSL_DEBUG_LEVEL > 1
+ uno::Reference < container::XNameAccess > xAccess( pImpl->mxStorage, uno::UNO_QUERY );
+ uno::Sequence< OUString> aSeq = xAccess->getElementNames();
+ const OUString* pIter = aSeq.getConstArray();
+ const OUString* pEnd = pIter + aSeq.getLength();
+ for(;pIter != pEnd;++pIter)
+ {
+ (void)*pIter;
+ }
+ OSL_ENSURE( aIt != pImpl->maNameToObjectMap.end() || xAccess->hasByName(rName), "Could not return object!" );
+#endif
+
+ // check if object was already created
+ if ( aIt != pImpl->maNameToObjectMap.end() )
+ xObj = (*aIt).second;
+ else
+ xObj = Get_Impl(rName, uno::Reference<embed::XEmbeddedObject>(), pBaseURL);
+
+ return xObj;
+}
+
+uno::Reference<embed::XEmbeddedObject> EmbeddedObjectContainer::Get_Impl(
+ const OUString& rName,
+ const uno::Reference<embed::XEmbeddedObject>& xCopy,
+ OUString const*const pBaseURL)
+{
+ uno::Reference < embed::XEmbeddedObject > xObj;
+ try
+ {
+ // create the object from the storage
+ uno::Reference < beans::XPropertySet > xSet( pImpl->mxStorage, uno::UNO_QUERY );
+ bool bReadOnlyMode = true;
+ if ( xSet.is() )
+ {
+ // get the open mode from the parent storage
+ sal_Int32 nMode = 0;
+ uno::Any aAny = xSet->getPropertyValue("OpenMode");
+ if ( aAny >>= nMode )
+ bReadOnlyMode = !(nMode & embed::ElementModes::WRITE );
+ }
+
+ // object was not added until now - should happen only by calling this method from "inside"
+ //TODO/LATER: it would be good to detect an error when an object should be created already, but isn't (not an "inside" call)
+ uno::Reference < embed::XEmbeddedObjectCreator > xFactory = embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() );
+ uno::Sequence< beans::PropertyValue > aObjDescr(1 + (xCopy.is() ? 1 : 0) + (pBaseURL ? 1 : 0));
+ auto itObjDescr = aObjDescr.getArray();
+ itObjDescr->Name = "Parent";
+ itObjDescr->Value <<= pImpl->m_xModel.get();
+ if (pBaseURL)
+ {
+ ++itObjDescr;
+ itObjDescr->Name = "DefaultParentBaseURL";
+ itObjDescr->Value <<= *pBaseURL;
+ }
+ if ( xCopy.is() )
+ {
+ ++itObjDescr;
+ itObjDescr->Name = "CloneFrom";
+ itObjDescr->Value <<= xCopy;
+ }
+
+ uno::Sequence< beans::PropertyValue > aMediaDescr{ comphelper::makePropertyValue(
+ "ReadOnly", bReadOnlyMode) };
+ xObj.set( xFactory->createInstanceInitFromEntry(
+ pImpl->mxStorage, rName,
+ aMediaDescr, aObjDescr ), uno::UNO_QUERY );
+
+ // insert object into my list
+ AddEmbeddedObject( xObj, rName );
+ }
+ catch (uno::Exception const& e)
+ {
+ SAL_WARN("comphelper.container", "EmbeddedObjectContainer::Get_Impl: exception caught: " << e);
+ }
+
+ return xObj;
+}
+
+uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::CreateEmbeddedObject( const uno::Sequence < sal_Int8 >& rClassId,
+ const uno::Sequence < beans::PropertyValue >& rArgs, OUString& rNewName, OUString const* pBaseURL )
+{
+ if ( rNewName.isEmpty() )
+ rNewName = CreateUniqueObjectName();
+
+ SAL_WARN_IF( HasEmbeddedObject(rNewName), "comphelper.container", "Object to create already exists!");
+
+ // create object from classid by inserting it into storage
+ uno::Reference < embed::XEmbeddedObject > xObj;
+ try
+ {
+ uno::Reference < embed::XEmbeddedObjectCreator > xFactory = embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() );
+
+ const size_t nExtraArgs = pBaseURL ? 2 : 1;
+ uno::Sequence< beans::PropertyValue > aObjDescr( rArgs.getLength() + nExtraArgs );
+ auto pObjDescr = aObjDescr.getArray();
+ pObjDescr[0].Name = "Parent";
+ pObjDescr[0].Value <<= pImpl->m_xModel.get();
+ if (pBaseURL)
+ {
+ pObjDescr[1].Name = "DefaultParentBaseURL";
+ pObjDescr[1].Value <<= *pBaseURL;
+ }
+ std::copy( rArgs.begin(), rArgs.end(), pObjDescr + nExtraArgs );
+ xObj.set( xFactory->createInstanceInitNew(
+ rClassId, OUString(), pImpl->mxStorage, rNewName,
+ aObjDescr ), uno::UNO_QUERY );
+
+ AddEmbeddedObject( xObj, rNewName );
+
+ OSL_ENSURE( !xObj.is() || xObj->getCurrentState() != embed::EmbedStates::LOADED,
+ "A freshly create object should be running always!" );
+ }
+ catch (uno::Exception const& e)
+ {
+ SAL_WARN("comphelper.container", "EmbeddedObjectContainer::CreateEmbeddedObject: exception caught: " << e);
+ }
+
+ return xObj;
+}
+
+uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::CreateEmbeddedObject( const uno::Sequence < sal_Int8 >& rClassId, OUString& rNewName, OUString const* pBaseURL )
+{
+ return CreateEmbeddedObject( rClassId, uno::Sequence < beans::PropertyValue >(), rNewName, pBaseURL );
+}
+
+void EmbeddedObjectContainer::AddEmbeddedObject( const css::uno::Reference < css::embed::XEmbeddedObject >& xObj, const OUString& rName )
+{
+#if OSL_DEBUG_LEVEL > 1
+ SAL_WARN_IF( rName.isEmpty(), "comphelper.container", "Added object doesn't have a name!");
+ uno::Reference < container::XNameAccess > xAccess( pImpl->mxStorage, uno::UNO_QUERY );
+ uno::Reference < embed::XEmbedPersist > xEmb( xObj, uno::UNO_QUERY );
+ uno::Reference < embed::XLinkageSupport > xLink( xEmb, uno::UNO_QUERY );
+ // if the object has a persistence and the object is not a link than it must have persistence entry in the storage
+ OSL_ENSURE( !( xEmb.is() && ( !xLink.is() || !xLink->isLink() ) ) || xAccess->hasByName(rName),
+ "Added element not in storage!" );
+#endif
+
+ // remember object - it needs to be in storage already
+ auto aIt = pImpl->maNameToObjectMap.find( rName );
+ OSL_ENSURE( aIt == pImpl->maNameToObjectMap.end(), "Element already inserted!" );
+ pImpl->maNameToObjectMap[ rName ] = xObj;
+ pImpl->maObjectToNameMap[ xObj ] = rName;
+ uno::Reference < container::XChild > xChild( xObj, uno::UNO_QUERY );
+ if ( xChild.is() && xChild->getParent() != pImpl->m_xModel.get() )
+ xChild->setParent( pImpl->m_xModel.get() );
+
+ // look for object in temporary container
+ if ( !pImpl->mpTempObjectContainer )
+ return;
+
+ auto& rObjectContainer = pImpl->mpTempObjectContainer->pImpl->maNameToObjectMap;
+ auto aIter = std::find_if(rObjectContainer.begin(), rObjectContainer.end(),
+ [&xObj](const EmbeddedObjectContainerNameMap::value_type& rEntry) { return rEntry.second == xObj; });
+ if (aIter == rObjectContainer.end())
+ return;
+
+ // copy replacement image from temporary container (if there is any)
+ OUString aTempName = aIter->first;
+ OUString aMediaType;
+ uno::Reference < io::XInputStream > xStream = pImpl->mpTempObjectContainer->GetGraphicStream( xObj, &aMediaType );
+ if ( xStream.is() )
+ {
+ InsertGraphicStream( xStream, rName, aMediaType );
+ xStream = nullptr;
+ pImpl->mpTempObjectContainer->RemoveGraphicStream( aTempName );
+ }
+
+ // remove object from storage of temporary container
+ uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
+ if ( xPersist.is() )
+ {
+ try
+ {
+ pImpl->mpTempObjectContainer->pImpl->mxStorage->removeElement( aTempName );
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ }
+
+ // temp. container needs to forget the object
+ pImpl->mpTempObjectContainer->pImpl->maObjectToNameMap.erase( aIter->second );
+ pImpl->mpTempObjectContainer->pImpl->maNameToObjectMap.erase( aIter );
+}
+
+bool EmbeddedObjectContainer::StoreEmbeddedObject(
+ const uno::Reference < embed::XEmbeddedObject >& xObj, OUString& rName, bool bCopy,
+ const OUString& rSrcShellID, const OUString& rDestShellID )
+{
+ uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
+ if ( rName.isEmpty() )
+ rName = CreateUniqueObjectName();
+
+#if OSL_DEBUG_LEVEL > 1
+ uno::Reference < container::XNameAccess > xAccess( pImpl->mxStorage, uno::UNO_QUERY );
+ OSL_ENSURE( !xPersist.is() || !xAccess->hasByName(rName), "Inserting element already present in storage!" );
+ OSL_ENSURE( xPersist.is() || xObj->getCurrentState() == embed::EmbedStates::RUNNING, "Non persistent object inserted!");
+#endif
+
+ // insert objects' storage into the container storage (if object has one)
+ try
+ {
+ if ( xPersist.is() )
+ {
+ uno::Sequence < beans::PropertyValue > aSeq;
+ if ( bCopy )
+ {
+ auto aObjArgs(::comphelper::InitPropertySequence({
+ { "SourceShellID", uno::Any(rSrcShellID) },
+ { "DestinationShellID", uno::Any(rDestShellID) }
+ }));
+ xPersist->storeToEntry(pImpl->mxStorage, rName, aSeq, aObjArgs);
+ }
+ else
+ {
+ //TODO/LATER: possible optimization, don't store immediately
+ //xPersist->setPersistentEntry( pImpl->mxStorage, rName, embed::EntryInitModes::ENTRY_NO_INIT, aSeq, aSeq );
+ xPersist->storeAsEntry( pImpl->mxStorage, rName, aSeq, aSeq );
+ xPersist->saveCompleted( true );
+ }
+ }
+ }
+ catch (uno::Exception const& e)
+ {
+ SAL_WARN("comphelper.container", "EmbeddedObjectContainer::StoreEmbeddedObject: exception caught: " << e);
+ // TODO/LATER: better error recovery should keep storage intact
+ return false;
+ }
+
+ return true;
+}
+
+bool EmbeddedObjectContainer::InsertEmbeddedObject( const uno::Reference < embed::XEmbeddedObject >& xObj, OUString& rName )
+{
+ // store it into the container storage
+ if (StoreEmbeddedObject(xObj, rName, false, OUString(), OUString()))
+ {
+ // remember object
+ AddEmbeddedObject( xObj, rName );
+ return true;
+ }
+ else
+ return false;
+}
+
+uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::InsertEmbeddedObject( const uno::Reference < io::XInputStream >& xStm, OUString& rNewName )
+{
+ if ( rNewName.isEmpty() )
+ rNewName = CreateUniqueObjectName();
+
+ // store it into the container storage
+ bool bIsStorage = false;
+ try
+ {
+ // first try storage persistence
+ uno::Reference < embed::XStorage > xStore = ::comphelper::OStorageHelper::GetStorageFromInputStream( xStm );
+
+ // storage was created from stream successfully
+ bIsStorage = true;
+
+ uno::Reference < embed::XStorage > xNewStore = pImpl->mxStorage->openStorageElement( rNewName, embed::ElementModes::READWRITE );
+ xStore->copyToStorage( xNewStore );
+ }
+ catch (const uno::Exception&)
+ {
+ if ( bIsStorage )
+ // it is storage persistence, but opening of new substorage or copying to it failed
+ return uno::Reference < embed::XEmbeddedObject >();
+
+ // stream didn't contain a storage, now try stream persistence
+ try
+ {
+ uno::Reference < io::XStream > xNewStream = pImpl->mxStorage->openStreamElement( rNewName, embed::ElementModes::READWRITE );
+ ::comphelper::OStorageHelper::CopyInputToOutput( xStm, xNewStream->getOutputStream() );
+
+ // No mediatype is provided so the default for OLE objects value is used
+ // it is correct so for now, but what if somebody introduces a new stream based embedded object?
+ // Probably introducing of such an object must be restricted ( a storage must be used! ).
+ uno::Reference< beans::XPropertySet > xProps( xNewStream, uno::UNO_QUERY_THROW );
+ xProps->setPropertyValue("MediaType",
+ uno::Any( OUString( "application/vnd.sun.star.oleobject" ) ) );
+ }
+ catch (uno::Exception const& e)
+ {
+ // complete disaster!
+ SAL_WARN("comphelper.container", "EmbeddedObjectContainer::InsertEmbeddedObject: exception caught: " << e);
+ return uno::Reference < embed::XEmbeddedObject >();
+ }
+ }
+
+ // stream was copied into the container storage in either way, now try to open something form it
+ uno::Reference < embed::XEmbeddedObject > xRet = GetEmbeddedObject( rNewName );
+ try
+ {
+ if ( !xRet.is() )
+ // no object could be created, so withdraw insertion
+ pImpl->mxStorage->removeElement( rNewName );
+ }
+ catch (const uno::Exception&)
+ {
+ }
+
+ return xRet;
+}
+
+uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::InsertEmbeddedObject( const css::uno::Sequence < css::beans::PropertyValue >& aMedium, OUString& rNewName, OUString const* pBaseURL )
+{
+ if ( rNewName.isEmpty() )
+ rNewName = CreateUniqueObjectName();
+
+ uno::Reference < embed::XEmbeddedObject > xObj;
+ try
+ {
+ uno::Reference < embed::XEmbeddedObjectCreator > xFactory = embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() );
+ uno::Sequence< beans::PropertyValue > aObjDescr(pBaseURL ? 2 : 1);
+ auto pObjDescr = aObjDescr.getArray();
+ pObjDescr[0].Name = "Parent";
+ pObjDescr[0].Value <<= pImpl->m_xModel.get();
+ if (pBaseURL)
+ {
+ pObjDescr[1].Name = "DefaultParentBaseURL";
+ pObjDescr[1].Value <<= *pBaseURL;
+ }
+ xObj.set( xFactory->createInstanceInitFromMediaDescriptor(
+ pImpl->mxStorage, rNewName, aMedium, aObjDescr ), uno::UNO_QUERY );
+ uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
+
+ OSL_ENSURE( !xObj.is() || xObj->getCurrentState() != embed::EmbedStates::LOADED,
+ "A freshly create object should be running always!" );
+
+ // possible optimization: store later!
+ if ( xPersist.is())
+ xPersist->storeOwn();
+
+ AddEmbeddedObject( xObj, rNewName );
+ }
+ catch (const uno::Exception&)
+ {
+ }
+
+ return xObj;
+}
+
+uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::InsertEmbeddedLink( const css::uno::Sequence < css::beans::PropertyValue >& aMedium, OUString& rNewName )
+{
+ if ( rNewName.isEmpty() )
+ rNewName = CreateUniqueObjectName();
+
+ uno::Reference < embed::XEmbeddedObject > xObj;
+ try
+ {
+ uno::Reference < embed::XEmbeddedObjectCreator > xFactory = embed::EmbeddedObjectCreator::create(::comphelper::getProcessComponentContext());
+ uno::Sequence< beans::PropertyValue > aObjDescr{ comphelper::makePropertyValue(
+ "Parent", pImpl->m_xModel.get()) };
+ xObj.set( xFactory->createInstanceLink( pImpl->mxStorage, rNewName, aMedium, aObjDescr ), uno::UNO_QUERY );
+
+ uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
+
+ OSL_ENSURE( !xObj.is() || xObj->getCurrentState() != embed::EmbedStates::LOADED,
+ "A freshly create object should be running always!" );
+
+ // possible optimization: store later!
+ if ( xPersist.is())
+ xPersist->storeOwn();
+
+ AddEmbeddedObject( xObj, rNewName );
+ }
+ catch (uno::Exception const& e)
+ {
+ SAL_WARN("comphelper.container", "EmbeddedObjectContainer::InsertEmbeddedLink: "
+ "exception caught: " << e);
+ }
+
+ return xObj;
+}
+
+bool EmbeddedObjectContainer::TryToCopyGraphReplacement( EmbeddedObjectContainer& rSrc,
+ const OUString& aOrigName,
+ const OUString& aTargetName )
+{
+ bool bResult = false;
+
+ if ( ( &rSrc != this || aOrigName != aTargetName ) && !aOrigName.isEmpty() && !aTargetName.isEmpty() )
+ {
+ OUString aMediaType;
+ uno::Reference < io::XInputStream > xGrStream = rSrc.GetGraphicStream( aOrigName, &aMediaType );
+ if ( xGrStream.is() )
+ bResult = InsertGraphicStream( xGrStream, aTargetName, aMediaType );
+ }
+
+ return bResult;
+}
+
+uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::CopyAndGetEmbeddedObject(
+ EmbeddedObjectContainer& rSrc, const uno::Reference <embed::XEmbeddedObject>& xObj, OUString& rName,
+ const OUString& rSrcShellID, const OUString& rDestShellID )
+{
+ uno::Reference< embed::XEmbeddedObject > xResult;
+
+ // TODO/LATER: For now only objects that implement XEmbedPersist have a replacement image, it might change in future
+ // do an incompatible change so that object name is provided in all the move and copy methods
+ OUString aOrigName;
+ try
+ {
+ uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY_THROW );
+ aOrigName = xPersist->getEntryName();
+ }
+ catch (const uno::Exception&)
+ {
+ }
+
+ if ( rName.isEmpty() )
+ rName = CreateUniqueObjectName();
+
+ // objects without persistence are not really stored by the method
+ if (xObj.is() && StoreEmbeddedObject(xObj, rName, true, rSrcShellID, rDestShellID))
+ {
+ SAL_INFO_IF(rDestShellID.isEmpty(), "comphelper.container",
+ "SfxObjectShell with no base URL?"); // every shell has a base URL, except the clipboard SwDocShell
+ xResult = Get_Impl(rName, xObj, &rDestShellID);
+ if ( !xResult.is() )
+ {
+ // this is a case when object has no real persistence
+ // in such cases a new object should be explicitly created and initialized with the data of the old one
+ try
+ {
+ uno::Reference< embed::XLinkageSupport > xOrigLinkage( xObj, uno::UNO_QUERY );
+ if ( xOrigLinkage.is() && xOrigLinkage->isLink() )
+ {
+ // this is an OOo link, it has no persistence
+ OUString aURL = xOrigLinkage->getLinkURL();
+ if ( aURL.isEmpty() )
+ throw uno::RuntimeException();
+
+ // create new linked object from the URL the link is based on
+ uno::Reference < embed::XEmbeddedObjectCreator > xCreator =
+ embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() );
+
+ uno::Sequence< beans::PropertyValue > aMediaDescr{ comphelper::makePropertyValue(
+ "URL", aURL) };
+ uno::Sequence< beans::PropertyValue > aObjDescr{ comphelper::makePropertyValue(
+ "Parent", pImpl->m_xModel.get()) };
+ xResult.set(xCreator->createInstanceLink(
+ pImpl->mxStorage,
+ rName,
+ aMediaDescr,
+ aObjDescr ),
+ uno::UNO_QUERY_THROW );
+ }
+ else
+ {
+ // the component is required for copying of this object
+ if ( xObj->getCurrentState() == embed::EmbedStates::LOADED )
+ xObj->changeState( embed::EmbedStates::RUNNING );
+
+ // this must be an object based on properties, otherwise we can not copy it currently
+ uno::Reference< beans::XPropertySet > xOrigProps( xObj->getComponent(), uno::UNO_QUERY_THROW );
+
+ // use object class ID to create a new one and transfer all the properties
+ uno::Reference < embed::XEmbeddedObjectCreator > xCreator =
+ embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() );
+
+ uno::Sequence< beans::PropertyValue > aObjDescr{ comphelper::makePropertyValue(
+ "Parent", pImpl->m_xModel.get()) };
+ xResult.set(xCreator->createInstanceInitNew(
+ xObj->getClassID(),
+ xObj->getClassName(),
+ pImpl->mxStorage,
+ rName,
+ aObjDescr ),
+ uno::UNO_QUERY_THROW );
+
+ if ( xResult->getCurrentState() == embed::EmbedStates::LOADED )
+ xResult->changeState( embed::EmbedStates::RUNNING );
+
+ uno::Reference< beans::XPropertySet > xTargetProps( xResult->getComponent(), uno::UNO_QUERY_THROW );
+
+ // copy all the properties from xOrigProps to xTargetProps
+ uno::Reference< beans::XPropertySetInfo > xOrigInfo = xOrigProps->getPropertySetInfo();
+ if ( !xOrigInfo.is() )
+ throw uno::RuntimeException();
+
+ const uno::Sequence< beans::Property > aPropertiesList = xOrigInfo->getProperties();
+ for ( const auto & p : aPropertiesList )
+ {
+ try
+ {
+ xTargetProps->setPropertyValue(
+ p.Name,
+ xOrigProps->getPropertyValue( p.Name ) );
+ }
+ catch (const beans::PropertyVetoException&)
+ {
+ // impossibility to copy readonly property is not treated as an error for now
+ // but the assertion is helpful to detect such scenarios and review them
+ SAL_WARN( "comphelper.container", "Could not copy readonly property!" );
+ }
+ }
+ }
+
+ if ( xResult.is() )
+ AddEmbeddedObject( xResult, rName );
+ }
+ catch (const uno::Exception&)
+ {
+ if ( xResult.is() )
+ {
+ try
+ {
+ xResult->close( true );
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ xResult.clear();
+ }
+ }
+ }
+ }
+
+ SAL_WARN_IF( !xResult.is(), "comphelper.container", "Can not copy embedded object that has no persistence!" );
+
+ if ( xResult.is() )
+ {
+ // the object is successfully copied, try to copy graphical replacement
+ if ( !aOrigName.isEmpty() )
+ TryToCopyGraphReplacement( rSrc, aOrigName, rName );
+
+ // the object might need the size to be set
+ try
+ {
+ if ( xResult->getStatus( embed::Aspects::MSOLE_CONTENT ) & embed::EmbedMisc::EMBED_NEEDSSIZEONLOAD )
+ xResult->setVisualAreaSize( embed::Aspects::MSOLE_CONTENT,
+ xObj->getVisualAreaSize( embed::Aspects::MSOLE_CONTENT ) );
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ }
+
+ return xResult;
+}
+
+// #i119941, bKeepToTempStorage: use to specify whether store the removed object to temporary storage+
+void EmbeddedObjectContainer::RemoveEmbeddedObject( const OUString& rName, bool bKeepToTempStorage )
+{
+ uno::Reference < embed::XEmbeddedObject > xObj = GetEmbeddedObject( rName );
+ if ( xObj.is() )
+ RemoveEmbeddedObject( xObj, bKeepToTempStorage );
+}
+
+bool EmbeddedObjectContainer::MoveEmbeddedObject( const OUString& rName, EmbeddedObjectContainer& rCnt )
+{
+ // find object entry
+ auto aIt2 = rCnt.pImpl->maNameToObjectMap.find( rName );
+ OSL_ENSURE( aIt2 == rCnt.pImpl->maNameToObjectMap.end(), "Object does already exist in target container!" );
+
+ if ( aIt2 != rCnt.pImpl->maNameToObjectMap.end() )
+ return false;
+
+ uno::Reference < embed::XEmbeddedObject > xObj;
+ auto aIt = pImpl->maNameToObjectMap.find( rName );
+ if ( aIt != pImpl->maNameToObjectMap.end() )
+ {
+ xObj = (*aIt).second;
+ try
+ {
+ if ( xObj.is() )
+ {
+ // move object
+ OUString aName( rName );
+ rCnt.InsertEmbeddedObject( xObj, aName );
+ pImpl->maObjectToNameMap.erase( aIt->second );
+ pImpl->maNameToObjectMap.erase( aIt );
+ uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
+ if ( xPersist.is() )
+ pImpl->mxStorage->removeElement( rName );
+ }
+ else
+ {
+ // copy storages; object *must* have persistence!
+ uno::Reference < embed::XStorage > xOld = pImpl->mxStorage->openStorageElement( rName, embed::ElementModes::READ );
+ uno::Reference < embed::XStorage > xNew = rCnt.pImpl->mxStorage->openStorageElement( rName, embed::ElementModes::READWRITE );
+ xOld->copyToStorage( xNew );
+ }
+
+ rCnt.TryToCopyGraphReplacement( *this, rName, rName );
+ // RemoveGraphicStream( rName );
+
+ return true;
+ }
+ catch (const uno::Exception&)
+ {
+ SAL_WARN( "comphelper.container", "Could not move object!");
+ return false;
+ }
+
+ }
+ else
+ SAL_WARN( "comphelper.container", "Unknown object!");
+ return false;
+}
+
+// #i119941, bKeepToTempStorage: use to specify whether store the removed object to temporary storage+
+bool EmbeddedObjectContainer::RemoveEmbeddedObject( const uno::Reference < embed::XEmbeddedObject >& xObj, bool bKeepToTempStorage )
+{
+ uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
+ OUString aName;
+ if ( xPersist.is() )
+ aName = xPersist->getEntryName();
+
+#if OSL_DEBUG_LEVEL > 1
+ uno::Reference < container::XNameAccess > xAccess( pImpl->mxStorage, uno::UNO_QUERY );
+ uno::Reference < embed::XLinkageSupport > xLink( xPersist, uno::UNO_QUERY );
+ sal_Bool bIsNotEmbedded = !xPersist.is() || ( xLink.is() && xLink->isLink() );
+
+ // if the object has a persistence and the object is not a link than it must have persistence entry in the storage
+ OSL_ENSURE( bIsNotEmbedded || xAccess->hasByName(aName), "Removing element not present in storage!" );
+#endif
+
+ // somebody still needs the object, so we must assign a temporary persistence
+ try
+ {
+ if ( xPersist.is() && bKeepToTempStorage ) // #i119941
+ {
+
+ if ( !pImpl->mpTempObjectContainer )
+ {
+ pImpl->mpTempObjectContainer = new EmbeddedObjectContainer();
+ try
+ {
+ // TODO/LATER: in future probably the temporary container will have two storages ( of two formats )
+ // the media type will be provided with object insertion
+ OUString aOrigStorMediaType;
+ uno::Reference< beans::XPropertySet > xStorProps( pImpl->mxStorage, uno::UNO_QUERY_THROW );
+ static constexpr OUStringLiteral s_sMediaType(u"MediaType");
+ xStorProps->getPropertyValue( s_sMediaType ) >>= aOrigStorMediaType;
+
+ SAL_WARN_IF( aOrigStorMediaType.isEmpty(), "comphelper.container", "No valuable media type in the storage!" );
+
+ uno::Reference< beans::XPropertySet > xTargetStorProps(
+ pImpl->mpTempObjectContainer->pImpl->mxStorage,
+ uno::UNO_QUERY_THROW );
+ xTargetStorProps->setPropertyValue( s_sMediaType,uno::Any( aOrigStorMediaType ) );
+ }
+ catch (const uno::Exception&)
+ {
+ SAL_WARN( "comphelper.container", "Can not set the new media type to a storage!" );
+ }
+ }
+
+ OUString aTempName, aMediaType;
+ /* Do not create a new name for a removed object, in the pImpl->mpTempObjectContainer,
+ because the original m_aEntryName of xObj will be overwritten by InsertEmbeddedObject(),
+ so uno::Reference < embed::XEmbeddedObject >& xObj will misbehave in
+ EmbeddedObjectContainer::StoreAsChildren and SfxObjectShell::SaveCompletedChildren
+ and will throw an exception because of objects with the same names! */
+ if( !pImpl->mpTempObjectContainer->HasEmbeddedObject(aName) )
+ aTempName = aName;
+
+ pImpl->mpTempObjectContainer->InsertEmbeddedObject( xObj, aTempName );
+
+ uno::Reference < io::XInputStream > xStream = GetGraphicStream( xObj, &aMediaType );
+ if ( xStream.is() )
+ pImpl->mpTempObjectContainer->InsertGraphicStream( xStream, aTempName, aMediaType );
+
+ // object is stored, so at least it can be set to loaded state
+ xObj->changeState( embed::EmbedStates::LOADED );
+ }
+ else
+ // objects without persistence need to stay in running state if they shall not be closed
+ xObj->changeState( embed::EmbedStates::RUNNING );
+ }
+ catch (const uno::Exception&)
+ {
+ return false;
+ }
+
+ auto aIter = std::find_if(pImpl->maNameToObjectMap.begin(), pImpl->maNameToObjectMap.end(),
+ [&xObj](const EmbeddedObjectContainerNameMap::value_type& rEntry) { return rEntry.second == xObj; });
+ if (aIter != pImpl->maNameToObjectMap.end())
+ {
+ pImpl->maObjectToNameMap.erase( aIter->second );
+ pImpl->maNameToObjectMap.erase( aIter );
+ uno::Reference < container::XChild > xChild( xObj, uno::UNO_QUERY );
+ if ( xChild.is() )
+ xChild->setParent( uno::Reference < uno::XInterface >() );
+ }
+ else
+ SAL_WARN( "comphelper.container", "Object not found for removal!" );
+
+ if ( !xPersist || !bKeepToTempStorage ) // #i119941#
+ return true;
+
+ // remove replacement image (if there is one)
+ RemoveGraphicStream( aName );
+
+ // now it's time to remove the storage from the container storage
+ try
+ {
+#if OSL_DEBUG_LEVEL > 1
+ // if the object has a persistence and the object is not a link than it must have persistence entry in storage
+ OSL_ENSURE( bIsNotEmbedded || pImpl->mxStorage->hasByName( aName ), "The object has no persistence entry in the storage!" );
+#endif
+ if ( xPersist.is() && pImpl->mxStorage->hasByName( aName ) )
+ pImpl->mxStorage->removeElement( aName );
+ }
+ catch (const uno::Exception&)
+ {
+ SAL_WARN( "comphelper.container", "Failed to remove object from storage!" );
+ return false;
+ }
+
+ return true;
+}
+
+void EmbeddedObjectContainer::CloseEmbeddedObject( const uno::Reference < embed::XEmbeddedObject >& xObj )
+{
+ // disconnect the object from the container and close it if possible
+
+ auto aIter = std::find_if(pImpl->maNameToObjectMap.begin(), pImpl->maNameToObjectMap.end(),
+ [&xObj](const EmbeddedObjectContainerNameMap::value_type& rEntry) { return rEntry.second == xObj; });
+ if (aIter == pImpl->maNameToObjectMap.end())
+ return;
+
+ pImpl->maObjectToNameMap.erase( aIter->second );
+ pImpl->maNameToObjectMap.erase( aIter );
+
+ try
+ {
+ xObj->close( true );
+ }
+ catch (const uno::Exception&)
+ {
+ // it is no problem if the object is already closed
+ // TODO/LATER: what if the object can not be closed?
+ }
+}
+
+uno::Reference < io::XInputStream > EmbeddedObjectContainer::GetGraphicStream( const OUString& aName, OUString* pMediaType )
+{
+ uno::Reference < io::XInputStream > xStream;
+
+ SAL_WARN_IF( aName.isEmpty(), "comphelper.container", "Retrieving graphic for unknown object!" );
+ if ( !aName.isEmpty() )
+ {
+ try
+ {
+ uno::Reference < embed::XStorage > xReplacements = pImpl->GetReplacements();
+ uno::Reference < io::XStream > xGraphicStream = xReplacements->openStreamElement( aName, embed::ElementModes::READ );
+ xStream = xGraphicStream->getInputStream();
+ if ( pMediaType )
+ {
+ uno::Reference < beans::XPropertySet > xSet( xStream, uno::UNO_QUERY );
+ if ( xSet.is() )
+ {
+ uno::Any aAny = xSet->getPropertyValue("MediaType");
+ aAny >>= *pMediaType;
+ }
+ }
+ }
+ catch (uno::Exception const& e)
+ {
+ SAL_INFO("comphelper.container",
+ "EmbeddedObjectContainer::GetGraphicStream(): " << e);
+ }
+ }
+
+ return xStream;
+}
+
+uno::Reference < io::XInputStream > EmbeddedObjectContainer::GetGraphicStream( const css::uno::Reference < css::embed::XEmbeddedObject >& xObj, OUString* pMediaType )
+{
+ // try to load it from the container storage
+ return GetGraphicStream( GetEmbeddedObjectName( xObj ), pMediaType );
+}
+
+bool EmbeddedObjectContainer::InsertGraphicStream( const css::uno::Reference < css::io::XInputStream >& rStream, const OUString& rObjectName, const OUString& rMediaType )
+{
+ try
+ {
+ uno::Reference < embed::XStorage > xReplacements = pImpl->GetReplacements();
+
+ // store it into the subfolder
+ uno::Reference < io::XOutputStream > xOutStream;
+ uno::Reference < io::XStream > xGraphicStream = xReplacements->openStreamElement( rObjectName,
+ embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE );
+ xOutStream = xGraphicStream->getOutputStream();
+ ::comphelper::OStorageHelper::CopyInputToOutput( rStream, xOutStream );
+ xOutStream->flush();
+
+ uno::Reference< beans::XPropertySet > xPropSet( xGraphicStream, uno::UNO_QUERY_THROW );
+
+ xPropSet->setPropertyValue("UseCommonStoragePasswordEncryption",
+ uno::Any( true ) );
+ xPropSet->setPropertyValue("MediaType", uno::Any(rMediaType) );
+
+ xPropSet->setPropertyValue("Compressed",
+ uno::Any( true ) );
+ }
+ catch (const uno::Exception&)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool EmbeddedObjectContainer::InsertGraphicStreamDirectly( const css::uno::Reference < css::io::XInputStream >& rStream, const OUString& rObjectName, const OUString& rMediaType )
+{
+ try
+ {
+ uno::Reference < embed::XStorage > xReplacement = pImpl->GetReplacements();
+ uno::Reference < embed::XOptimizedStorage > xOptRepl( xReplacement, uno::UNO_QUERY_THROW );
+
+ // store it into the subfolder
+ uno::Sequence< beans::PropertyValue > aProps{
+ comphelper::makePropertyValue("MediaType", rMediaType),
+ comphelper::makePropertyValue("UseCommonStoragePasswordEncryption", true),
+ comphelper::makePropertyValue("Compressed", true)
+ };
+
+ if ( xReplacement->hasByName( rObjectName ) )
+ xReplacement->removeElement( rObjectName );
+
+ xOptRepl->insertStreamElementDirect( rObjectName, rStream, aProps );
+ }
+ catch (const uno::Exception&)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+
+void EmbeddedObjectContainer::RemoveGraphicStream( const OUString& rObjectName )
+{
+ try
+ {
+ uno::Reference < embed::XStorage > xReplacements = pImpl->GetReplacements();
+ xReplacements->removeElement( rObjectName );
+ }
+ catch (const uno::Exception&)
+ {
+ }
+}
+namespace {
+ void InsertStreamIntoPicturesStorage_Impl( const uno::Reference< embed::XStorage >& xDocStor,
+ const uno::Reference< io::XInputStream >& xInStream,
+ const OUString& aStreamName )
+ {
+ OSL_ENSURE( !aStreamName.isEmpty() && xInStream.is() && xDocStor.is(), "Misuse of the method!" );
+
+ try
+ {
+ uno::Reference< embed::XStorage > xPictures = xDocStor->openStorageElement(
+ "Pictures",
+ embed::ElementModes::READWRITE );
+ uno::Reference< io::XStream > xObjReplStr = xPictures->openStreamElement(
+ aStreamName,
+ embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE );
+ uno::Reference< io::XOutputStream > xOutStream(
+ xObjReplStr->getInputStream(), uno::UNO_QUERY_THROW );
+
+ ::comphelper::OStorageHelper::CopyInputToOutput( xInStream, xOutStream );
+ xOutStream->closeOutput();
+
+ uno::Reference< embed::XTransactedObject > xTransact( xPictures, uno::UNO_QUERY );
+ if ( xTransact.is() )
+ xTransact->commit();
+ }
+ catch (const uno::Exception&)
+ {
+ SAL_WARN( "comphelper.container", "The images storage is not available!" );
+ }
+ }
+
+}
+
+bool EmbeddedObjectContainer::StoreAsChildren(bool _bOasisFormat,bool _bCreateEmbedded, bool _bAutoSaveEvent,
+ const uno::Reference < embed::XStorage >& _xStorage)
+{
+ bool bResult = false;
+ try
+ {
+ comphelper::EmbeddedObjectContainer aCnt( _xStorage );
+ const uno::Sequence < OUString > aNames = GetObjectNames();
+ const OUString* pIter = aNames.getConstArray();
+ const OUString* pEnd = pIter + aNames.getLength();
+ for(;pIter != pEnd;++pIter)
+ {
+ uno::Reference < embed::XEmbeddedObject > xObj = GetEmbeddedObject( *pIter );
+ SAL_WARN_IF( !xObj.is(), "comphelper.container", "An empty entry in the embedded objects list!" );
+ if ( xObj.is() )
+ {
+ bool bSwitchBackToLoaded = false;
+ uno::Reference< embed::XLinkageSupport > xLink( xObj, uno::UNO_QUERY );
+
+ uno::Reference < io::XInputStream > xStream;
+ OUString aMediaType;
+
+ sal_Int32 nCurState = xObj->getCurrentState();
+ if ( nCurState == embed::EmbedStates::LOADED || nCurState == embed::EmbedStates::RUNNING )
+ {
+ // means that the object is not active
+ // copy replacement image from old to new container
+ xStream = GetGraphicStream( xObj, &aMediaType );
+ }
+
+ if ( !xStream.is() && getUserAllowsLinkUpdate() )
+ {
+ // the image must be regenerated
+ // TODO/LATER: another aspect could be used
+ if ( xObj->getCurrentState() == embed::EmbedStates::LOADED )
+ bSwitchBackToLoaded = true;
+
+ xStream = GetGraphicReplacementStream(
+ embed::Aspects::MSOLE_CONTENT,
+ xObj,
+ &aMediaType );
+ }
+
+ if ( _bOasisFormat || (xLink.is() && xLink->isLink()) )
+ {
+ if ( xStream.is() )
+ {
+ if ( _bOasisFormat )
+ {
+ // if it is an embedded object or the optimized inserting fails the normal inserting should be done
+ if ( _bCreateEmbedded
+ || !aCnt.InsertGraphicStreamDirectly( xStream, *pIter, aMediaType ) )
+ aCnt.InsertGraphicStream( xStream, *pIter, aMediaType );
+ }
+ else
+ {
+ // it is a linked object exported into SO7 format
+ InsertStreamIntoPicturesStorage_Impl( _xStorage, xStream, *pIter );
+ }
+ }
+ }
+
+ uno::Reference< embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
+ if ( xPersist.is() )
+ {
+ uno::Sequence< beans::PropertyValue > aArgs( _bOasisFormat ? 3 : 4 );
+ auto pArgs = aArgs.getArray();
+ pArgs[0].Name = "StoreVisualReplacement";
+ pArgs[0].Value <<= !_bOasisFormat;
+
+ // if it is an embedded object or the optimized inserting fails the normal inserting should be done
+ pArgs[1].Name = "CanTryOptimization";
+ pArgs[1].Value <<= !_bCreateEmbedded;
+
+ pArgs[2].Name = "AutoSaveEvent";
+ pArgs[2].Value <<= _bAutoSaveEvent;
+
+ if ( !_bOasisFormat )
+ {
+ // if object has no cached replacement it will use this one
+ pArgs[3].Name = "VisualReplacement";
+ pArgs[3].Value <<= xStream;
+ }
+
+ try
+ {
+ xPersist->storeAsEntry( _xStorage, xPersist->getEntryName(), uno::Sequence< beans::PropertyValue >(), aArgs );
+ }
+ catch (const embed::WrongStateException&)
+ {
+ SAL_WARN("comphelper.container", "failed to store '" << *pIter << "'");
+ }
+ }
+
+ if ( bSwitchBackToLoaded )
+ // switch back to loaded state; that way we have a minimum cache confusion
+ xObj->changeState( embed::EmbedStates::LOADED );
+ }
+ }
+
+ bResult = aCnt.CommitImageSubStorage();
+
+ }
+ catch (const uno::Exception& e)
+ {
+ // TODO/LATER: error handling
+ bResult = false;
+ SAL_WARN("comphelper.container", "failed. Message: " << e);
+ }
+
+ // the old SO6 format does not store graphical replacements
+ if ( !_bOasisFormat && bResult )
+ {
+ try
+ {
+ // the substorage still can not be locked by the embedded object container
+ OUString aObjReplElement( "ObjectReplacements" );
+ if ( _xStorage->hasByName( aObjReplElement ) && _xStorage->isStorageElement( aObjReplElement ) )
+ _xStorage->removeElement( aObjReplElement );
+ }
+ catch (const uno::Exception&)
+ {
+ // TODO/LATER: error handling;
+ bResult = false;
+ }
+ }
+ return bResult;
+}
+
+bool EmbeddedObjectContainer::StoreChildren(bool _bOasisFormat,bool _bObjectsOnly)
+{
+ bool bResult = true;
+ const uno::Sequence < OUString > aNames = GetObjectNames();
+ const OUString* pIter = aNames.getConstArray();
+ const OUString* pEnd = pIter + aNames.getLength();
+ for(;pIter != pEnd;++pIter)
+ {
+ try
+ {
+ uno::Reference < embed::XEmbeddedObject > xObj = GetEmbeddedObject( *pIter );
+ SAL_WARN_IF( !xObj.is(), "comphelper.container", "An empty entry in the embedded objects list!" );
+ if ( xObj.is() )
+ {
+ sal_Int32 nCurState = xObj->getCurrentState();
+ if ( _bOasisFormat && nCurState != embed::EmbedStates::LOADED && nCurState != embed::EmbedStates::RUNNING )
+ {
+ // means that the object is active
+ // the image must be regenerated
+ OUString aMediaType;
+
+ // TODO/LATER: another aspect could be used
+ uno::Reference < io::XInputStream > xStream =
+ GetGraphicReplacementStream(
+ embed::Aspects::MSOLE_CONTENT,
+ xObj,
+ &aMediaType );
+ if ( xStream.is() )
+ {
+ if ( !InsertGraphicStreamDirectly( xStream, *pIter, aMediaType ) )
+ InsertGraphicStream( xStream, *pIter, aMediaType );
+ }
+ }
+
+ // TODO/LATER: currently the object by default does not cache replacement image
+ // that means that if somebody loads SO7 document and store its objects using
+ // this method the images might be lost.
+ // Currently this method is only used on storing to alien formats, that means
+ // that SO7 documents storing does not use it, and all other filters are
+ // based on OASIS format. But if it changes the method must be fixed. The fix
+ // must be done only on demand since it can affect performance.
+
+ uno::Reference< embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
+ if ( xPersist.is() )
+ {
+ try
+ {
+ //TODO/LATER: only storing if changed!
+ //xPersist->storeOwn(); //commented, i120168
+
+ // begin:all charts will be persisted as xml format on disk when saving, which is time consuming.
+ // '_bObjectsOnly' mean we are storing to alien formats.
+ // 'isStorageElement' mean current object is NOT a MS OLE format. (may also include in future), i120168
+ if (_bObjectsOnly && (nCurState == embed::EmbedStates::LOADED || nCurState == embed::EmbedStates::RUNNING)
+ && (pImpl->mxStorage->isStorageElement( *pIter ) ))
+ {
+ uno::Reference< util::XModifiable > xModifiable( xObj->getComponent(), uno::UNO_QUERY );
+ if ( xModifiable.is() && xModifiable->isModified())
+ {
+ xPersist->storeOwn();
+ }
+ else
+ {
+ //do nothing. Embedded model is not modified, no need to persist.
+ }
+ }
+ else //the embedded object is in active status, always store back it.
+ {
+ xPersist->storeOwn();
+ }
+ //end i120168
+ }
+ catch (const uno::Exception&)
+ {
+ // TODO/LATER: error handling
+ bResult = false;
+ break;
+ }
+ }
+
+ if ( !_bOasisFormat && !_bObjectsOnly )
+ {
+ // copy replacement images for linked objects
+ try
+ {
+ uno::Reference< embed::XLinkageSupport > xLink( xObj, uno::UNO_QUERY );
+ if ( xLink.is() && xLink->isLink() )
+ {
+ OUString aMediaType;
+ uno::Reference < io::XInputStream > xInStream = GetGraphicStream( xObj, &aMediaType );
+ if ( xInStream.is() )
+ InsertStreamIntoPicturesStorage_Impl( pImpl->mxStorage, xInStream, *pIter );
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ }
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ // TODO/LATER: error handling
+ }
+ }
+
+ if ( bResult && _bOasisFormat )
+ bResult = CommitImageSubStorage();
+
+ if ( bResult && !_bObjectsOnly )
+ {
+ try
+ {
+ ReleaseImageSubStorage();
+ OUString aObjReplElement( "ObjectReplacements" );
+ if ( !_bOasisFormat && pImpl->mxStorage->hasByName( aObjReplElement ) && pImpl->mxStorage->isStorageElement( aObjReplElement ) )
+ pImpl->mxStorage->removeElement( aObjReplElement );
+ }
+ catch (const uno::Exception&)
+ {
+ // TODO/LATER: error handling
+ bResult = false;
+ }
+ }
+ return bResult;
+}
+
+uno::Reference< io::XInputStream > EmbeddedObjectContainer::GetGraphicReplacementStream(
+ sal_Int64 nViewAspect,
+ const uno::Reference< embed::XEmbeddedObject >& xObj,
+ OUString* pMediaType )
+{
+ uno::Reference< io::XInputStream > xInStream;
+ if ( xObj.is() )
+ {
+ try
+ {
+ // retrieving of the visual representation can switch object to running state
+ embed::VisualRepresentation aRep = xObj->getPreferredVisualRepresentation( nViewAspect );
+ if ( pMediaType )
+ *pMediaType = aRep.Flavor.MimeType;
+
+ uno::Sequence < sal_Int8 > aSeq;
+ aRep.Data >>= aSeq;
+ xInStream = new ::comphelper::SequenceInputStream( aSeq );
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ }
+
+ return xInStream;
+}
+
+bool EmbeddedObjectContainer::SetPersistentEntries(const uno::Reference< embed::XStorage >& _xStorage,bool _bClearModifiedFlag)
+{
+ bool bError = false;
+ const uno::Sequence < OUString > aNames = GetObjectNames();
+ const OUString* pIter = aNames.getConstArray();
+ const OUString* pEnd = pIter + aNames.getLength();
+ for(;pIter != pEnd;++pIter)
+ {
+ uno::Reference < embed::XEmbeddedObject > xObj = GetEmbeddedObject( *pIter );
+ SAL_WARN_IF( !xObj.is(), "comphelper.container", "An empty entry in the embedded objects list!" );
+ if ( xObj.is() )
+ {
+ uno::Reference< embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY );
+ if ( xPersist.is() )
+ {
+ try
+ {
+ xPersist->setPersistentEntry( _xStorage,
+ *pIter,
+ embed::EntryInitModes::NO_INIT,
+ uno::Sequence< beans::PropertyValue >(),
+ uno::Sequence< beans::PropertyValue >() );
+
+ }
+ catch (const uno::Exception&)
+ {
+ // TODO/LATER: error handling
+ bError = true;
+ break;
+ }
+ }
+ if ( _bClearModifiedFlag )
+ {
+ // if this method is used as part of SaveCompleted the object must stay unmodified after execution
+ try
+ {
+ uno::Reference< util::XModifiable > xModif( xObj->getComponent(), uno::UNO_QUERY_THROW );
+ if ( xModif->isModified() )
+ xModif->setModified( false );
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ }
+ }
+ }
+ return bError;
+}
+
+bool EmbeddedObjectContainer::getUserAllowsLinkUpdate() const
+{
+ return pImpl->mbUserAllowsLinkUpdate;
+}
+
+void EmbeddedObjectContainer::setUserAllowsLinkUpdate(bool bNew)
+{
+ if(pImpl->mbUserAllowsLinkUpdate != bNew)
+ {
+ pImpl->mbUserAllowsLinkUpdate = bNew;
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/container/enumerablemap.cxx b/comphelper/source/container/enumerablemap.cxx
new file mode 100644
index 000000000..6ca7c36d2
--- /dev/null
+++ b/comphelper/source/container/enumerablemap.cxx
@@ -0,0 +1,713 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <comphelper/anytostring.hxx>
+#include <comphelper/anycompare.hxx>
+#include <comphelper/componentbase.hxx>
+
+#include <com/sun/star/container/XEnumerableMap.hpp>
+#include <com/sun/star/lang/NoSupportException.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/ucb/AlreadyInitializedException.hpp>
+#include <com/sun/star/beans/IllegalTypeException.hpp>
+#include <com/sun/star/beans/Pair.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include <cppuhelper/compbase3.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <typelib/typedescription.hxx>
+
+#include <cmath>
+#include <map>
+#include <memory>
+#include <optional>
+#include <utility>
+
+namespace comphelper
+{
+
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::uno::UNO_QUERY;
+ using ::com::sun::star::uno::RuntimeException;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::uno::Type;
+ using ::com::sun::star::container::XEnumerableMap;
+ using ::com::sun::star::lang::NoSupportException;
+ using ::com::sun::star::beans::IllegalTypeException;
+ using ::com::sun::star::container::NoSuchElementException;
+ using ::com::sun::star::lang::IllegalArgumentException;
+ using ::com::sun::star::lang::XInitialization;
+ using ::com::sun::star::ucb::AlreadyInitializedException;
+ using ::com::sun::star::beans::Pair;
+ using ::com::sun::star::uno::TypeClass;
+ using ::com::sun::star::uno::TypeClass_VOID;
+ using ::com::sun::star::uno::TypeClass_UNKNOWN;
+ using ::com::sun::star::uno::TypeClass_ANY;
+ using ::com::sun::star::uno::TypeClass_EXCEPTION;
+ using ::com::sun::star::uno::TypeClass_STRUCT;
+ using ::com::sun::star::uno::TypeClass_FLOAT;
+ using ::com::sun::star::uno::TypeClass_DOUBLE;
+ using ::com::sun::star::uno::TypeClass_INTERFACE;
+ using ::com::sun::star::lang::XServiceInfo;
+ using ::com::sun::star::uno::XComponentContext;
+ using ::com::sun::star::container::XEnumeration;
+ using ::com::sun::star::uno::TypeDescription;
+ using ::com::sun::star::lang::DisposedException;
+
+ namespace {
+
+ class MapEnumerator;
+
+ }
+
+ typedef std::map< Any, Any, LessPredicateAdapter > KeyedValues;
+
+ namespace {
+
+ struct MapData
+ {
+ Type m_aKeyType;
+ Type m_aValueType;
+ std::optional< KeyedValues > m_pValues;
+ std::shared_ptr< IKeyPredicateLess > m_pKeyCompare;
+ bool m_bMutable;
+ std::vector< MapEnumerator* > m_aModListeners;
+
+ MapData()
+ :m_bMutable( true )
+ {
+ }
+
+ MapData( const MapData& _source )
+ :m_aKeyType( _source.m_aKeyType )
+ ,m_aValueType( _source.m_aValueType )
+ ,m_pKeyCompare( _source.m_pKeyCompare )
+ ,m_bMutable( false )
+ {
+ m_pValues.emplace( *_source.m_pValues );
+ }
+ private:
+ MapData& operator=( const MapData& _source ) = delete;
+ };
+
+ }
+
+ static void lcl_registerMapModificationListener( MapData& _mapData, MapEnumerator& _listener )
+ {
+ #if OSL_DEBUG_LEVEL > 0
+ for ( const MapEnumerator* lookup : _mapData.m_aModListeners )
+ {
+ OSL_ENSURE( lookup != &_listener, "lcl_registerMapModificationListener: this listener is already registered!" );
+ }
+ #endif
+ _mapData.m_aModListeners.push_back( &_listener );
+ }
+
+
+ static void lcl_revokeMapModificationListener( MapData& _mapData, MapEnumerator& _listener )
+ {
+ auto lookup = std::find(_mapData.m_aModListeners.begin(), _mapData.m_aModListeners.end(), &_listener);
+ if (lookup != _mapData.m_aModListeners.end())
+ {
+ _mapData.m_aModListeners.erase( lookup );
+ return;
+ }
+ OSL_FAIL( "lcl_revokeMapModificationListener: the listener is not registered!" );
+ }
+
+
+ static void lcl_notifyMapDataListeners_nothrow( const MapData& _mapData );
+
+
+ // EnumerableMap
+
+ typedef ::cppu::WeakAggComponentImplHelper3 < XInitialization
+ , XEnumerableMap
+ , XServiceInfo
+ > Map_IFace;
+
+ namespace {
+
+ class EnumerableMap: public Map_IFace, public ComponentBase
+ {
+ public:
+ EnumerableMap();
+ protected:
+ virtual ~EnumerableMap() override;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const Sequence< Any >& aArguments ) override;
+
+ // XEnumerableMap
+ virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createKeyEnumeration( sal_Bool Isolated ) override;
+ virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createValueEnumeration( sal_Bool Isolated ) override;
+ virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createElementEnumeration( sal_Bool Isolated ) override;
+
+ // XMap
+ virtual Type SAL_CALL getKeyType() override;
+ virtual Type SAL_CALL getValueType() override;
+ virtual void SAL_CALL clear( ) override;
+ virtual sal_Bool SAL_CALL containsKey( const Any& _key ) override;
+ virtual sal_Bool SAL_CALL containsValue( const Any& _value ) override;
+ virtual Any SAL_CALL get( const Any& _key ) override;
+ virtual Any SAL_CALL put( const Any& _key, const Any& _value ) override;
+ virtual Any SAL_CALL remove( const Any& _key ) override;
+
+ // XElementAccess (base of XMap)
+ virtual Type SAL_CALL getElementType() override;
+ virtual sal_Bool SAL_CALL hasElements() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+ private:
+ void impl_initValues_throw( const Sequence< Pair< Any, Any > >& _initialValues );
+
+ /// throws an IllegalTypeException if the given value is not compatible with our ValueType
+ void impl_checkValue_throw( const Any& _value ) const;
+ void impl_checkKey_throw( const Any& _key ) const;
+ void impl_checkNaN_throw( const Any& _keyOrValue, const Type& _keyOrValueType ) const;
+ void impl_checkMutable_throw() const;
+
+ private:
+ ::osl::Mutex m_aMutex;
+ MapData m_aData;
+ };
+
+ enum EnumerationType
+ {
+ eKeys, eValues, eBoth
+ };
+
+ class MapEnumerator final
+ {
+ public:
+ MapEnumerator( ::cppu::OWeakObject& _rParent, MapData& _mapData, const EnumerationType _type )
+ :m_rParent( _rParent )
+ ,m_rMapData( _mapData )
+ ,m_eType( _type )
+ ,m_mapPos( _mapData.m_pValues->begin() )
+ ,m_disposed( false )
+ {
+ lcl_registerMapModificationListener( m_rMapData, *this );
+ }
+
+ ~MapEnumerator()
+ {
+ dispose();
+ }
+
+ void dispose()
+ {
+ if ( !m_disposed )
+ {
+ lcl_revokeMapModificationListener( m_rMapData, *this );
+ m_disposed = true;
+ }
+ }
+
+ // noncopyable
+ MapEnumerator(const MapEnumerator&) = delete;
+ const MapEnumerator& operator=(const MapEnumerator&) = delete;
+
+ // XEnumeration equivalents
+ bool hasMoreElements();
+ Any nextElement();
+
+ /// called when the map was modified
+ void mapModified();
+
+ private:
+ ::cppu::OWeakObject& m_rParent;
+ MapData& m_rMapData;
+ const EnumerationType m_eType;
+ KeyedValues::const_iterator m_mapPos;
+ bool m_disposed;
+ };
+
+ }
+
+ static void lcl_notifyMapDataListeners_nothrow( const MapData& _mapData )
+ {
+ for ( MapEnumerator* loop : _mapData.m_aModListeners )
+ {
+ loop->mapModified();
+ }
+ }
+
+ typedef ::cppu::WeakImplHelper < XEnumeration
+ > MapEnumeration_Base;
+
+ namespace {
+
+ class MapEnumeration :public ComponentBase
+ ,public MapEnumeration_Base
+ {
+ public:
+ MapEnumeration( ::cppu::OWeakObject& _parentMap, MapData& _mapData, ::cppu::OBroadcastHelper& _rBHelper,
+ const EnumerationType _type, const bool _isolated )
+ :ComponentBase( _rBHelper, ComponentBase::NoInitializationNeeded() )
+ ,m_xKeepMapAlive( _parentMap )
+ ,m_pMapDataCopy( _isolated ? new MapData( _mapData ) : nullptr )
+ ,m_aEnumerator( *this, _isolated ? *m_pMapDataCopy : _mapData, _type )
+ {
+ }
+
+ // XEnumeration
+ virtual sal_Bool SAL_CALL hasMoreElements( ) override;
+ virtual Any SAL_CALL nextElement( ) override;
+
+ protected:
+ virtual ~MapEnumeration() override
+ {
+ acquire();
+ {
+ ::osl::MutexGuard aGuard( getMutex() );
+ m_aEnumerator.dispose();
+ m_pMapDataCopy.reset();
+ }
+ }
+
+ private:
+ // since we share our mutex with the main map, we need to keep it alive as long as we live
+ Reference< XInterface > m_xKeepMapAlive;
+ std::unique_ptr< MapData > m_pMapDataCopy;
+ MapEnumerator m_aEnumerator;
+ };
+
+ }
+
+ EnumerableMap::EnumerableMap()
+ :Map_IFace( m_aMutex )
+ ,ComponentBase( Map_IFace::rBHelper )
+ {
+ }
+
+
+ EnumerableMap::~EnumerableMap()
+ {
+ if ( !impl_isDisposed() )
+ {
+ acquire();
+ dispose();
+ }
+ }
+
+
+ void SAL_CALL EnumerableMap::initialize( const Sequence< Any >& _arguments )
+ {
+ ComponentMethodGuard aGuard( *this, ComponentMethodGuard::MethodType::WithoutInit );
+ if ( impl_isInitialized_nothrow() )
+ throw AlreadyInitializedException();
+
+ sal_Int32 nArgumentCount = _arguments.getLength();
+ if ( ( nArgumentCount != 2 ) && ( nArgumentCount != 3 ) )
+ throw IllegalArgumentException("wrong number of args", static_cast<cppu::OWeakObject*>(this), 1);
+
+ Type aKeyType, aValueType;
+ if ( !( _arguments[0] >>= aKeyType ) )
+ throw IllegalArgumentException("com.sun.star.uno.Type expected.", *this, 1 );
+ if ( !( _arguments[1] >>= aValueType ) )
+ throw IllegalArgumentException("com.sun.star.uno.Type expected.", *this, 2 );
+
+ Sequence< Pair< Any, Any > > aInitialValues;
+ bool bMutable = true;
+ if ( nArgumentCount == 3 )
+ {
+ if ( !( _arguments[2] >>= aInitialValues ) )
+ throw IllegalArgumentException("[]com.sun.star.beans.Pair<any,any> expected.", *this, 2 );
+ bMutable = false;
+ }
+
+ // for the value, anything is allowed, except VOID
+ if ( ( aValueType.getTypeClass() == TypeClass_VOID ) || ( aValueType.getTypeClass() == TypeClass_UNKNOWN ) )
+ throw IllegalTypeException("Unsupported value type.", *this );
+
+ // create the comparator for the KeyType, and throw if the type is not supported
+ std::unique_ptr< IKeyPredicateLess > pComparator( getStandardLessPredicate( aKeyType, nullptr ) );
+ if (!pComparator)
+ throw IllegalTypeException("Unsupported key type.", *this );
+
+ // init members
+ m_aData.m_aKeyType = aKeyType;
+ m_aData.m_aValueType = aValueType;
+ m_aData.m_pKeyCompare = std::move(pComparator);
+ m_aData.m_pValues.emplace( *m_aData.m_pKeyCompare );
+ m_aData.m_bMutable = bMutable;
+
+ if ( aInitialValues.hasElements() )
+ impl_initValues_throw( aInitialValues );
+
+ setInitialized();
+ }
+
+
+ void EnumerableMap::impl_initValues_throw( const Sequence< Pair< Any, Any > >& _initialValues )
+ {
+ OSL_PRECOND( m_aData.m_pValues && m_aData.m_pValues->empty(), "EnumerableMap::impl_initValues_throw: illegal call!" );
+ if (!m_aData.m_pValues || !m_aData.m_pValues->empty())
+ throw RuntimeException();
+
+ const Pair< Any, Any >* mapping = _initialValues.getConstArray();
+ const Pair< Any, Any >* mappingEnd = mapping + _initialValues.getLength();
+ for ( ; mapping != mappingEnd; ++mapping )
+ {
+ impl_checkValue_throw( mapping->Second );
+ (*m_aData.m_pValues)[ mapping->First ] = mapping->Second;
+ }
+ }
+
+
+ void EnumerableMap::impl_checkValue_throw( const Any& _value ) const
+ {
+ if ( !_value.hasValue() )
+ // nothing to do, NULL values are always allowed, regardless of the ValueType
+ return;
+
+ TypeClass eAllowedTypeClass = m_aData.m_aValueType.getTypeClass();
+ bool bValid = false;
+
+ switch ( eAllowedTypeClass )
+ {
+ default:
+ bValid = ( _value.getValueTypeClass() == eAllowedTypeClass );
+ break;
+ case TypeClass_ANY:
+ bValid = true;
+ break;
+ case TypeClass_INTERFACE:
+ {
+ // special treatment: _value might contain the proper type, but the interface
+ // might actually be NULL. Which is still valid ...
+ if ( m_aData.m_aValueType.isAssignableFrom( _value.getValueType() ) )
+ // this also catches the special case where XFoo is our value type,
+ // and _value contains a NULL-reference to XFoo, or a derived type
+ bValid = true;
+ else
+ {
+ Reference< XInterface > xValue( _value, UNO_QUERY );
+ if ( xValue.is() )
+ // XInterface is not-NULL, but is X(ValueType) not-NULL, too?
+ xValue.set( xValue->queryInterface( m_aData.m_aValueType ), UNO_QUERY );
+ bValid = xValue.is();
+ }
+ }
+ break;
+ case TypeClass_EXCEPTION:
+ case TypeClass_STRUCT:
+ {
+ // values are accepted if and only if their type equals, or is derived from, our value type
+
+ if ( _value.getValueTypeClass() != eAllowedTypeClass )
+ bValid = false;
+ else
+ {
+ const TypeDescription aValueTypeDesc( _value.getValueType() );
+ const TypeDescription aRequiredTypeDesc( m_aData.m_aValueType );
+
+ const _typelib_CompoundTypeDescription* pValueCompoundTypeDesc =
+ reinterpret_cast< const _typelib_CompoundTypeDescription* >( aValueTypeDesc.get() );
+
+ while ( pValueCompoundTypeDesc )
+ {
+ if ( typelib_typedescription_equals( &pValueCompoundTypeDesc->aBase, aRequiredTypeDesc.get() ) )
+ break;
+ pValueCompoundTypeDesc = pValueCompoundTypeDesc->pBaseTypeDescription;
+ }
+ bValid = ( pValueCompoundTypeDesc != nullptr );
+ }
+ }
+ break;
+ }
+
+ if ( !bValid )
+ {
+ throw IllegalTypeException(
+ "Incompatible value type. Found '" + _value.getValueTypeName()
+ + "', where '" + m_aData.m_aValueType.getTypeName()
+ + "' (or compatible type) is expected.",
+ *const_cast< EnumerableMap* >( this ) );
+ }
+
+ impl_checkNaN_throw( _value, m_aData.m_aValueType );
+ }
+
+
+ void EnumerableMap::impl_checkNaN_throw( const Any& _keyOrValue, const Type& _keyOrValueType ) const
+ {
+ if ( ( _keyOrValueType.getTypeClass() == TypeClass_DOUBLE )
+ || ( _keyOrValueType.getTypeClass() == TypeClass_FLOAT )
+ )
+ {
+ double nValue(0);
+ if ( _keyOrValue >>= nValue )
+ if ( std::isnan( nValue ) )
+ throw IllegalArgumentException(
+ "NaN (not-a-number) not supported by this implementation.",
+ *const_cast< EnumerableMap* >( this ), 0 );
+ // (note that the case of _key not containing a float/double value is handled in the
+ // respective IKeyPredicateLess implementation, so there's no need to handle this here.)
+ }
+ }
+
+
+ void EnumerableMap::impl_checkKey_throw( const Any& _key ) const
+ {
+ if ( !_key.hasValue() )
+ throw IllegalArgumentException(
+ "NULL keys not supported by this implementation.",
+ *const_cast< EnumerableMap* >( this ), 0 );
+
+ impl_checkNaN_throw( _key, m_aData.m_aKeyType );
+ }
+
+
+ void EnumerableMap::impl_checkMutable_throw() const
+ {
+ if ( !m_aData.m_bMutable )
+ throw NoSupportException(
+ "The map is immutable.",
+ *const_cast< EnumerableMap* >( this ) );
+ }
+
+
+ Reference< XEnumeration > SAL_CALL EnumerableMap::createKeyEnumeration( sal_Bool Isolated )
+ {
+ ComponentMethodGuard aGuard( *this );
+ return new MapEnumeration( *this, m_aData, getBroadcastHelper(), eKeys, Isolated );
+ }
+
+
+ Reference< XEnumeration > SAL_CALL EnumerableMap::createValueEnumeration( sal_Bool Isolated )
+ {
+ ComponentMethodGuard aGuard( *this );
+ return new MapEnumeration( *this, m_aData, getBroadcastHelper(), eValues, Isolated );
+ }
+
+
+ Reference< XEnumeration > SAL_CALL EnumerableMap::createElementEnumeration( sal_Bool Isolated )
+ {
+ ComponentMethodGuard aGuard( *this );
+ return new MapEnumeration( *this, m_aData, getBroadcastHelper(), eBoth, Isolated );
+ }
+
+
+ Type SAL_CALL EnumerableMap::getKeyType()
+ {
+ ComponentMethodGuard aGuard( *this );
+ return m_aData.m_aKeyType;
+ }
+
+
+ Type SAL_CALL EnumerableMap::getValueType()
+ {
+ ComponentMethodGuard aGuard( *this );
+ return m_aData.m_aValueType;
+ }
+
+
+ void SAL_CALL EnumerableMap::clear( )
+ {
+ ComponentMethodGuard aGuard( *this );
+ impl_checkMutable_throw();
+
+ m_aData.m_pValues->clear();
+
+ lcl_notifyMapDataListeners_nothrow( m_aData );
+ }
+
+
+ sal_Bool SAL_CALL EnumerableMap::containsKey( const Any& _key )
+ {
+ ComponentMethodGuard aGuard( *this );
+ impl_checkKey_throw( _key );
+
+ KeyedValues::const_iterator pos = m_aData.m_pValues->find( _key );
+ return ( pos != m_aData.m_pValues->end() );
+ }
+
+
+ sal_Bool SAL_CALL EnumerableMap::containsValue( const Any& _value )
+ {
+ ComponentMethodGuard aGuard( *this );
+ impl_checkValue_throw( _value );
+ for (auto const& value : *m_aData.m_pValues)
+ {
+ if ( value.second == _value )
+ return true;
+ }
+ return false;
+ }
+
+
+ Any SAL_CALL EnumerableMap::get( const Any& _key )
+ {
+ ComponentMethodGuard aGuard( *this );
+ impl_checkKey_throw( _key );
+
+ KeyedValues::const_iterator pos = m_aData.m_pValues->find( _key );
+ if ( pos == m_aData.m_pValues->end() )
+ throw NoSuchElementException( anyToString( _key ), *this );
+
+ return pos->second;
+ }
+
+
+ Any SAL_CALL EnumerableMap::put( const Any& _key, const Any& _value )
+ {
+ ComponentMethodGuard aGuard( *this );
+ impl_checkMutable_throw();
+ impl_checkKey_throw( _key );
+ impl_checkValue_throw( _value );
+
+ Any previousValue;
+
+ KeyedValues::iterator pos = m_aData.m_pValues->find( _key );
+ if ( pos != m_aData.m_pValues->end() )
+ {
+ previousValue = pos->second;
+ pos->second = _value;
+ }
+ else
+ {
+ (*m_aData.m_pValues)[ _key ] = _value;
+ }
+
+ lcl_notifyMapDataListeners_nothrow( m_aData );
+
+ return previousValue;
+ }
+
+
+ Any SAL_CALL EnumerableMap::remove( const Any& _key )
+ {
+ ComponentMethodGuard aGuard( *this );
+ impl_checkMutable_throw();
+ impl_checkKey_throw( _key );
+
+ Any previousValue;
+
+ KeyedValues::iterator pos = m_aData.m_pValues->find( _key );
+ if ( pos != m_aData.m_pValues->end() )
+ {
+ previousValue = pos->second;
+ m_aData.m_pValues->erase( pos );
+ }
+
+ lcl_notifyMapDataListeners_nothrow( m_aData );
+
+ return previousValue;
+ }
+
+
+ Type SAL_CALL EnumerableMap::getElementType()
+ {
+ return ::cppu::UnoType< Pair< Any, Any > >::get();
+ }
+
+
+ sal_Bool SAL_CALL EnumerableMap::hasElements()
+ {
+ ComponentMethodGuard aGuard( *this );
+ return m_aData.m_pValues->empty();
+ }
+
+
+ OUString SAL_CALL EnumerableMap::getImplementationName( )
+ {
+ return "org.openoffice.comp.comphelper.EnumerableMap";
+ }
+
+ sal_Bool SAL_CALL EnumerableMap::supportsService( const OUString& _serviceName )
+ {
+ return cppu::supportsService(this, _serviceName);
+ }
+
+
+ Sequence< OUString > SAL_CALL EnumerableMap::getSupportedServiceNames( )
+ {
+ return { "com.sun.star.container.EnumerableMap" };
+ }
+
+ bool MapEnumerator::hasMoreElements()
+ {
+ if ( m_disposed )
+ throw DisposedException( OUString(), m_rParent );
+ return m_mapPos != m_rMapData.m_pValues->end();
+ }
+
+
+ Any MapEnumerator::nextElement()
+ {
+ if ( m_disposed )
+ throw DisposedException( OUString(), m_rParent );
+ if ( m_mapPos == m_rMapData.m_pValues->end() )
+ throw NoSuchElementException("No more elements.", m_rParent );
+
+ Any aNextElement;
+ switch ( m_eType )
+ {
+ case eKeys: aNextElement = m_mapPos->first; break;
+ case eValues: aNextElement = m_mapPos->second; break;
+ case eBoth: aNextElement <<= Pair< Any, Any >( m_mapPos->first, m_mapPos->second ); break;
+ }
+ ++m_mapPos;
+ return aNextElement;
+ }
+
+
+ void MapEnumerator::mapModified()
+ {
+ m_disposed = true;
+ }
+
+
+ sal_Bool SAL_CALL MapEnumeration::hasMoreElements( )
+ {
+ ComponentMethodGuard aGuard( *this );
+ return m_aEnumerator.hasMoreElements();
+ }
+
+
+ Any SAL_CALL MapEnumeration::nextElement( )
+ {
+ ComponentMethodGuard aGuard( *this );
+ return m_aEnumerator.nextElement();
+ }
+
+
+} // namespace comphelper
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+org_openoffice_comp_comphelper_EnumerableMap(
+ css::uno::XComponentContext*, css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new comphelper::EnumerableMap());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/container/enumhelper.cxx b/comphelper/source/container/enumhelper.cxx
new file mode 100644
index 000000000..bc9ae43e4
--- /dev/null
+++ b/comphelper/source/container/enumhelper.cxx
@@ -0,0 +1,280 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <comphelper/enumhelper.hxx>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <utility>
+
+namespace comphelper
+{
+
+OEnumerationByName::OEnumerationByName(css::uno::Reference<css::container::XNameAccess> _xAccess)
+ :m_aNames(_xAccess->getElementNames())
+ ,m_xAccess(_xAccess)
+ ,m_nPos(0)
+ ,m_bListening(false)
+{
+ impl_startDisposeListening();
+}
+
+
+OEnumerationByName::OEnumerationByName(const css::uno::Reference<css::container::XNameAccess> _xAccess,
+ std::vector<OUString> _aNames )
+ :m_aNames(std::move(_aNames))
+ ,m_xAccess(std::move(_xAccess))
+ ,m_nPos(0)
+ ,m_bListening(false)
+{
+ impl_startDisposeListening();
+}
+
+OEnumerationByName::~OEnumerationByName()
+{
+ std::lock_guard aLock(m_aLock);
+
+ impl_stopDisposeListening();
+}
+
+
+sal_Bool SAL_CALL OEnumerationByName::hasMoreElements( )
+{
+ std::lock_guard aLock(m_aLock);
+
+ if (m_xAccess.is() && getLength() > m_nPos)
+ return true;
+
+ if (m_xAccess.is())
+ {
+ impl_stopDisposeListening();
+ m_xAccess.clear();
+ }
+
+ return false;
+}
+
+
+css::uno::Any SAL_CALL OEnumerationByName::nextElement( )
+{
+ std::lock_guard aLock(m_aLock);
+
+ css::uno::Any aRes;
+ if (m_xAccess.is() && m_nPos < getLength())
+ aRes = m_xAccess->getByName(getElement(m_nPos++));
+
+ if (m_xAccess.is() && m_nPos >= getLength())
+ {
+ impl_stopDisposeListening();
+ m_xAccess.clear();
+ }
+
+ if (!aRes.hasValue()) //There are no more elements
+ throw css::container::NoSuchElementException();
+
+ return aRes;
+}
+
+void SAL_CALL OEnumerationByName::disposing(const css::lang::EventObject& aEvent)
+{
+ std::lock_guard aLock(m_aLock);
+
+ if (aEvent.Source == m_xAccess)
+ m_xAccess.clear();
+}
+
+
+void OEnumerationByName::impl_startDisposeListening()
+{
+ if (m_bListening)
+ return;
+
+ osl_atomic_increment(&m_refCount);
+ css::uno::Reference< css::lang::XComponent > xDisposable(m_xAccess, css::uno::UNO_QUERY);
+ if (xDisposable.is())
+ {
+ xDisposable->addEventListener(this);
+ m_bListening = true;
+ }
+ osl_atomic_decrement(&m_refCount);
+}
+
+
+void OEnumerationByName::impl_stopDisposeListening()
+{
+ if (!m_bListening)
+ return;
+
+ osl_atomic_increment(&m_refCount);
+ css::uno::Reference< css::lang::XComponent > xDisposable(m_xAccess, css::uno::UNO_QUERY);
+ if (xDisposable.is())
+ {
+ xDisposable->removeEventListener(this);
+ m_bListening = false;
+ }
+ osl_atomic_decrement(&m_refCount);
+}
+
+sal_Int32 OEnumerationByName::getLength() const
+{
+ if (m_aNames.index() == 0)
+ return std::get<css::uno::Sequence<OUString>>(m_aNames).getLength();
+ else
+ return std::get<std::vector<OUString>>(m_aNames).size();
+}
+
+const OUString& OEnumerationByName::getElement(sal_Int32 nIndex) const
+{
+ if (m_aNames.index() == 0)
+ return std::get<css::uno::Sequence<OUString>>(m_aNames).getConstArray()[nIndex];
+ else
+ return std::get<std::vector<OUString>>(m_aNames)[nIndex];
+}
+
+
+OEnumerationByIndex::OEnumerationByIndex(css::uno::Reference< css::container::XIndexAccess > _xAccess)
+ :m_xAccess(std::move(_xAccess))
+ ,m_nPos(0)
+ ,m_bListening(false)
+{
+ impl_startDisposeListening();
+}
+
+
+OEnumerationByIndex::~OEnumerationByIndex()
+{
+ std::lock_guard aLock(m_aLock);
+
+ impl_stopDisposeListening();
+}
+
+
+sal_Bool SAL_CALL OEnumerationByIndex::hasMoreElements( )
+{
+ std::lock_guard aLock(m_aLock);
+
+ if (m_xAccess.is() && m_xAccess->getCount() > m_nPos)
+ return true;
+
+ if (m_xAccess.is())
+ {
+ impl_stopDisposeListening();
+ m_xAccess.clear();
+ }
+
+ return false;
+}
+
+
+css::uno::Any SAL_CALL OEnumerationByIndex::nextElement( )
+{
+ std::lock_guard aLock(m_aLock);
+
+ css::uno::Any aRes;
+ if (m_xAccess.is() && m_nPos < m_xAccess->getCount())
+ aRes = m_xAccess->getByIndex(m_nPos++);
+
+ if (m_xAccess.is() && m_nPos >= m_xAccess->getCount())
+ {
+ impl_stopDisposeListening();
+ m_xAccess.clear();
+ }
+
+ if (!aRes.hasValue())
+ throw css::container::NoSuchElementException();
+ return aRes;
+}
+
+
+void SAL_CALL OEnumerationByIndex::disposing(const css::lang::EventObject& aEvent)
+{
+ std::lock_guard aLock(m_aLock);
+
+ if (aEvent.Source == m_xAccess)
+ m_xAccess.clear();
+}
+
+
+void OEnumerationByIndex::impl_startDisposeListening()
+{
+ if (m_bListening)
+ return;
+
+ osl_atomic_increment(&m_refCount);
+ css::uno::Reference< css::lang::XComponent > xDisposable(m_xAccess, css::uno::UNO_QUERY);
+ if (xDisposable.is())
+ {
+ xDisposable->addEventListener(this);
+ m_bListening = true;
+ }
+ osl_atomic_decrement(&m_refCount);
+}
+
+
+void OEnumerationByIndex::impl_stopDisposeListening()
+{
+ if (!m_bListening)
+ return;
+
+ osl_atomic_increment(&m_refCount);
+ css::uno::Reference< css::lang::XComponent > xDisposable(m_xAccess, css::uno::UNO_QUERY);
+ if (xDisposable.is())
+ {
+ xDisposable->removeEventListener(this);
+ m_bListening = false;
+ }
+ osl_atomic_decrement(&m_refCount);
+}
+
+OAnyEnumeration::OAnyEnumeration(const css::uno::Sequence< css::uno::Any >& lItems)
+ :m_nPos(0)
+ ,m_lItems(lItems)
+{
+}
+
+
+OAnyEnumeration::~OAnyEnumeration()
+{
+}
+
+
+sal_Bool SAL_CALL OAnyEnumeration::hasMoreElements( )
+{
+ std::lock_guard aLock(m_aLock);
+
+ return (m_lItems.getLength() > m_nPos);
+}
+
+
+css::uno::Any SAL_CALL OAnyEnumeration::nextElement( )
+{
+ if ( ! hasMoreElements())
+ throw css::container::NoSuchElementException();
+
+ std::lock_guard aLock(m_aLock);
+ sal_Int32 nPos = m_nPos;
+ ++m_nPos;
+ return m_lItems[nPos];
+}
+
+
+} // namespace comphelper
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/container/interfacecontainer2.cxx b/comphelper/source/container/interfacecontainer2.cxx
new file mode 100644
index 000000000..9acff0a7f
--- /dev/null
+++ b/comphelper/source/container/interfacecontainer2.cxx
@@ -0,0 +1,421 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <comphelper/interfacecontainer2.hxx>
+#include <comphelper/multicontainer2.hxx>
+
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <osl/mutex.hxx>
+
+#include <memory>
+
+#include <com/sun/star/lang/XEventListener.hpp>
+
+
+using namespace osl;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+
+namespace comphelper
+{
+
+OInterfaceIteratorHelper2::OInterfaceIteratorHelper2( OInterfaceContainerHelper2 & rCont_ )
+ : rCont( rCont_ )
+{
+ MutexGuard aGuard( rCont.rMutex );
+ if( rCont.bInUse )
+ // worst case, two iterators at the same time
+ rCont.copyAndResetInUse();
+ bIsList = rCont_.bIsList;
+ aData = rCont_.aData;
+ if( bIsList )
+ {
+ rCont.bInUse = true;
+ nRemain = aData.pAsVector->size();
+ }
+ else if( aData.pAsInterface )
+ {
+ aData.pAsInterface->acquire();
+ nRemain = 1;
+ }
+ else
+ nRemain = 0;
+}
+
+OInterfaceIteratorHelper2::~OInterfaceIteratorHelper2()
+{
+ bool bShared;
+ {
+ MutexGuard aGuard( rCont.rMutex );
+ // bResetInUse protect the iterator against recursion
+ bShared = aData.pAsVector == rCont.aData.pAsVector && rCont.bIsList;
+ if( bShared )
+ {
+ OSL_ENSURE( rCont.bInUse, "OInterfaceContainerHelper2 must be in use" );
+ rCont.bInUse = false;
+ }
+ }
+
+ if( !bShared )
+ {
+ if( bIsList )
+ // Sequence owned by the iterator
+ delete aData.pAsVector;
+ else if( aData.pAsInterface )
+ // Interface is acquired by the iterator
+ aData.pAsInterface->release();
+ }
+}
+
+XInterface * OInterfaceIteratorHelper2::next()
+{
+ if( nRemain )
+ {
+ nRemain--;
+ if( bIsList )
+ return (*aData.pAsVector)[nRemain].get();
+ else if( aData.pAsInterface )
+ return aData.pAsInterface;
+ }
+ // exception
+ return nullptr;
+}
+
+void OInterfaceIteratorHelper2::remove()
+{
+ if( bIsList )
+ {
+ OSL_ASSERT( nRemain >= 0 &&
+ o3tl::make_unsigned(nRemain) < aData.pAsVector->size() );
+ rCont.removeInterface( (*aData.pAsVector)[nRemain] );
+ }
+ else
+ {
+ OSL_ASSERT( 0 == nRemain );
+ rCont.removeInterface( aData.pAsInterface );
+ }
+}
+
+OInterfaceContainerHelper2::OInterfaceContainerHelper2( Mutex & rMutex_ )
+ : rMutex( rMutex_ )
+ , bInUse( false )
+ , bIsList( false )
+{
+}
+
+OInterfaceContainerHelper2::~OInterfaceContainerHelper2()
+{
+ OSL_ENSURE( !bInUse, "~OInterfaceContainerHelper2 but is in use" );
+ if( bIsList )
+ delete aData.pAsVector;
+ else if( aData.pAsInterface )
+ aData.pAsInterface->release();
+}
+
+sal_Int32 OInterfaceContainerHelper2::getLength() const
+{
+ MutexGuard aGuard( rMutex );
+ if( bIsList )
+ return aData.pAsVector->size();
+ else if( aData.pAsInterface )
+ return 1;
+ return 0;
+}
+
+std::vector< Reference<XInterface> > OInterfaceContainerHelper2::getElements() const
+{
+ std::vector< Reference<XInterface> > rVec;
+ MutexGuard aGuard( rMutex );
+ if( bIsList )
+ rVec = *aData.pAsVector;
+ else if( aData.pAsInterface )
+ {
+ rVec.emplace_back( aData.pAsInterface );
+ }
+ return rVec;
+}
+
+void OInterfaceContainerHelper2::copyAndResetInUse()
+{
+ OSL_ENSURE( bInUse, "OInterfaceContainerHelper2 not in use" );
+ if( bInUse )
+ {
+ // this should be the worst case. If an iterator is active
+ // and a new Listener is added.
+ if( bIsList )
+ aData.pAsVector = new std::vector< Reference< XInterface > >( *aData.pAsVector );
+ else if( aData.pAsInterface )
+ aData.pAsInterface->acquire();
+
+ bInUse = false;
+ }
+}
+
+sal_Int32 OInterfaceContainerHelper2::addInterface( const Reference<XInterface> & rListener )
+{
+ OSL_ASSERT( rListener.is() );
+ MutexGuard aGuard( rMutex );
+ if( bInUse )
+ copyAndResetInUse();
+
+ if( bIsList )
+ {
+ aData.pAsVector->push_back( rListener );
+ return aData.pAsVector->size();
+ }
+ else if( aData.pAsInterface )
+ {
+ std::vector< Reference< XInterface > > * pVec = new std::vector< Reference< XInterface > >( 2 );
+ (*pVec)[0] = aData.pAsInterface;
+ (*pVec)[1] = rListener;
+ aData.pAsInterface->release();
+ aData.pAsVector = pVec;
+ bIsList = true;
+ return 2;
+ }
+ else
+ {
+ aData.pAsInterface = rListener.get();
+ if( rListener.is() )
+ rListener->acquire();
+ return 1;
+ }
+}
+
+sal_Int32 OInterfaceContainerHelper2::removeInterface( const Reference<XInterface> & rListener )
+{
+ OSL_ASSERT( rListener.is() );
+ MutexGuard aGuard( rMutex );
+ if( bInUse )
+ copyAndResetInUse();
+
+ if( bIsList )
+ {
+ // It is not valid to compare the pointer directly, but it's faster.
+ auto it = std::find_if(aData.pAsVector->begin(), aData.pAsVector->end(),
+ [&rListener](const css::uno::Reference<css::uno::XInterface>& rItem) {
+ return rItem.get() == rListener.get(); });
+
+ // interface not found, use the correct compare method
+ if (it == aData.pAsVector->end())
+ it = std::find(aData.pAsVector->begin(), aData.pAsVector->end(), rListener);
+
+ if (it != aData.pAsVector->end())
+ aData.pAsVector->erase(it);
+
+ if( aData.pAsVector->size() == 1 )
+ {
+ XInterface * p = (*aData.pAsVector)[0].get();
+ p->acquire();
+ delete aData.pAsVector;
+ aData.pAsInterface = p;
+ bIsList = false;
+ return 1;
+ }
+ else
+ return aData.pAsVector->size();
+ }
+ else if( aData.pAsInterface && Reference<XInterface>( aData.pAsInterface ) == rListener )
+ {
+ aData.pAsInterface->release();
+ aData.pAsInterface = nullptr;
+ }
+ return aData.pAsInterface ? 1 : 0;
+}
+
+Reference<XInterface> OInterfaceContainerHelper2::getInterface( sal_Int32 nIndex ) const
+{
+ MutexGuard aGuard( rMutex );
+
+ if( bIsList )
+ return (*aData.pAsVector)[nIndex];
+ else if( aData.pAsInterface )
+ {
+ if (nIndex == 0)
+ return aData.pAsInterface;
+ }
+ throw std::out_of_range("index out of range");
+}
+
+void OInterfaceContainerHelper2::disposeAndClear( const EventObject & rEvt )
+{
+ ClearableMutexGuard aGuard( rMutex );
+ OInterfaceIteratorHelper2 aIt( *this );
+ // Release container, in case new entries come while disposing
+ OSL_ENSURE( !bIsList || bInUse, "OInterfaceContainerHelper2 not in use" );
+ if( !bIsList && aData.pAsInterface )
+ aData.pAsInterface->release();
+ // set the member to null, use the iterator to delete the values
+ aData.pAsInterface = nullptr;
+ bIsList = false;
+ bInUse = false;
+ aGuard.clear();
+ while( aIt.hasMoreElements() )
+ {
+ try
+ {
+ Reference<XEventListener > xLst( aIt.next(), UNO_QUERY );
+ if( xLst.is() )
+ xLst->disposing( rEvt );
+ }
+ catch ( RuntimeException & )
+ {
+ // be robust, if e.g. a remote bridge has disposed already.
+ // there is no way to delegate the error to the caller :o(.
+ }
+ }
+}
+
+
+void OInterfaceContainerHelper2::clear()
+{
+ MutexGuard aGuard( rMutex );
+ // Release container, in case new entries come while disposing
+ OSL_ENSURE( !bIsList || bInUse, "OInterfaceContainerHelper2 not in use" );
+ if (bInUse)
+ copyAndResetInUse();
+ if (bIsList)
+ delete aData.pAsVector;
+ else if (aData.pAsInterface)
+ aData.pAsInterface->release();
+ aData.pAsInterface = nullptr;
+ bIsList = false;
+}
+
+
+
+// specialized class for type
+
+OMultiTypeInterfaceContainerHelper2::OMultiTypeInterfaceContainerHelper2( Mutex & rMutex_ )
+ : rMutex( rMutex_ )
+{
+}
+
+OMultiTypeInterfaceContainerHelper2::~OMultiTypeInterfaceContainerHelper2()
+{
+}
+
+std::vector< css::uno::Type > OMultiTypeInterfaceContainerHelper2::getContainedTypes() const
+{
+ ::osl::MutexGuard aGuard( rMutex );
+ std::vector< Type > aInterfaceTypes;
+ aInterfaceTypes.reserve( m_aMap.size() );
+ for (const auto& rItem : m_aMap)
+ {
+ // are interfaces added to this container?
+ if( rItem.second->getLength() )
+ // yes, put the type in the array
+ aInterfaceTypes.push_back(rItem.first);
+ }
+ return aInterfaceTypes;
+}
+
+OMultiTypeInterfaceContainerHelper2::t_type2ptr::iterator OMultiTypeInterfaceContainerHelper2::findType(const Type & rKey )
+{
+ return std::find_if(m_aMap.begin(), m_aMap.end(),
+ [&rKey](const t_type2ptr::value_type& rItem) { return rItem.first == rKey; });
+}
+
+OMultiTypeInterfaceContainerHelper2::t_type2ptr::const_iterator OMultiTypeInterfaceContainerHelper2::findType(const Type & rKey ) const
+{
+ return std::find_if(m_aMap.begin(), m_aMap.end(),
+ [&rKey](const t_type2ptr::value_type& rItem) { return rItem.first == rKey; });
+}
+
+OInterfaceContainerHelper2 * OMultiTypeInterfaceContainerHelper2::getContainer( const Type & rKey ) const
+{
+ ::osl::MutexGuard aGuard( rMutex );
+
+ auto iter = findType( rKey );
+ if( iter != m_aMap.end() )
+ return (*iter).second.get();
+ return nullptr;
+}
+
+sal_Int32 OMultiTypeInterfaceContainerHelper2::addInterface(
+ const Type & rKey, const Reference< XInterface > & rListener )
+{
+ ::osl::MutexGuard aGuard( rMutex );
+ auto iter = findType( rKey );
+ if( iter == m_aMap.end() )
+ {
+ OInterfaceContainerHelper2 * pLC = new OInterfaceContainerHelper2( rMutex );
+ m_aMap.emplace_back(rKey, pLC);
+ return pLC->addInterface( rListener );
+ }
+ return (*iter).second->addInterface( rListener );
+}
+
+sal_Int32 OMultiTypeInterfaceContainerHelper2::removeInterface(
+ const Type & rKey, const Reference< XInterface > & rListener )
+{
+ ::osl::MutexGuard aGuard( rMutex );
+
+ // search container with id nUik
+ auto iter = findType( rKey );
+ // container found?
+ if( iter != m_aMap.end() )
+ return (*iter).second->removeInterface( rListener );
+
+ // no container with this id. Always return 0
+ return 0;
+}
+
+void OMultiTypeInterfaceContainerHelper2::disposeAndClear( const EventObject & rEvt )
+{
+ t_type2ptr::size_type nSize = 0;
+ std::unique_ptr<OInterfaceContainerHelper2 *[]> ppListenerContainers;
+ {
+ ::osl::MutexGuard aGuard( rMutex );
+ nSize = m_aMap.size();
+ if( nSize )
+ {
+ typedef OInterfaceContainerHelper2* ppp;
+ ppListenerContainers.reset(new ppp[nSize]);
+
+ t_type2ptr::size_type i = 0;
+ for (const auto& rItem : m_aMap)
+ {
+ ppListenerContainers[i++] = rItem.second.get();
+ }
+ }
+ }
+
+ // create a copy, because do not fire event in a guarded section
+ for( t_type2ptr::size_type i = 0; i < nSize; i++ )
+ {
+ if( ppListenerContainers[i] )
+ ppListenerContainers[i]->disposeAndClear( rEvt );
+ }
+}
+
+void OMultiTypeInterfaceContainerHelper2::clear()
+{
+ ::osl::MutexGuard aGuard( rMutex );
+
+ for (auto& rItem : m_aMap)
+ rItem.second->clear();
+}
+
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/container/namecontainer.cxx b/comphelper/source/container/namecontainer.cxx
new file mode 100644
index 000000000..c13ee7486
--- /dev/null
+++ b/comphelper/source/container/namecontainer.cxx
@@ -0,0 +1,166 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <map>
+#include <mutex>
+
+#include <comphelper/namecontainer.hxx>
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <osl/mutex.hxx>
+#include <com/sun/star/container/XNameContainer.hpp>
+
+typedef std::map<OUString, css::uno::Any> SvGenericNameContainerMapImpl;
+
+namespace comphelper
+{
+ namespace {
+
+ /** this is the base helper class for NameContainer that's also declared in this header. */
+ class NameContainer : public ::cppu::WeakImplHelper< css::container::XNameContainer >
+ {
+ public:
+ explicit NameContainer( const css::uno::Type& aType );
+
+ // XNameContainer
+ virtual void SAL_CALL insertByName( const OUString& aName, const css::uno::Any& aElement ) override;
+ virtual void SAL_CALL removeByName( const OUString& Name ) override;
+
+ // XNameReplace
+ virtual void SAL_CALL replaceByName( const OUString& aName, const css::uno::Any& aElement ) override;
+
+ // XNameAccess
+ virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getElementNames( ) override;
+ virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override;
+
+ // XElementAccess
+ virtual sal_Bool SAL_CALL hasElements( ) override;
+ virtual css::uno::Type SAL_CALL getElementType( ) override;
+
+ private:
+ SvGenericNameContainerMapImpl maProperties;
+ const css::uno::Type maType;
+ std::mutex maMutex;
+ };
+
+ }
+}
+
+using namespace ::comphelper;
+using namespace ::osl;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::lang;
+
+
+NameContainer::NameContainer( const css::uno::Type& aType )
+: maType( aType )
+{
+}
+
+// XNameContainer
+void SAL_CALL NameContainer::insertByName( const OUString& aName, const Any& aElement )
+{
+ std::scoped_lock aGuard( maMutex );
+
+ if( maProperties.find( aName ) != maProperties.end() )
+ throw ElementExistException();
+
+ if( aElement.getValueType() != maType )
+ throw IllegalArgumentException("element is wrong type", static_cast<cppu::OWeakObject*>(this), 2);
+
+ maProperties.emplace(aName,aElement);
+}
+
+void SAL_CALL NameContainer::removeByName( const OUString& Name )
+{
+ std::scoped_lock aGuard( maMutex );
+
+ SvGenericNameContainerMapImpl::iterator aIter = maProperties.find( Name );
+ if( aIter == maProperties.end() )
+ throw NoSuchElementException();
+
+ maProperties.erase( aIter );
+}
+
+// XNameReplace
+
+void SAL_CALL NameContainer::replaceByName( const OUString& aName, const Any& aElement )
+{
+ std::scoped_lock aGuard( maMutex );
+
+ SvGenericNameContainerMapImpl::iterator aIter( maProperties.find( aName ) );
+ if( aIter == maProperties.end() )
+ throw NoSuchElementException();
+
+ if( aElement.getValueType() != maType )
+ throw IllegalArgumentException("element is wrong type", static_cast<cppu::OWeakObject*>(this), 2);
+
+ (*aIter).second = aElement;
+}
+
+// XNameAccess
+
+Any SAL_CALL NameContainer::getByName( const OUString& aName )
+{
+ std::scoped_lock aGuard( maMutex );
+
+ SvGenericNameContainerMapImpl::iterator aIter = maProperties.find( aName );
+ if( aIter == maProperties.end() )
+ throw NoSuchElementException();
+
+ return (*aIter).second;
+}
+
+Sequence< OUString > SAL_CALL NameContainer::getElementNames( )
+{
+ std::scoped_lock aGuard( maMutex );
+
+ return comphelper::mapKeysToSequence(maProperties);
+}
+
+sal_Bool SAL_CALL NameContainer::hasByName( const OUString& aName )
+{
+ std::scoped_lock aGuard( maMutex );
+
+ SvGenericNameContainerMapImpl::iterator aIter = maProperties.find( aName );
+ return aIter != maProperties.end();
+}
+
+sal_Bool SAL_CALL NameContainer::hasElements( )
+{
+ std::scoped_lock aGuard( maMutex );
+
+ return !maProperties.empty();
+}
+
+Type SAL_CALL NameContainer::getElementType()
+{
+ return maType;
+}
+
+Reference< XNameContainer > comphelper::NameContainer_createInstance( const Type& aType )
+{
+ return new NameContainer(aType);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */