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 #ifndef INCLUDED_COMPHELPER_INTERFACECONTAINER3_H
20 #define INCLUDED_COMPHELPER_INTERFACECONTAINER3_H
21 
22 #include <sal/config.h>
23 
24 #include <com/sun/star/lang/EventObject.hpp>
25 #include <com/sun/star/lang/DisposedException.hpp>
26 #include <comphelper/comphelperdllapi.h>
27 #include <o3tl/cow_wrapper.hxx>
28 #include <vector>
29 
30 namespace com
31 {
32 namespace sun
33 {
34 namespace star
35 {
36 namespace uno
37 {
38 class XInterface;
39 }
40 }
41 }
42 }
43 namespace osl
44 {
45 class Mutex;
46 }
47 
48 /** */ //for docpp
49 namespace comphelper
50 {
51 template <class ListenerT> class OInterfaceContainerHelper3;
52 /**
53   This is the iterator of an InterfaceContainerHelper. Typically
54   one constructs an instance on the stack for one firing session.
55   It is not allowed to assign or copy an instance of this class.
56 
57   @tparam ListenerT UNO event listener type
58   @see OInterfaceContainerHelper
59  */
60 template <class ListenerT> class OInterfaceIteratorHelper3
61 {
62 public:
63     /**
64        Create an iterator over the elements of the container. The iterator
65        copies the elements of the container. A change to the container
66        during the lifetime of an iterator is allowed and does not
67        affect the iterator-instance. The iterator and the container take cares
68        themself for concurrent access, no additional guarding is necessary.
69 
70        Remark: The copy is on demand. The iterator copy the elements only if the container
71        change the contents...
72 
73        @param rCont the container of the elements.
74      */
OInterfaceIteratorHelper3(OInterfaceContainerHelper3<ListenerT> & rCont_)75     OInterfaceIteratorHelper3(OInterfaceContainerHelper3<ListenerT>& rCont_)
76         : rCont(rCont_)
77         , maData(rCont.maData)
78         , nRemain(maData->size())
79     {
80     }
81 
82     /** Return true, if there are more elements in the iterator. */
hasMoreElements() const83     bool hasMoreElements() const { return nRemain != 0; }
84     /** Return the next element of the iterator. Calling this method if
85         hasMoreElements() has returned false, is an error.
86      */
87     css::uno::Reference<ListenerT> const& next();
88 
89     /** Removes the current element (the last one returned by next())
90         from the underlying container. Calling this method before
91         next() has been called or calling it twice with no next()
92         in between is an error.
93     */
94     void remove();
95 
96 private:
97     OInterfaceContainerHelper3<ListenerT>& rCont;
98     o3tl::cow_wrapper<std::vector<css::uno::Reference<ListenerT>>> maData;
99     sal_Int32 nRemain;
100 
101     OInterfaceIteratorHelper3(const OInterfaceIteratorHelper3&) = delete;
102     OInterfaceIteratorHelper3& operator=(const OInterfaceIteratorHelper3&) = delete;
103 };
104 
105 template <class ListenerT>
next()106 const css::uno::Reference<ListenerT>& OInterfaceIteratorHelper3<ListenerT>::next()
107 {
108     nRemain--;
109     return (*maData)[nRemain];
110 }
111 
remove()112 template <class ListenerT> void OInterfaceIteratorHelper3<ListenerT>::remove()
113 {
114     rCont.removeInterface((*maData)[nRemain]);
115 }
116 
117 /**
118   A container of interfaces. To access the elements use an iterator.
119   This implementation is thread safe.
120 
121   @tparam ListenerT UNO event listener type
122   @see OInterfaceIteratorHelper
123  */
124 template <class ListenerT> class OInterfaceContainerHelper3
125 {
126 public:
127     /**
128        Create an interface container.
129 
130        @param rMutex    the mutex to protect multi thread access.
131        The lifetime must be longer than the lifetime
132        of this object.
133      */
OInterfaceContainerHelper3(::osl::Mutex & rMutex_)134     OInterfaceContainerHelper3(::osl::Mutex& rMutex_)
135         : rMutex(rMutex_)
136     {
137     }
138     /**
139       Return the number of Elements in the container. Only useful if you have acquired
140       the mutex.
141      */
142     sal_Int32 getLength() const;
143 
144     /**
145       Return all interfaces added to this container.
146      **/
147     std::vector<css::uno::Reference<ListenerT>> getElements() const;
148 
149     /** Inserts an element into the container.  The position is not specified, thus it is not
150         specified in which order events are fired.
151 
152         @attention
153         If you add the same interface more than once, then it will be added to the elements list
154         more than once and thus if you want to remove that interface from the list, you have to call
155         removeInterface() the same number of times.
156         In the latter case, you will also get events fired more than once (if the interface is a
157         listener interface).
158 
159         @param rxIFace
160                interface to be added; it is allowed to
161                the same interface more than once
162         @return
163                 the new count of elements in the container
164     */
165     sal_Int32 addInterface(const css::uno::Reference<ListenerT>& rxIFace);
166     /** Removes an element from the container.  It uses interface equality to remove the interface.
167 
168         @param rxIFace
169                interface to be removed
170         @return
171                 the new count of elements in the container
172     */
173     sal_Int32 removeInterface(const css::uno::Reference<ListenerT>& rxIFace);
174     /**
175       Call disposing on all object in the container that
176       support XEventListener. Then clear the container.
177      */
178     void disposeAndClear(const css::lang::EventObject& rEvt);
179     /**
180       Clears the container without calling disposing().
181      */
182     void clear();
183 
184     /** Executes a functor for each contained listener of specified type, e.g.
185         <code>forEach<awt::XPaintListener>(...</code>.
186 
187         If a css::lang::DisposedException occurs which relates to
188         the called listener, then that listener is removed from the container.
189 
190         @tparam FuncT unary functor type, let your compiler deduce this for you
191         @param func unary functor object expecting an argument of type
192                     css::uno::Reference<ListenerT>
193     */
194     template <typename FuncT> inline void forEach(FuncT const& func);
195 
196     /** Calls a UNO listener method for each contained listener.
197 
198         The listener method must take a single argument of type EventT,
199         and return <code>void</code>.
200 
201         If a css::lang::DisposedException occurs which relates to
202         the called listener, then that listener is removed from the container.
203 
204         @tparam EventT event type, let your compiler deduce this for you
205         @param NotificationMethod
206             Pointer to a method of a ListenerT interface.
207         @param Event
208             Event to notify to all contained listeners
209 
210         Example:
211 @code
212     awt::PaintEvent aEvent( static_cast< cppu::OWeakObject* >( this ), ... );
213     listeners.notifyEach( &XPaintListener::windowPaint, aEvent );
214 @endcode
215     */
216     template <typename EventT>
217     inline void notifyEach(void (SAL_CALL ListenerT::*NotificationMethod)(const EventT&),
218                            const EventT& Event);
219 
220 private:
221     friend class OInterfaceIteratorHelper3<ListenerT>;
222     o3tl::cow_wrapper<std::vector<css::uno::Reference<ListenerT>>> maData;
223     ::osl::Mutex& rMutex;
224     OInterfaceContainerHelper3(const OInterfaceContainerHelper3&) = delete;
225     OInterfaceContainerHelper3& operator=(const OInterfaceContainerHelper3&) = delete;
226 
227 private:
228     template <typename EventT> class NotifySingleListener
229     {
230     private:
231         typedef void (SAL_CALL ListenerT::*NotificationMethod)(const EventT&);
232         NotificationMethod const m_pMethod;
233         const EventT& m_rEvent;
234 
235     public:
NotifySingleListener(NotificationMethod method,const EventT & event)236         NotifySingleListener(NotificationMethod method, const EventT& event)
237             : m_pMethod(method)
238             , m_rEvent(event)
239         {
240         }
241 
operator ()(const css::uno::Reference<ListenerT> & listener) const242         void operator()(const css::uno::Reference<ListenerT>& listener) const
243         {
244             (listener.get()->*m_pMethod)(m_rEvent);
245         }
246     };
247 };
248 
249 template <class T>
250 template <typename FuncT>
forEach(FuncT const & func)251 inline void OInterfaceContainerHelper3<T>::forEach(FuncT const& func)
252 {
253     OInterfaceIteratorHelper3<T> iter(*this);
254     while (iter.hasMoreElements())
255     {
256         auto xListener = iter.next();
257         try
258         {
259             func(xListener);
260         }
261         catch (css::lang::DisposedException const& exc)
262         {
263             if (exc.Context == xListener)
264                 iter.remove();
265         }
266     }
267 }
268 
269 template <class ListenerT>
270 template <typename EventT>
notifyEach(void (SAL_CALL ListenerT::* NotificationMethod)(const EventT &),const EventT & Event)271 inline void OInterfaceContainerHelper3<ListenerT>::notifyEach(
272     void (SAL_CALL ListenerT::*NotificationMethod)(const EventT&), const EventT& Event)
273 {
274     forEach<NotifySingleListener<EventT>>(NotifySingleListener<EventT>(NotificationMethod, Event));
275 }
276 
getLength() const277 template <class ListenerT> sal_Int32 OInterfaceContainerHelper3<ListenerT>::getLength() const
278 {
279     osl::MutexGuard aGuard(rMutex);
280     return maData->size();
281 }
282 
283 template <class ListenerT>
284 std::vector<css::uno::Reference<ListenerT>>
getElements() const285 OInterfaceContainerHelper3<ListenerT>::getElements() const
286 {
287     std::vector<css::uno::Reference<ListenerT>> rVec;
288     osl::MutexGuard aGuard(rMutex);
289     rVec = *maData;
290     return rVec;
291 }
292 
293 template <class ListenerT>
294 sal_Int32
addInterface(const css::uno::Reference<ListenerT> & rListener)295 OInterfaceContainerHelper3<ListenerT>::addInterface(const css::uno::Reference<ListenerT>& rListener)
296 {
297     assert(rListener.is());
298     osl::MutexGuard aGuard(rMutex);
299 
300     maData->push_back(rListener);
301     return maData->size();
302 }
303 
304 template <class ListenerT>
removeInterface(const css::uno::Reference<ListenerT> & rListener)305 sal_Int32 OInterfaceContainerHelper3<ListenerT>::removeInterface(
306     const css::uno::Reference<ListenerT>& rListener)
307 {
308     assert(rListener.is());
309     osl::MutexGuard aGuard(rMutex);
310 
311     // It is not valid to compare the pointer directly, but it's faster.
312     auto it = std::find_if(maData->begin(), maData->end(),
313                            [&rListener](const css::uno::Reference<css::uno::XInterface>& rItem) {
314                                return rItem.get() == rListener.get();
315                            });
316 
317     // interface not found, use the correct compare method
318     if (it == maData->end())
319         it = std::find(maData->begin(), maData->end(), rListener);
320 
321     if (it != maData->end())
322         maData->erase(it);
323 
324     return maData->size();
325 }
326 
327 template <class ListenerT>
disposeAndClear(const css::lang::EventObject & rEvt)328 void OInterfaceContainerHelper3<ListenerT>::disposeAndClear(const css::lang::EventObject& rEvt)
329 {
330     osl::ClearableMutexGuard aGuard(rMutex);
331     OInterfaceIteratorHelper3<ListenerT> aIt(*this);
332     maData->clear();
333     aGuard.clear();
334     while (aIt.hasMoreElements())
335     {
336         try
337         {
338             aIt.next()->disposing(rEvt);
339         }
340         catch (css::uno::RuntimeException&)
341         {
342             // be robust, if e.g. a remote bridge has disposed already.
343             // there is no way to delegate the error to the caller :o(.
344         }
345     }
346 }
347 
clear()348 template <class ListenerT> void OInterfaceContainerHelper3<ListenerT>::clear()
349 {
350     osl::MutexGuard aGuard(rMutex);
351     maData->clear();
352 }
353 }
354 #endif
355 
356 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
357