1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <sal/config.h>
23 #include <com/sun/star/lang/EventObject.hpp>
24 #include <com/sun/star/lang/DisposedException.hpp>
25 #include <o3tl/cow_wrapper.hxx>
30 namespace com::sun::star::uno
37 template <class ListenerT
> class OInterfaceContainerHelper4
;
39 This is the iterator of an OInterfaceContainerHelper4. Typically
40 one constructs an instance on the stack for one firing session.
41 It is not allowed to assign or copy an instance of this class.
43 @tparam ListenerT UNO event listener type
44 @see OInterfaceContainerHelper4
46 template <class ListenerT
> class OInterfaceIteratorHelper4
50 Create an iterator over the elements of the container. The iterator
51 copies the elements of the container. A change to the container
52 during the lifetime of an iterator is allowed and does not
53 affect the iterator-instance. The iterator and the container take cares
54 themself for concurrent access, no additional guarding is necessary.
56 Remark: The copy is on demand. The iterator copy the elements only if the container
57 change the contents...
59 @param rCont the container of the elements.
61 this parameter only here to make that this container is accessed while locked
63 OInterfaceIteratorHelper4(std::unique_lock
<std::mutex
>& rGuard
,
64 OInterfaceContainerHelper4
<ListenerT
>& rCont_
)
66 , maData(rCont
.maData
)
67 // const_cast so we don't trigger make_unique via o3tl::cow_wrapper::operator->
68 , nRemain(std::as_const(maData
)->size())
70 assert(rGuard
.owns_lock());
74 /** Return true, if there are more elements in the iterator. */
75 bool hasMoreElements() const { return nRemain
!= 0; }
76 /** Return the next element of the iterator. Calling this method if
77 hasMoreElements() has returned false, is an error.
79 css::uno::Reference
<ListenerT
> const& next();
81 /** Removes the current element (the last one returned by next())
82 from the underlying container. Calling this method before
83 next() has been called or calling it twice with no next()
84 in between is an error.
86 this parameter only here to make that this container is accessed while locked
88 void remove(::std::unique_lock
<::std::mutex
>& rGuard
);
91 OInterfaceContainerHelper4
<ListenerT
>& rCont
;
92 o3tl::cow_wrapper
<std::vector
<css::uno::Reference
<ListenerT
>>,
93 o3tl::ThreadSafeRefCountingPolicy
>
97 OInterfaceIteratorHelper4(const OInterfaceIteratorHelper4
&) = delete;
98 OInterfaceIteratorHelper4
& operator=(const OInterfaceIteratorHelper4
&) = delete;
101 template <class ListenerT
>
102 const css::uno::Reference
<ListenerT
>& OInterfaceIteratorHelper4
<ListenerT
>::next()
105 return (*std::as_const(maData
))[nRemain
];
108 template <class ListenerT
>
109 void OInterfaceIteratorHelper4
<ListenerT
>::remove(::std::unique_lock
<::std::mutex
>& rGuard
)
111 rCont
.removeInterface(rGuard
, (*std::as_const(maData
))[nRemain
]);
115 A container of interfaces. To access the elements use an iterator.
116 This implementation is thread-safe.
118 This is a copy of the code at include/comphelper/interfacecontainer3.hxx,
119 except that it (a) uses std::mutex instead of osl::Mutex and (b) does not
120 store a reference to the mutex, but relies on the calling class to take
121 a lock around using it.
123 @tparam ListenerT UNO event listener type
124 @see OInterfaceIteratorHelper
126 template <class ListenerT
> class OInterfaceContainerHelper4
129 OInterfaceContainerHelper4();
132 Return the number of Elements in the container. Only useful if you have acquired
135 this parameter only here to make that this container is accessed while locked
137 sal_Int32
getLength(std::unique_lock
<std::mutex
>& rGuard
) const;
140 Return all interfaces added to this container.
142 this parameter only here to make that this container is accessed while locked
144 std::vector
<css::uno::Reference
<ListenerT
>>
145 getElements(std::unique_lock
<std::mutex
>& rGuard
) const;
147 /** Inserts an element into the container. The position is not specified, thus it is not
148 specified in which order events are fired.
151 If you add the same interface more than once, then it will be added to the elements list
152 more than once and thus if you want to remove that interface from the list, you have to call
153 removeInterface() the same number of times.
154 In the latter case, you will also get events fired more than once (if the interface is a
158 interface to be added; it is allowed to insert
159 the same interface more than once
161 this parameter only here to make that this container is accessed while locked
163 the new count of elements in the container
165 sal_Int32
addInterface(std::unique_lock
<std::mutex
>& rGuard
,
166 const css::uno::Reference
<ListenerT
>& rxIFace
);
167 /** Removes an element from the container. It uses interface equality to remove the interface.
170 interface to be removed
172 this parameter only here to make that this container is accessed while locked
174 the new count of elements in the container
176 sal_Int32
removeInterface(std::unique_lock
<std::mutex
>& rGuard
,
177 const css::uno::Reference
<ListenerT
>& rxIFace
);
179 Call disposing on all object in the container that
180 support XEventListener. Then clear the container.
181 The guard is unlock()'ed before calling the listeners.
183 void disposeAndClear(::std::unique_lock
<::std::mutex
>& rGuard
,
184 const css::lang::EventObject
& rEvt
);
186 Clears the container without calling disposing().
188 this parameter only here to make that this container is accessed while locked
190 void clear(::std::unique_lock
<::std::mutex
>& rGuard
);
192 /** Executes a functor for each contained listener of specified type, e.g.
193 <code>forEach<awt::XPaintListener>(...</code>.
195 If a css::lang::DisposedException occurs which relates to
196 the called listener, then that listener is removed from the container.
198 @tparam FuncT unary functor type, let your compiler deduce this for you
199 @param func unary functor object expecting an argument of type
200 css::uno::Reference<ListenerT>
202 this parameter only here to make that this container is accessed while locked
204 template <typename FuncT
>
205 inline void forEach(std::unique_lock
<std::mutex
>& rGuard
, FuncT
const& func
) const;
207 /** Calls a UNO listener method for each contained listener.
209 The listener method must take a single argument of type EventT,
210 and return <code>void</code>.
212 If a css::lang::DisposedException occurs which relates to
213 the called listener, then that listener is removed from the container.
215 @tparam EventT event type, let your compiler deduce this for you
216 @param NotificationMethod
217 Pointer to a method of a ListenerT interface.
219 Event to notify to all contained listeners
221 this parameter only here to make that this container is accessed while locked
225 awt::PaintEvent aEvent( static_cast< cppu::OWeakObject* >( this ), ... );
226 listeners.notifyEach( &XPaintListener::windowPaint, aEvent );
229 template <typename EventT
>
230 inline void notifyEach(std::unique_lock
<std::mutex
>& rGuard
,
231 void (SAL_CALL
ListenerT::*NotificationMethod
)(const EventT
&),
232 const EventT
& Event
) const;
234 // this is moveable, but not copyable
235 OInterfaceContainerHelper4(OInterfaceContainerHelper4
&&) = default;
236 OInterfaceContainerHelper4
& operator=(OInterfaceContainerHelper4
&&) = default;
239 friend class OInterfaceIteratorHelper4
<ListenerT
>;
240 o3tl::cow_wrapper
<std::vector
<css::uno::Reference
<ListenerT
>>,
241 o3tl::ThreadSafeRefCountingPolicy
>
243 OInterfaceContainerHelper4(const OInterfaceContainerHelper4
&) = delete;
244 OInterfaceContainerHelper4
& operator=(const OInterfaceContainerHelper4
&) = delete;
246 static o3tl::cow_wrapper
<std::vector
<css::uno::Reference
<ListenerT
>>,
247 o3tl::ThreadSafeRefCountingPolicy
>&
250 static o3tl::cow_wrapper
<std::vector
<css::uno::Reference
<ListenerT
>>,
251 o3tl::ThreadSafeRefCountingPolicy
>
257 template <typename EventT
> class NotifySingleListener
260 typedef void (SAL_CALL
ListenerT::*NotificationMethod
)(const EventT
&);
261 NotificationMethod
const m_pMethod
;
262 const EventT
& m_rEvent
;
265 NotifySingleListener(NotificationMethod method
, const EventT
& event
)
272 void operator()(const css::uno::Reference
<ListenerT
>& listener
) const
274 (listener
.get()->*m_pMethod
)(m_rEvent
);
280 inline OInterfaceContainerHelper4
<T
>::OInterfaceContainerHelper4()
281 : maData(OInterfaceContainerHelper4
<T
>::DEFAULT())
286 template <typename FuncT
>
287 inline void OInterfaceContainerHelper4
<T
>::forEach(std::unique_lock
<std::mutex
>& rGuard
,
288 FuncT
const& func
) const
290 assert(rGuard
.owns_lock());
291 if (std::as_const(maData
)->empty())
295 const_cast<OInterfaceContainerHelper4
&>(*this)
296 .maData
.make_unique(); // so we can iterate over the data without holding the lock
297 OInterfaceIteratorHelper4
<T
> iter(rGuard
, const_cast<OInterfaceContainerHelper4
&>(*this));
299 while (iter
.hasMoreElements())
301 auto xListener
= iter
.next();
306 catch (css::lang::DisposedException
const& exc
)
308 if (exc
.Context
== xListener
)
319 template <class ListenerT
>
320 template <typename EventT
>
321 inline void OInterfaceContainerHelper4
<ListenerT
>::notifyEach(
322 std::unique_lock
<std::mutex
>& rGuard
,
323 void (SAL_CALL
ListenerT::*NotificationMethod
)(const EventT
&), const EventT
& Event
) const
325 forEach
<NotifySingleListener
<EventT
>>(rGuard
,
326 NotifySingleListener
<EventT
>(NotificationMethod
, Event
));
329 template <class ListenerT
>
331 OInterfaceContainerHelper4
<ListenerT
>::getLength(std::unique_lock
<std::mutex
>& rGuard
) const
333 assert(rGuard
.owns_lock());
335 return maData
->size();
338 template <class ListenerT
>
339 std::vector
<css::uno::Reference
<ListenerT
>>
340 OInterfaceContainerHelper4
<ListenerT
>::getElements(std::unique_lock
<std::mutex
>& rGuard
) const
342 assert(rGuard
.owns_lock());
347 template <class ListenerT
>
349 OInterfaceContainerHelper4
<ListenerT
>::addInterface(std::unique_lock
<std::mutex
>& rGuard
,
350 const css::uno::Reference
<ListenerT
>& rListener
)
352 assert(rGuard
.owns_lock());
354 assert(rListener
.is());
355 maData
->push_back(rListener
);
356 return std::as_const(maData
)->size();
359 template <class ListenerT
>
360 sal_Int32 OInterfaceContainerHelper4
<ListenerT
>::removeInterface(
361 std::unique_lock
<std::mutex
>& rGuard
, const css::uno::Reference
<ListenerT
>& rListener
)
363 assert(rGuard
.owns_lock());
365 assert(rListener
.is());
367 // It is not valid to compare the pointer directly, but it's faster.
368 auto it
= std::find_if(maData
->begin(), maData
->end(),
369 [&rListener
](const css::uno::Reference
<css::uno::XInterface
>& rItem
) {
370 return rItem
.get() == rListener
.get();
373 // interface not found, use the correct compare method
374 if (it
== maData
->end())
375 it
= std::find(maData
->begin(), maData
->end(), rListener
);
377 if (it
!= maData
->end())
380 return std::as_const(maData
)->size();
383 template <class ListenerT
>
384 void OInterfaceContainerHelper4
<ListenerT
>::disposeAndClear(std::unique_lock
<std::mutex
>& rGuard
,
385 const css::lang::EventObject
& rEvt
)
388 OInterfaceIteratorHelper4
<ListenerT
> aIt(rGuard
, *this);
390 = DEFAULT(); // cheaper than calling maData->clear() because it doesn't allocate a new vector
392 // unlock followed by iterating is only safe because we are not going to call remove() on the iterator
393 while (aIt
.hasMoreElements())
397 aIt
.next()->disposing(rEvt
);
399 catch (css::uno::RuntimeException
&)
401 // be robust, if e.g. a remote bridge has disposed already.
402 // there is no way to delegate the error to the caller :o(.
406 // tdf#152077 need to destruct the OInterfaceIteratorHelper4 before we take the lock again
407 // because there is a vague chance that destructing it will trigger a call back into something
408 // that wants to take the lock.
412 template <class ListenerT
>
413 void OInterfaceContainerHelper4
<ListenerT
>::clear(::std::unique_lock
<::std::mutex
>& rGuard
)
415 assert(rGuard
.owns_lock());
421 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */