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 
20 /*
21  * This file is part of LibreOffice published API.
22  */
23 #ifndef INCLUDED_CPPUHELPER_INTERFACECONTAINER_H
24 #define INCLUDED_CPPUHELPER_INTERFACECONTAINER_H
25 
26 #include "sal/config.h"
27 
28 #include <cstddef>
29 #include <functional>
30 #include <vector>
31 #include <utility>
32 
33 #include "osl/diagnose.h"
34 #include "osl/mutex.hxx"
35 #include "rtl/alloc.h"
36 #include "com/sun/star/uno/Sequence.hxx"
37 #include "com/sun/star/lang/EventObject.hpp"
38 
39 #include "com/sun/star/lang/DisposedException.hpp"
40 #include "cppuhelper/cppuhelperdllapi.h"
41 
42 namespace com { namespace sun { namespace star { namespace uno { class XInterface; } } } }
43 
44 /** */ //for docpp
45 namespace cppu
46 {
47 
48 namespace detail {
49 
50     /**
51       This is here to optimise space in the common case that there are zero or one
52       listeners.
53     */
54     union element_alias
55     {
56         std::vector< css::uno::Reference< css::uno::XInterface > > *pAsVector;
57         css::uno::XInterface * pAsInterface;
element_alias()58         element_alias() : pAsInterface(NULL) {}
59     };
60 
61 }
62 
63 
64 class OInterfaceContainerHelper;
65 /**
66   This is the iterator of an InterfaceContainerHelper. Typically
67   one constructs an instance on the stack for one firing session.
68   It is not allowed to assign or copy an instance of this class.
69 
70   @see OInterfaceContainerHelper
71  */
72 class CPPUHELPER_DLLPUBLIC OInterfaceIteratorHelper
73 {
74 public:
75     /**
76        Create an iterator over the elements of the container. The iterator
77        copies the elements of the container. A change to the container
78        during the lifetime of an iterator is allowed and does not
79        affect the iterator-instance. The iterator and the container take cares
80        themself for concurrent access, no additional guarding is necessary.
81 
82        Remark: The copy is on demand. The iterator copy the elements only if the container
83        change the contents. It is not allowed to destroy the container as long
84        as an iterator exist.
85 
86        @param rCont the container of the elements.
87      */
88     OInterfaceIteratorHelper( OInterfaceContainerHelper & rCont );
89 
90     /**
91       Releases the connection to the container.
92      */
93     ~OInterfaceIteratorHelper();
94 
95     /** Return true, if there are more elements in the iterator. */
hasMoreElements()96     bool SAL_CALL hasMoreElements() const
97         { return nRemain != 0; }
98     /** Return the next element of the iterator. Calling this method if
99         hasMoreElements() has returned false, is an error. Cast the
100         returned pointer to the
101      */
102     css::uno::XInterface * SAL_CALL next();
103 
104     /** Removes the current element (the last one returned by next())
105         from the underlying container. Calling this method before
106         next() has been called or calling it twice with no next()
107         inbetween is an error.
108     */
109     void SAL_CALL remove();
110 
111 private:
112     OInterfaceContainerHelper & rCont;
113     sal_Bool                    bIsList;
114 
115     detail::element_alias aData;
116 
117     sal_Int32                   nRemain;
118 
119     OInterfaceIteratorHelper( const OInterfaceIteratorHelper & )
120         SAL_DELETED_FUNCTION;
121     OInterfaceIteratorHelper &  operator = ( const OInterfaceIteratorHelper & )
122         SAL_DELETED_FUNCTION;
123 };
124 
125 
126 /**
127   A container of interfaces. To access the elements use an iterator.
128   This implementation is thread save.
129 
130   @see OInterfaceIteratorHelper
131  */
132 class SAL_WARN_UNUSED CPPUHELPER_DLLPUBLIC OInterfaceContainerHelper
133 {
134 public:
135     // these are here to force memory de/allocation to sal lib.
new(size_t nSize)136     static void * SAL_CALL operator new( size_t nSize )
137         { return ::rtl_allocateMemory( nSize ); }
delete(void * pMem)138     static void SAL_CALL operator delete( void * pMem )
139         { ::rtl_freeMemory( pMem ); }
new(size_t,void * pMem)140     static void * SAL_CALL operator new( size_t, void * pMem )
141         { return pMem; }
delete(void *,void *)142     static void SAL_CALL operator delete( void *, void * )
143         {}
144 
145     /**
146        Create an interface container.
147 
148        @param rMutex    the mutex to protect multi thread access.
149        The lifetime must be longer than the lifetime
150        of this object.
151      */
152     OInterfaceContainerHelper( ::osl::Mutex & rMutex );
153     /**
154       Release all interfaces. All iterators must be destroyed before
155       the container is destructed.
156      */
157     ~OInterfaceContainerHelper();
158     /**
159       Return the number of Elements in the container. Only useful if you have acquired
160       the mutex.
161      */
162     sal_Int32 SAL_CALL getLength() const;
163 
164     /**
165       Return all interfaces added to this container.
166      **/
167     css::uno::Sequence< css::uno::Reference< css::uno::XInterface > > SAL_CALL getElements() const;
168 
169     /** Inserts an element into the container.  The position is not specified, thus it is not
170         specified in which order events are fired.
171 
172         @attention
173         If you add the same interface more than once, then it will be added to the elements list
174         more than once and thus if you want to remove that interface from the list, you have to call
175         removeInterface() the same number of times.
176         In the latter case, you will also get events fired more than once (if the interface is a
177         listener interface).
178 
179         @param rxIFace
180                interface to be added; it is allowed to insert null or
181                the same interface more than once
182         @return
183                 the new count of elements in the container
184     */
185     sal_Int32 SAL_CALL addInterface( const css::uno::Reference< css::uno::XInterface > & rxIFace );
186     /** Removes an element from the container.  It uses interface equality to remove the interface.
187 
188         @param rxIFace
189                interface to be removed
190         @return
191                 the new count of elements in the container
192     */
193     sal_Int32 SAL_CALL removeInterface( const css::uno::Reference< css::uno::XInterface > & rxIFace );
194     /**
195       Call disposing on all object in the container that
196       support XEventListener. Then clear the container.
197      */
198     void SAL_CALL disposeAndClear( const css::lang::EventObject & rEvt );
199     /**
200       Clears the container without calling disposing().
201      */
202     void SAL_CALL clear();
203 
204     /** Executes a functor for each contained listener of specified type, e.g.
205         <code>forEach<awt::XPaintListener>(...</code>.
206 
207         If a css::lang::DisposedException occurs which relates to
208         the called listener, then that listener is removed from the container.
209 
210         @tparam ListenerT listener type
211         @tparam FuncT unary functor type, let your compiler deduce this for you
212         @param func unary functor object expecting an argument of type
213                     css::uno::Reference<ListenerT>
214     */
215     template <typename ListenerT, typename FuncT>
216     inline void forEach( FuncT const& func );
217 
218     /** Calls a UNO listener method for each contained listener.
219 
220         The listener method must take a single argument of type EventT,
221         and return <code>void</code>.
222 
223         If a css::lang::DisposedException occurs which relates to
224         the called listener, then that listener is removed from the container.
225 
226         @tparam ListenerT UNO event listener type, let your compiler deduce this for you
227         @tparam EventT event type, let your compiler deduce this for you
228         @param NotificationMethod
229             Pointer to a method of a ListenerT interface.
230         @param Event
231             Event to notify to all contained listeners
232 
233         Example:
234 @code
235     awt::PaintEvent aEvent( static_cast< cppu::OWeakObject* >( this ), ... );
236     listeners.notifyEach( &XPaintListener::windowPaint, aEvent );
237 @endcode
238     */
239     template< typename ListenerT, typename EventT >
240     inline void notifyEach( void ( SAL_CALL ListenerT::*NotificationMethod )( const EventT& ), const EventT& Event );
241 
242 private:
243 friend class OInterfaceIteratorHelper;
244     /**
245       bIsList == TRUE -> aData.pAsSequence of type Sequence< XInterfaceSequence >,
246       otherwise aData.pAsInterface == of type (XEventListener *)
247      */
248     detail::element_alias   aData;
249     ::osl::Mutex &          rMutex;
250     /** TRUE -> used by an iterator. */
251     sal_Bool                bInUse;
252     /** TRUE -> aData.pAsSequence is of type Sequence< XInterfaceSequence >. */
253     sal_Bool                bIsList;
254 
255     OInterfaceContainerHelper( const OInterfaceContainerHelper & )
256         SAL_DELETED_FUNCTION;
257     OInterfaceContainerHelper & operator = ( const OInterfaceContainerHelper & )
258         SAL_DELETED_FUNCTION;
259 
260     /*
261       Duplicate content of the container and release the old one without destroying.
262       The mutex must be locked and the memberbInUse must be true.
263      */
264     void copyAndResetInUse();
265 
266 private:
267     template< typename ListenerT, typename EventT >
268     class NotifySingleListener
269     {
270     private:
271         typedef void ( SAL_CALL ListenerT::*NotificationMethod )( const EventT& );
272         NotificationMethod  m_pMethod;
273         const EventT&       m_rEvent;
274     public:
NotifySingleListener(NotificationMethod method,const EventT & event)275         NotifySingleListener( NotificationMethod method, const EventT& event ) : m_pMethod( method ), m_rEvent( event ) { }
276 
operator()277         void operator()( const css::uno::Reference<ListenerT>& listener ) const
278         {
279             (listener.get()->*m_pMethod)( m_rEvent );
280         }
281     };
282 };
283 
284 template <typename ListenerT, typename FuncT>
forEach(FuncT const & func)285 inline void OInterfaceContainerHelper::forEach( FuncT const& func )
286 {
287     OInterfaceIteratorHelper iter( *this );
288     while (iter.hasMoreElements()) {
289         css::uno::Reference<ListenerT> const xListener( iter.next(), css::uno::UNO_QUERY );
290         if (xListener.is()) {
291             try {
292                 func( xListener );
293             }
294             catch (css::lang::DisposedException const& exc) {
295                 if (exc.Context == xListener)
296                     iter.remove();
297             }
298         }
299     }
300 }
301 
302 template< typename ListenerT, typename EventT >
notifyEach(void (SAL_CALL ListenerT::* NotificationMethod)(const EventT &),const EventT & Event)303 inline void OInterfaceContainerHelper::notifyEach( void ( SAL_CALL ListenerT::*NotificationMethod )( const EventT& ), const EventT& Event )
304 {
305     forEach< ListenerT, NotifySingleListener< ListenerT, EventT > >( NotifySingleListener< ListenerT, EventT >( NotificationMethod, Event ) );
306 }
307 
308 
309 /**
310   A helper class to store interface references of different types.
311 
312   @see OInterfaceIteratorHelper
313   @see OInterfaceContainerHelper
314  */
315 template< class key, class hashImpl = void, class equalImpl = std::equal_to<key> >
316 class OMultiTypeInterfaceContainerHelperVar
317 {
318 public:
319     // these are here to force memory de/allocation to sal lib.
new(size_t nSize)320     static void * SAL_CALL operator new( size_t nSize )
321         { return ::rtl_allocateMemory( nSize ); }
delete(void * pMem)322     static void SAL_CALL operator delete( void * pMem )
323         { ::rtl_freeMemory( pMem ); }
new(size_t,void * pMem)324     static void * SAL_CALL operator new( size_t, void * pMem )
325         { return pMem; }
delete(void *,void *)326     static void SAL_CALL operator delete( void *, void * )
327         {}
328 
329     /**
330       Create a container of interface containers.
331 
332       @param rMutex the mutex to protect multi thread access.
333                          The lifetime must be longer than the lifetime
334                          of this object.
335      */
336     inline OMultiTypeInterfaceContainerHelperVar( ::osl::Mutex & rMutex );
337     /**
338       Deletes all containers.
339      */
340     inline ~OMultiTypeInterfaceContainerHelperVar();
341 
342     /**
343       Return all id's under which at least one interface is added.
344      */
345     inline css::uno::Sequence< key > SAL_CALL getContainedTypes() const;
346 
347     /**
348       Return the container created under this key.
349       The InterfaceContainerHelper exists until the whole MultiTypeContainer is destroyed.
350       @return the container created under this key. If the container
351                  was not created, null was returned.
352      */
353     inline OInterfaceContainerHelper * SAL_CALL getContainer( const key & ) const;
354 
355     /** Inserts an element into the container with the specified key.
356         The position is not specified, thus it is not specified in which order events are fired.
357 
358         @attention
359         If you add the same interface more than once, then it will be added to the elements list
360         more than once and thus if you want to remove that interface from the list, you have to call
361         removeInterface() the same number of times.
362         In the latter case, you will also get events fired more than once (if the interface is a
363         listener interface).
364 
365         @param rKey
366                the id of the container
367         @param r
368                interface to be added; it is allowed, to insert null or
369                the same interface more than once
370         @return
371                 the new count of elements in the container
372     */
373     inline sal_Int32 SAL_CALL addInterface(
374         const key & rKey,
375         const css::uno::Reference< css::uno::XInterface > & r );
376 
377     /** Removes an element from the container with the specified key.
378         It uses interface equality to remove the interface.
379 
380         @param rKey
381                the id of the container
382         @param rxIFace
383                interface to be removed
384         @return
385                 the new count of elements in the container
386     */
387     inline sal_Int32 SAL_CALL removeInterface(
388         const key & rKey,
389         const css::uno::Reference< css::uno::XInterface > & rxIFace );
390 
391     /**
392       Call disposing on all references in the container, that
393       support XEventListener. Then clears the container.
394       @param rEvt the event object which is passed during disposing() call
395      */
396     inline void SAL_CALL disposeAndClear( const css::lang::EventObject & rEvt );
397     /**
398       Remove all elements of all containers. Does not delete the container.
399      */
400     inline void SAL_CALL clear();
401 
402     typedef key keyType;
403 private:
404     typedef ::std::vector< std::pair < key , void* > > InterfaceMap;
405     InterfaceMap *m_pMap;
406     ::osl::Mutex &  rMutex;
407 
find(const key & rKey)408     typename InterfaceMap::iterator find(const key &rKey) const
409     {
410         typename InterfaceMap::iterator iter = m_pMap->begin();
411         typename InterfaceMap::iterator end = m_pMap->end();
412 
413         while( iter != end )
414         {
415             equalImpl equal;
416             if( equal( iter->first, rKey ) )
417                 break;
418             ++iter;
419         }
420         return iter;
421     }
422 
423     OMultiTypeInterfaceContainerHelperVar( const OMultiTypeInterfaceContainerHelperVar & ) SAL_DELETED_FUNCTION;
424     OMultiTypeInterfaceContainerHelperVar & operator = ( const OMultiTypeInterfaceContainerHelperVar & ) SAL_DELETED_FUNCTION;
425 };
426 
427 
428 
429 
430 /**
431   This struct contains the standard variables of a broadcaster. Helper
432   classes only know a reference to this struct instead of references
433   to the four members. The access to the members must be guarded with
434   rMutex.
435 
436   The additional template parameter keyType has been added, because gcc
437   can't compile addListener( const container::keyType &key ).
438  */
439 template < class container , class keyType >
440 struct SAL_WARN_UNUSED OBroadcastHelperVar
441 {
442     /** The shared mutex. */
443     ::osl::Mutex &                      rMutex;
444     /** ListenerContainer class is thread safe. */
445     container   aLC;
446     /** Dispose call ready. */
447     sal_Bool                            bDisposed;
448     /** In dispose call. */
449     sal_Bool                            bInDispose;
450 
451     /**
452       Initialize the structure. bDispose and bInDispose are set to false.
453       @param rMutex_ the mutex reference.
454      */
OBroadcastHelperVarOBroadcastHelperVar455     OBroadcastHelperVar( ::osl::Mutex & rMutex_ )
456         : rMutex( rMutex_ )
457         , aLC( rMutex_ )
458         , bDisposed( false )
459         , bInDispose( false )
460     {}
461 
462     /**
463       adds a listener threadsafe.
464      **/
addListenerOBroadcastHelperVar465     void addListener(
466         const keyType &key,
467         const css::uno::Reference < css::uno::XInterface > &r )
468     {
469         ::osl::MutexGuard guard( rMutex );
470         OSL_ENSURE( !bInDispose, "do not add listeners in the dispose call" );
471         OSL_ENSURE( !bDisposed, "object is disposed" );
472         if( ! bInDispose && ! bDisposed  )
473             aLC.addInterface( key , r );
474     }
475 
476     /**
477       removes a listener threadsafe
478      **/
removeListenerOBroadcastHelperVar479     void removeListener(
480         const keyType &key,
481         const css::uno::Reference < css::uno::XInterface > & r )
482     {
483         ::osl::MutexGuard guard( rMutex );
484         if( ! bInDispose && ! bDisposed  )
485             aLC.removeInterface( key , r );
486     }
487 
488     /**
489       Return the container created under this key.
490       @return the container created under this key. If the container
491                 was not created, null was returned. This can be used to optimize
492               performance ( construction of an event object can be avoided ).
493      ***/
getContainerOBroadcastHelperVar494     OInterfaceContainerHelper * SAL_CALL getContainer( const keyType &key ) const
495         { return aLC.getContainer( key ); }
496 };
497 
498 /*------------------------------------------
499 *
500 * In general, the above templates are used with a Type as key.
501 * Therefore a default declaration is given ( OMultiTypeInterfaceContainerHelper and OBroadcastHelper )
502 *
503 *------------------------------------------*/
504 
505 // helper function call class
506 struct hashType_Impl
507 {
operatorhashType_Impl508     size_t operator()(const css::uno::Type & s) const
509     { return static_cast<size_t>(s.getTypeName().hashCode()); }
510 };
511 
512 
513 /** Specialized class for key type css::uno::Type,
514     without explicit usage of STL symbols.
515 */
516 class CPPUHELPER_DLLPUBLIC OMultiTypeInterfaceContainerHelper
517 {
518 public:
519     // these are here to force memory de/allocation to sal lib.
new(size_t nSize)520     static void * SAL_CALL operator new( size_t nSize )
521         { return ::rtl_allocateMemory( nSize ); }
delete(void * pMem)522     static void SAL_CALL operator delete( void * pMem )
523         { ::rtl_freeMemory( pMem ); }
new(size_t,void * pMem)524     static void * SAL_CALL operator new( size_t, void * pMem )
525         { return pMem; }
delete(void *,void *)526     static void SAL_CALL operator delete( void *, void * )
527         {}
528 
529     /**
530       Create a container of interface containers.
531 
532       @param rMutex the mutex to protect multi thread access.
533                          The lifetime must be longer than the lifetime
534                          of this object.
535      */
536     OMultiTypeInterfaceContainerHelper( ::osl::Mutex & rMutex );
537     /**
538       Delete all containers.
539      */
540     ~OMultiTypeInterfaceContainerHelper();
541 
542     /**
543       Return all id's under which at least one interface is added.
544      */
545     css::uno::Sequence< css::uno::Type > SAL_CALL getContainedTypes() const;
546 
547     /**
548       Return the container created under this key.
549       @return the container created under this key. If the container
550                  was not created, null was returned.
551      */
552     OInterfaceContainerHelper * SAL_CALL getContainer( const css::uno::Type & rKey ) const;
553 
554     /** Inserts an element into the container with the specified key.
555         The position is not specified, thus it is not specified in which order events are fired.
556 
557         @attention
558         If you add the same interface more than once, then it will be added to the elements list
559         more than once and thus if you want to remove that interface from the list, you have to call
560         removeInterface() the same number of times.
561         In the latter case, you will also get events fired more than once (if the interface is a
562         listener interface).
563 
564         @param rKey
565                the id of the container
566         @param r
567                interface to be added; it is allowed, to insert null or
568                the same interface more than once
569         @return
570                 the new count of elements in the container
571     */
572     sal_Int32 SAL_CALL addInterface(
573         const css::uno::Type & rKey,
574         const css::uno::Reference< css::uno::XInterface > & r );
575 
576     /** Removes an element from the container with the specified key.
577         It uses interface equality to remove the interface.
578 
579         @param rKey
580                the id of the container
581         @param rxIFace
582                interface to be removed
583         @return
584                 the new count of elements in the container
585     */
586     sal_Int32 SAL_CALL removeInterface(
587         const css::uno::Type & rKey,
588         const css::uno::Reference< css::uno::XInterface > & rxIFace );
589 
590     /**
591       Call disposing on all object in the container that
592       support XEventListener. Then clear the container.
593      */
594     void SAL_CALL disposeAndClear( const css::lang::EventObject & rEvt );
595     /**
596       Remove all elements of all containers. Does not delete the container.
597      */
598     void SAL_CALL clear();
599 
600     typedef css::uno::Type keyType;
601 private:
602     void *          m_pMap;
603     ::osl::Mutex &  rMutex;
604 
605     OMultiTypeInterfaceContainerHelper( const OMultiTypeInterfaceContainerHelper & ) SAL_DELETED_FUNCTION;
606     OMultiTypeInterfaceContainerHelper & operator = ( const OMultiTypeInterfaceContainerHelper & ) SAL_DELETED_FUNCTION;
607 };
608 
609 typedef OBroadcastHelperVar< OMultiTypeInterfaceContainerHelper , OMultiTypeInterfaceContainerHelper::keyType > OBroadcastHelper;
610 
611 }
612 
613 #endif
614 
615 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
616