/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project .
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License , v . 2 . 0 . If a copy of the MPL was not distributed with this
* file , You can obtain one at http : //mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice :
*
* Licensed to the Apache Software Foundation ( ASF ) under one or more
* contributor license agreements . See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership . The ASF licenses this file to you under the Apache
* License , Version 2 . 0 ( the " License " ) ; you may not use this file
* except in compliance with the License . You may obtain a copy of
* the License at http : //www.apache.org/licenses/LICENSE-2.0 .
*/
#pragma once
#include <sal/config.h>
#include <com/sun/star/lang/EventObject.hpp>
#include <com/sun/star/lang/DisposedException.hpp>
#include <o3tl/cow_wrapper.hxx>
#include <cassert>
#include <mutex>
#include <vector>
namespace com::sun::star::uno
{
class XInterface;
}
/**
This is a straight copy of the include / comphelper / interfacecontainer4 . hxx file , copied here
because it is nigh impossible to move shared code down into the URE layer .
*/
namespace cppuhelper
{
template <class ListenerT> class OInterfaceContainerHelper4;
/**
This is the iterator of an OInterfaceContainerHelper4 . Typically
one constructs an instance on the stack for one firing session .
It is not allowed to assign or copy an instance of this class .
@ tparam ListenerT UNO event listener type
@ see OInterfaceContainerHelper4
*/
template <class ListenerT> class OInterfaceIteratorHelper4
{
public :
/**
Create an iterator over the elements of the container . The iterator
copies the elements of the container . A change to the container
during the lifetime of an iterator is allowed and does not
affect the iterator - instance . The iterator and the container take cares
themself for concurrent access , no additional guarding is necessary .
Remark : The copy is on demand . The iterator copy the elements only if the container
change the contents . . .
@ param rCont the container of the elements .
@ param rGuard
this parameter only here to make that this container is accessed while locked
*/
OInterfaceIteratorHelper4(std::unique_lock<std::mutex>& rGuard,
OInterfaceContainerHelper4<ListenerT>& rCont_)
: m_rCont(rCont_)
, m_aData(m_rCont.maData)
// const_cast so we don't trigger make_unique via o3tl::cow_wrapper::operator->
, m_nRemain(std::as_const(m_aData)->size())
{
assert(rGuard.owns_lock());
(void )rGuard;
}
/** Return true, if there are more elements in the iterator. */
bool hasMoreElements() const { return m_nRemain != 0 ; }
/** Return the next element of the iterator. Calling this method if
hasMoreElements ( ) has returned false , is an error .
*/
css::uno::Reference<ListenerT> const & next();
/** Removes the current element (the last one returned by next())
from the underlying container . Calling this method before
next ( ) has been called or calling it twice with no next ( )
in between is an error .
@ param rGuard
this parameter only here to make that this container is accessed while locked
*/
void remove(::std::unique_lock<::std::mutex>& rGuard);
private :
OInterfaceContainerHelper4<ListenerT>& m_rCont;
o3tl::cow_wrapper<std::vector<css::uno::Reference<ListenerT>>,
o3tl::ThreadSafeRefCountingPolicy>
m_aData;
sal_Int32 m_nRemain;
OInterfaceIteratorHelper4(const OInterfaceIteratorHelper4&) = delete ;
OInterfaceIteratorHelper4& operator =(const OInterfaceIteratorHelper4&) = delete ;
};
template <class ListenerT>
const css::uno::Reference<ListenerT>& OInterfaceIteratorHelper4<ListenerT>::next()
{
m_nRemain--;
return (*std::as_const(m_aData))[m_nRemain];
}
template <class ListenerT>
void OInterfaceIteratorHelper4<ListenerT>::remove(::std::unique_lock<::std::mutex>& rGuard)
{
m_rCont.removeInterface(rGuard, (*std::as_const(m_aData))[m_nRemain]);
}
/**
A container of interfaces . To access the elements use an iterator .
This implementation is thread - safe .
This is a copy of the code at include / comphelper / interfacecontainer3 . hxx ,
except that it ( a ) uses std : : mutex instead of osl : : Mutex and ( b ) does not
store a reference to the mutex , but relies on the calling class to take
a lock around using it .
@ tparam ListenerT UNO event listener type
@ see OInterfaceIteratorHelper
*/
template <class ListenerT> class OInterfaceContainerHelper4
{
public :
OInterfaceContainerHelper4();
/**
Return the number of Elements in the container . Only useful if you have acquired
the mutex .
@ param rGuard
this parameter only here to make that this container is accessed while locked
*/
sal_Int32 getLength(std::unique_lock<std::mutex>& rGuard) const ;
/**
Return all interfaces added to this container .
@ param rGuard
this parameter only here to make that this container is accessed while locked
**/
std::vector<css::uno::Reference<ListenerT>>
getElements(std::unique_lock<std::mutex>& rGuard) const ;
/** Inserts an element into the container. The position is not specified, thus it is not
specified in which order events are fired .
@ attention
If you add the same interface more than once , then it will be added to the elements list
more than once and thus if you want to remove that interface from the list , you have to call
removeInterface ( ) the same number of times .
In the latter case , you will also get events fired more than once ( if the interface is a
listener interface ) .
@ param rxIFace
interface to be added ; it is allowed to insert
the same interface more than once
@ param rGuard
this parameter only here to make that this container is accessed while locked
@ return
the new count of elements in the container
*/
sal_Int32 addInterface(std::unique_lock<std::mutex>& rGuard,
const css::uno::Reference<ListenerT>& rxIFace);
/** Removes an element from the container. It uses interface equality to remove the interface.
@ param rxIFace
interface to be removed
@ param rGuard
this parameter only here to make that this container is accessed while locked
@ return
the new count of elements in the container
*/
sal_Int32 removeInterface(std::unique_lock<std::mutex>& rGuard,
const css::uno::Reference<ListenerT>& rxIFace);
/**
Call disposing on all object in the container that
support XEventListener . Then clear the container .
The guard is unlock ( ) ' ed before calling the listeners .
*/
void disposeAndClear(::std::unique_lock<::std::mutex>& rGuard,
const css::lang::EventObject& rEvt);
/**
Clears the container without calling disposing ( ) .
@ param rGuard
this parameter only here to make that this container is accessed while locked
*/
void clear(::std::unique_lock<::std::mutex>& rGuard);
/** Executes a functor for each contained listener of specified type, e.g.
< code > forEach < awt : : XPaintListener > ( . . . < / code > .
If a css : : lang : : DisposedException occurs which relates to
the called listener , then that listener is removed from the container .
@ tparam FuncT unary functor type , let your compiler deduce this for you
@ param func unary functor object expecting an argument of type
css : : uno : : Reference < ListenerT >
@ param rGuard
this parameter only here to make that this container is accessed while locked
*/
template <typename FuncT>
inline void forEach(std::unique_lock<std::mutex>& rGuard, FuncT const & func) const ;
/** Calls a UNO listener method for each contained listener.
The listener method must take a single argument of type EventT ,
and return < code > void < / code > .
If a css : : lang : : DisposedException occurs which relates to
the called listener , then that listener is removed from the container .
@ tparam EventT event type , let your compiler deduce this for you
@ param NotificationMethod
Pointer to a method of a ListenerT interface .
@ param Event
Event to notify to all contained listeners
@ param rGuard
this parameter only here to make that this container is accessed while locked
Example :
@ code
awt : : PaintEvent aEvent ( static_cast < cppu : : OWeakObject * > ( this ) , . . . ) ;
listeners . notifyEach ( & XPaintListener : : windowPaint , aEvent ) ;
@ endcode
*/
template <typename EventT>
inline void notifyEach(std::unique_lock<std::mutex>& rGuard,
void (SAL_CALL ListenerT::*NotificationMethod)(const EventT&),
const EventT& Event) const ;
// this is moveable, but not copyable
OInterfaceContainerHelper4(OInterfaceContainerHelper4&&) = default ;
OInterfaceContainerHelper4& operator =(OInterfaceContainerHelper4&&) = default ;
private :
friend class OInterfaceIteratorHelper4<ListenerT>;
o3tl::cow_wrapper<std::vector<css::uno::Reference<ListenerT>>,
o3tl::ThreadSafeRefCountingPolicy>
maData;
OInterfaceContainerHelper4(const OInterfaceContainerHelper4&) = delete ;
OInterfaceContainerHelper4& operator =(const OInterfaceContainerHelper4&) = delete ;
static o3tl::cow_wrapper<std::vector<css::uno::Reference<ListenerT>>,
o3tl::ThreadSafeRefCountingPolicy>&
DEFAULT ()
{
static o3tl::cow_wrapper<std::vector<css::uno::Reference<ListenerT>>,
o3tl::ThreadSafeRefCountingPolicy>
SINGLETON;
return SINGLETON;
}
private :
template <typename EventT> class NotifySingleListener
{
private :
typedef void (SAL_CALL ListenerT::*NotificationMethod)(const EventT&);
NotificationMethod const m_pMethod;
const EventT& m_rEvent;
public :
NotifySingleListener(NotificationMethod method, const EventT& event)
: m_pMethod(method)
, m_rEvent(event)
{
assert(m_pMethod);
}
void operator ()(const css::uno::Reference<ListenerT>& listener) const
{
(listener.get()->*m_pMethod)(m_rEvent);
}
};
};
template <class T>
inline OInterfaceContainerHelper4<T>::OInterfaceContainerHelper4()
: maData(OInterfaceContainerHelper4<T>::DEFAULT ())
{
}
template <class T>
template <typename FuncT>
inline void OInterfaceContainerHelper4<T>::forEach(std::unique_lock<std::mutex>& rGuard,
FuncT const & func) const
{
assert(rGuard.owns_lock());
if (std::as_const(maData)->size() == 0 )
{
return ;
}
const_cast <OInterfaceContainerHelper4&>(*this )
.maData.make_unique(); // so we can iterate over the data without holding the lock
OInterfaceIteratorHelper4<T> iter(rGuard, const_cast <OInterfaceContainerHelper4&>(*this ));
rGuard.unlock();
while (iter.hasMoreElements())
{
auto xListener = iter.next();
try
{
func(xListener);
}
catch (css::lang::DisposedException const & exc)
{
if (exc.Context == xListener)
{
rGuard.lock();
iter.remove(rGuard);
rGuard.unlock();
}
}
}
rGuard.lock();
}
template <class ListenerT>
template <typename EventT>
inline void OInterfaceContainerHelper4<ListenerT>::notifyEach(
std::unique_lock<std::mutex>& rGuard,
void (SAL_CALL ListenerT::*NotificationMethod)(const EventT&), const EventT& Event) const
{
forEach<NotifySingleListener<EventT>>(rGuard,
NotifySingleListener<EventT>(NotificationMethod, Event));
}
template <class ListenerT>
sal_Int32
OInterfaceContainerHelper4<ListenerT>::getLength(std::unique_lock<std::mutex>& rGuard) const
{
assert(rGuard.owns_lock());
(void )rGuard;
return maData->size();
}
template <class ListenerT>
std::vector<css::uno::Reference<ListenerT>>
OInterfaceContainerHelper4<ListenerT>::getElements(std::unique_lock<std::mutex>& rGuard) const
{
assert(rGuard.owns_lock());
(void )rGuard;
return *maData;
}
template <class ListenerT>
sal_Int32
OInterfaceContainerHelper4<ListenerT>::addInterface(std::unique_lock<std::mutex>& rGuard,
const css::uno::Reference<ListenerT>& rListener)
{
assert(rGuard.owns_lock());
(void )rGuard;
assert(rListener.is());
maData->push_back(rListener);
return std::as_const(maData)->size();
}
template <class ListenerT>
sal_Int32 OInterfaceContainerHelper4<ListenerT>::removeInterface(
std::unique_lock<std::mutex>& rGuard, const css::uno::Reference<ListenerT>& rListener)
{
assert(rGuard.owns_lock());
(void )rGuard;
assert(rListener.is());
// It is not valid to compare the pointer directly, but it's faster.
auto it = std::find_if(maData->begin(), maData->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 == maData->end())
it = std::find(maData->begin(), maData->end(), rListener);
if (it != maData->end())
maData->erase(it);
return std::as_const(maData)->size();
}
template <class ListenerT>
void OInterfaceContainerHelper4<ListenerT>::disposeAndClear(std::unique_lock<std::mutex>& rGuard,
const css::lang::EventObject& rEvt)
{
{
OInterfaceIteratorHelper4<ListenerT> aIt(rGuard, *this );
maData
= DEFAULT (); // cheaper than calling maData->clear() because it doesn't allocate a new vector
rGuard.unlock();
// unlock followed by iterating is only safe because we are not going to call remove() on the iterator
while (aIt.hasMoreElements())
{
try
{
aIt.next()->disposing(rEvt);
}
catch (css::uno::RuntimeException&)
{
// be robust, if e.g. a remote bridge has disposed already.
// there is no way to delegate the error to the caller :o(.
}
}
}
// tdf#152077 need to destruct the OInterfaceIteratorHelper4 before we take the lock again
// because there is a vague chance that destructing it will trigger a call back into something
// that wants to take the lock.
rGuard.lock();
}
template <class ListenerT>
void OInterfaceContainerHelper4<ListenerT>::clear(::std::unique_lock<::std::mutex>& rGuard)
{
assert(rGuard.owns_lock());
(void )rGuard;
maData->clear();
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Messung V0.5 in Prozent C=93 H=98 G=95
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet am 2026-06-09)
¤
*© Formatika GbR, Deutschland