/src/libreoffice/cppuhelper/inc/interfacecontainer4.hxx
Line | Count | Source |
1 | | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | /* |
3 | | * This file is part of the LibreOffice project. |
4 | | * |
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/. |
8 | | * |
9 | | * This file incorporates work covered by the following license notice: |
10 | | * |
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 . |
18 | | */ |
19 | | #pragma once |
20 | | |
21 | | #include <sal/config.h> |
22 | | |
23 | | #include <com/sun/star/lang/EventObject.hpp> |
24 | | #include <com/sun/star/lang/DisposedException.hpp> |
25 | | #include <o3tl/cow_wrapper.hxx> |
26 | | #include <cassert> |
27 | | #include <mutex> |
28 | | #include <vector> |
29 | | |
30 | | namespace com::sun::star::uno |
31 | | { |
32 | | class XInterface; |
33 | | } |
34 | | |
35 | | /** |
36 | | This is a straight copy of the include/comphelper/interfacecontainer4.hxx file, copied here |
37 | | because it is nigh impossible to move shared code down into the URE layer. |
38 | | */ |
39 | | |
40 | | namespace cppuhelper |
41 | | { |
42 | | template <class ListenerT> class OInterfaceContainerHelper4; |
43 | | /** |
44 | | This is the iterator of an OInterfaceContainerHelper4. Typically |
45 | | one constructs an instance on the stack for one firing session. |
46 | | It is not allowed to assign or copy an instance of this class. |
47 | | |
48 | | @tparam ListenerT UNO event listener type |
49 | | @see OInterfaceContainerHelper4 |
50 | | */ |
51 | | template <class ListenerT> class OInterfaceIteratorHelper4 |
52 | | { |
53 | | public: |
54 | | /** |
55 | | Create an iterator over the elements of the container. The iterator |
56 | | copies the elements of the container. A change to the container |
57 | | during the lifetime of an iterator is allowed and does not |
58 | | affect the iterator-instance. The iterator and the container take cares |
59 | | themself for concurrent access, no additional guarding is necessary. |
60 | | |
61 | | Remark: The copy is on demand. The iterator copy the elements only if the container |
62 | | change the contents... |
63 | | |
64 | | @param rCont the container of the elements. |
65 | | @param rGuard |
66 | | this parameter only here to make that this container is accessed while locked |
67 | | */ |
68 | | OInterfaceIteratorHelper4(std::unique_lock<std::mutex>& rGuard, |
69 | | OInterfaceContainerHelper4<ListenerT>& rCont_) |
70 | 0 | : m_rCont(rCont_) |
71 | 0 | , m_aData(m_rCont.maData) |
72 | | // const_cast so we don't trigger make_unique via o3tl::cow_wrapper::operator-> |
73 | 0 | , m_nRemain(std::as_const(m_aData)->size()) |
74 | 0 | { |
75 | 0 | assert(rGuard.owns_lock()); |
76 | 0 | (void)rGuard; |
77 | 0 | } |
78 | | |
79 | | /** Return true, if there are more elements in the iterator. */ |
80 | 0 | bool hasMoreElements() const { return m_nRemain != 0; } |
81 | | /** Return the next element of the iterator. Calling this method if |
82 | | hasMoreElements() has returned false, is an error. |
83 | | */ |
84 | | css::uno::Reference<ListenerT> const& next(); |
85 | | |
86 | | /** Removes the current element (the last one returned by next()) |
87 | | from the underlying container. Calling this method before |
88 | | next() has been called or calling it twice with no next() |
89 | | in between is an error. |
90 | | @param rGuard |
91 | | this parameter only here to make that this container is accessed while locked |
92 | | */ |
93 | | void remove(::std::unique_lock<::std::mutex>& rGuard); |
94 | | |
95 | | private: |
96 | | OInterfaceContainerHelper4<ListenerT>& m_rCont; |
97 | | o3tl::cow_wrapper<std::vector<css::uno::Reference<ListenerT>>, |
98 | | o3tl::ThreadSafeRefCountingPolicy> |
99 | | m_aData; |
100 | | sal_Int32 m_nRemain; |
101 | | |
102 | | OInterfaceIteratorHelper4(const OInterfaceIteratorHelper4&) = delete; |
103 | | OInterfaceIteratorHelper4& operator=(const OInterfaceIteratorHelper4&) = delete; |
104 | | }; |
105 | | |
106 | | template <class ListenerT> |
107 | | const css::uno::Reference<ListenerT>& OInterfaceIteratorHelper4<ListenerT>::next() |
108 | 0 | { |
109 | 0 | m_nRemain--; |
110 | 0 | return (*std::as_const(m_aData))[m_nRemain]; |
111 | 0 | } |
112 | | |
113 | | template <class ListenerT> |
114 | | void OInterfaceIteratorHelper4<ListenerT>::remove(::std::unique_lock<::std::mutex>& rGuard) |
115 | | { |
116 | | m_rCont.removeInterface(rGuard, (*std::as_const(m_aData))[m_nRemain]); |
117 | | } |
118 | | |
119 | | /** |
120 | | A container of interfaces. To access the elements use an iterator. |
121 | | This implementation is thread-safe. |
122 | | |
123 | | This is a copy of the code at include/comphelper/interfacecontainer3.hxx, |
124 | | except that it (a) uses std::mutex instead of osl::Mutex and (b) does not |
125 | | store a reference to the mutex, but relies on the calling class to take |
126 | | a lock around using it. |
127 | | |
128 | | @tparam ListenerT UNO event listener type |
129 | | @see OInterfaceIteratorHelper |
130 | | */ |
131 | | template <class ListenerT> class OInterfaceContainerHelper4 |
132 | | { |
133 | | public: |
134 | | OInterfaceContainerHelper4(); |
135 | | |
136 | | /** |
137 | | Return the number of Elements in the container. Only useful if you have acquired |
138 | | the mutex. |
139 | | @param rGuard |
140 | | this parameter only here to make that this container is accessed while locked |
141 | | */ |
142 | | sal_Int32 getLength(std::unique_lock<std::mutex>& rGuard) const; |
143 | | |
144 | | /** |
145 | | Return all interfaces added to this container. |
146 | | @param rGuard |
147 | | this parameter only here to make that this container is accessed while locked |
148 | | **/ |
149 | | std::vector<css::uno::Reference<ListenerT>> |
150 | | getElements(std::unique_lock<std::mutex>& rGuard) const; |
151 | | |
152 | | /** Inserts an element into the container. The position is not specified, thus it is not |
153 | | specified in which order events are fired. |
154 | | |
155 | | @attention |
156 | | If you add the same interface more than once, then it will be added to the elements list |
157 | | more than once and thus if you want to remove that interface from the list, you have to call |
158 | | removeInterface() the same number of times. |
159 | | In the latter case, you will also get events fired more than once (if the interface is a |
160 | | listener interface). |
161 | | |
162 | | @param rxIFace |
163 | | interface to be added; it is allowed to insert |
164 | | the same interface more than once |
165 | | @param rGuard |
166 | | this parameter only here to make that this container is accessed while locked |
167 | | @return |
168 | | the new count of elements in the container |
169 | | */ |
170 | | sal_Int32 addInterface(std::unique_lock<std::mutex>& rGuard, |
171 | | const css::uno::Reference<ListenerT>& rxIFace); |
172 | | /** Removes an element from the container. It uses interface equality to remove the interface. |
173 | | |
174 | | @param rxIFace |
175 | | interface to be removed |
176 | | @param rGuard |
177 | | this parameter only here to make that this container is accessed while locked |
178 | | @return |
179 | | the new count of elements in the container |
180 | | */ |
181 | | sal_Int32 removeInterface(std::unique_lock<std::mutex>& rGuard, |
182 | | const css::uno::Reference<ListenerT>& rxIFace); |
183 | | /** |
184 | | Call disposing on all object in the container that |
185 | | support XEventListener. Then clear the container. |
186 | | The guard is unlock()'ed before calling the listeners. |
187 | | */ |
188 | | void disposeAndClear(::std::unique_lock<::std::mutex>& rGuard, |
189 | | const css::lang::EventObject& rEvt); |
190 | | /** |
191 | | Clears the container without calling disposing(). |
192 | | @param rGuard |
193 | | this parameter only here to make that this container is accessed while locked |
194 | | */ |
195 | | void clear(::std::unique_lock<::std::mutex>& rGuard); |
196 | | |
197 | | /** Executes a functor for each contained listener of specified type, e.g. |
198 | | <code>forEach<awt::XPaintListener>(...</code>. |
199 | | |
200 | | If a css::lang::DisposedException occurs which relates to |
201 | | the called listener, then that listener is removed from the container. |
202 | | |
203 | | @tparam FuncT unary functor type, let your compiler deduce this for you |
204 | | @param func unary functor object expecting an argument of type |
205 | | css::uno::Reference<ListenerT> |
206 | | @param rGuard |
207 | | this parameter only here to make that this container is accessed while locked |
208 | | */ |
209 | | template <typename FuncT> |
210 | | inline void forEach(std::unique_lock<std::mutex>& rGuard, FuncT const& func) const; |
211 | | |
212 | | /** Calls a UNO listener method for each contained listener. |
213 | | |
214 | | The listener method must take a single argument of type EventT, |
215 | | and return <code>void</code>. |
216 | | |
217 | | If a css::lang::DisposedException occurs which relates to |
218 | | the called listener, then that listener is removed from the container. |
219 | | |
220 | | @tparam EventT event type, let your compiler deduce this for you |
221 | | @param NotificationMethod |
222 | | Pointer to a method of a ListenerT interface. |
223 | | @param Event |
224 | | Event to notify to all contained listeners |
225 | | @param rGuard |
226 | | this parameter only here to make that this container is accessed while locked |
227 | | |
228 | | Example: |
229 | | @code |
230 | | awt::PaintEvent aEvent( static_cast< cppu::OWeakObject* >( this ), ... ); |
231 | | listeners.notifyEach( &XPaintListener::windowPaint, aEvent ); |
232 | | @endcode |
233 | | */ |
234 | | template <typename EventT> |
235 | | inline void notifyEach(std::unique_lock<std::mutex>& rGuard, |
236 | | void (SAL_CALL ListenerT::*NotificationMethod)(const EventT&), |
237 | | const EventT& Event) const; |
238 | | |
239 | | // this is moveable, but not copyable |
240 | | OInterfaceContainerHelper4(OInterfaceContainerHelper4&&) = default; |
241 | | OInterfaceContainerHelper4& operator=(OInterfaceContainerHelper4&&) = default; |
242 | | |
243 | | private: |
244 | | friend class OInterfaceIteratorHelper4<ListenerT>; |
245 | | o3tl::cow_wrapper<std::vector<css::uno::Reference<ListenerT>>, |
246 | | o3tl::ThreadSafeRefCountingPolicy> |
247 | | maData; |
248 | | OInterfaceContainerHelper4(const OInterfaceContainerHelper4&) = delete; |
249 | | OInterfaceContainerHelper4& operator=(const OInterfaceContainerHelper4&) = delete; |
250 | | |
251 | | static o3tl::cow_wrapper<std::vector<css::uno::Reference<ListenerT>>, |
252 | | o3tl::ThreadSafeRefCountingPolicy>& |
253 | | DEFAULT() |
254 | 318 | { |
255 | 318 | static o3tl::cow_wrapper<std::vector<css::uno::Reference<ListenerT>>, |
256 | 318 | o3tl::ThreadSafeRefCountingPolicy> |
257 | 318 | SINGLETON; |
258 | 318 | return SINGLETON; |
259 | 318 | } |
260 | | |
261 | | private: |
262 | | template <typename EventT> class NotifySingleListener |
263 | | { |
264 | | private: |
265 | | typedef void (SAL_CALL ListenerT::*NotificationMethod)(const EventT&); |
266 | | NotificationMethod const m_pMethod; |
267 | | const EventT& m_rEvent; |
268 | | |
269 | | public: |
270 | | NotifySingleListener(NotificationMethod method, const EventT& event) |
271 | | : m_pMethod(method) |
272 | | , m_rEvent(event) |
273 | | { |
274 | | assert(m_pMethod); |
275 | | } |
276 | | |
277 | | void operator()(const css::uno::Reference<ListenerT>& listener) const |
278 | | { |
279 | | (listener.get()->*m_pMethod)(m_rEvent); |
280 | | } |
281 | | }; |
282 | | }; |
283 | | |
284 | | template <class T> |
285 | | inline OInterfaceContainerHelper4<T>::OInterfaceContainerHelper4() |
286 | 318 | : maData(OInterfaceContainerHelper4<T>::DEFAULT()) |
287 | 318 | { |
288 | 318 | } |
289 | | |
290 | | template <class T> |
291 | | template <typename FuncT> |
292 | | inline void OInterfaceContainerHelper4<T>::forEach(std::unique_lock<std::mutex>& rGuard, |
293 | | FuncT const& func) const |
294 | | { |
295 | | assert(rGuard.owns_lock()); |
296 | | if (std::as_const(maData)->size() == 0) |
297 | | { |
298 | | return; |
299 | | } |
300 | | const_cast<OInterfaceContainerHelper4&>(*this) |
301 | | .maData.make_unique(); // so we can iterate over the data without holding the lock |
302 | | OInterfaceIteratorHelper4<T> iter(rGuard, const_cast<OInterfaceContainerHelper4&>(*this)); |
303 | | rGuard.unlock(); |
304 | | while (iter.hasMoreElements()) |
305 | | { |
306 | | auto xListener = iter.next(); |
307 | | try |
308 | | { |
309 | | func(xListener); |
310 | | } |
311 | | catch (css::lang::DisposedException const& exc) |
312 | | { |
313 | | if (exc.Context == xListener) |
314 | | { |
315 | | rGuard.lock(); |
316 | | iter.remove(rGuard); |
317 | | rGuard.unlock(); |
318 | | } |
319 | | } |
320 | | } |
321 | | rGuard.lock(); |
322 | | } |
323 | | |
324 | | template <class ListenerT> |
325 | | template <typename EventT> |
326 | | inline void OInterfaceContainerHelper4<ListenerT>::notifyEach( |
327 | | std::unique_lock<std::mutex>& rGuard, |
328 | | void (SAL_CALL ListenerT::*NotificationMethod)(const EventT&), const EventT& Event) const |
329 | | { |
330 | | forEach<NotifySingleListener<EventT>>(rGuard, |
331 | | NotifySingleListener<EventT>(NotificationMethod, Event)); |
332 | | } |
333 | | |
334 | | template <class ListenerT> |
335 | | sal_Int32 |
336 | | OInterfaceContainerHelper4<ListenerT>::getLength(std::unique_lock<std::mutex>& rGuard) const |
337 | | { |
338 | | assert(rGuard.owns_lock()); |
339 | | (void)rGuard; |
340 | | return maData->size(); |
341 | | } |
342 | | |
343 | | template <class ListenerT> |
344 | | std::vector<css::uno::Reference<ListenerT>> |
345 | | OInterfaceContainerHelper4<ListenerT>::getElements(std::unique_lock<std::mutex>& rGuard) const |
346 | | { |
347 | | assert(rGuard.owns_lock()); |
348 | | (void)rGuard; |
349 | | return *maData; |
350 | | } |
351 | | |
352 | | template <class ListenerT> |
353 | | sal_Int32 |
354 | | OInterfaceContainerHelper4<ListenerT>::addInterface(std::unique_lock<std::mutex>& rGuard, |
355 | | const css::uno::Reference<ListenerT>& rListener) |
356 | 111 | { |
357 | 111 | assert(rGuard.owns_lock()); |
358 | 111 | (void)rGuard; |
359 | 111 | assert(rListener.is()); |
360 | 111 | maData->push_back(rListener); |
361 | 111 | return std::as_const(maData)->size(); |
362 | 111 | } |
363 | | |
364 | | template <class ListenerT> |
365 | | sal_Int32 OInterfaceContainerHelper4<ListenerT>::removeInterface( |
366 | | std::unique_lock<std::mutex>& rGuard, const css::uno::Reference<ListenerT>& rListener) |
367 | 0 | { |
368 | 0 | assert(rGuard.owns_lock()); |
369 | 0 | (void)rGuard; |
370 | 0 | assert(rListener.is()); |
371 | | |
372 | | // It is not valid to compare the pointer directly, but it's faster. |
373 | 0 | auto it = std::find_if(maData->begin(), maData->end(), |
374 | 0 | [&rListener](const css::uno::Reference<css::uno::XInterface>& rItem) { |
375 | 0 | return rItem.get() == rListener.get(); |
376 | 0 | }); |
377 | | |
378 | | // interface not found, use the correct compare method |
379 | 0 | if (it == maData->end()) |
380 | 0 | it = std::find(maData->begin(), maData->end(), rListener); |
381 | |
|
382 | 0 | if (it != maData->end()) |
383 | 0 | maData->erase(it); |
384 | |
|
385 | 0 | return std::as_const(maData)->size(); |
386 | 0 | } |
387 | | |
388 | | template <class ListenerT> |
389 | | void OInterfaceContainerHelper4<ListenerT>::disposeAndClear(std::unique_lock<std::mutex>& rGuard, |
390 | | const css::lang::EventObject& rEvt) |
391 | 0 | { |
392 | 0 | { |
393 | 0 | OInterfaceIteratorHelper4<ListenerT> aIt(rGuard, *this); |
394 | 0 | maData |
395 | 0 | = DEFAULT(); // cheaper than calling maData->clear() because it doesn't allocate a new vector |
396 | 0 | rGuard.unlock(); |
397 | | // unlock followed by iterating is only safe because we are not going to call remove() on the iterator |
398 | 0 | while (aIt.hasMoreElements()) |
399 | 0 | { |
400 | 0 | try |
401 | 0 | { |
402 | 0 | aIt.next()->disposing(rEvt); |
403 | 0 | } |
404 | 0 | catch (css::uno::RuntimeException&) |
405 | 0 | { |
406 | | // be robust, if e.g. a remote bridge has disposed already. |
407 | | // there is no way to delegate the error to the caller :o(. |
408 | 0 | } |
409 | 0 | } |
410 | 0 | } |
411 | | // tdf#152077 need to destruct the OInterfaceIteratorHelper4 before we take the lock again |
412 | | // because there is a vague chance that destructing it will trigger a call back into something |
413 | | // that wants to take the lock. |
414 | 0 | rGuard.lock(); |
415 | 0 | } |
416 | | |
417 | | template <class ListenerT> |
418 | | void OInterfaceContainerHelper4<ListenerT>::clear(::std::unique_lock<::std::mutex>& rGuard) |
419 | | { |
420 | | assert(rGuard.owns_lock()); |
421 | | (void)rGuard; |
422 | | maData->clear(); |
423 | | } |
424 | | } |
425 | | |
426 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |