crashtesting: assert on reimport of docx export of ooo102874-2.doc
[LibreOffice.git] / slideshow / source / inc / listenercontainer.hxx
blobaf40fb3e6467c252f383630ad4d0709290d23088
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 .
19 #ifndef INCLUDED_SLIDESHOW_SOURCE_INC_LISTENERCONTAINER_HXX
20 #define INCLUDED_SLIDESHOW_SOURCE_INC_LISTENERCONTAINER_HXX
22 #include <algorithm>
23 #include <memory>
24 #include <vector>
25 #include <iterator>
27 namespace slideshow::internal {
29 struct EmptyBase
31 struct EmptyGuard{ explicit EmptyGuard(EmptyBase) {} };
32 struct EmptyClearableGuard
34 explicit EmptyClearableGuard(EmptyBase) {}
35 static void clear() {}
38 typedef EmptyGuard Guard;
39 typedef EmptyClearableGuard ClearableGuard;
42 template< typename result_type, typename ListenerTargetT > struct FunctionApply
44 template<typename FuncT> static bool apply(
45 FuncT func,
46 ListenerTargetT const& rArg )
48 return func(rArg);
52 template<typename ListenerTargetT> struct FunctionApply<void,ListenerTargetT>
54 template<typename FuncT> static bool apply(
55 FuncT func,
56 ListenerTargetT const& rArg )
58 func(rArg);
59 return true;
63 template< typename ListenerT > struct ListenerOperations
65 /// Notify a single one of the listeners
66 template< typename ContainerT,
67 typename FuncT >
68 static bool notifySingleListener( ContainerT& rContainer,
69 FuncT func )
71 // true: a handler in this queue processed the event
72 // false: no handler in this queue finally processed the event
73 return std::any_of( rContainer.begin(),
74 rContainer.end(),
75 func );
78 /// Notify all listeners
79 template< typename ContainerT,
80 typename FuncT >
81 static bool notifyAllListeners( ContainerT& rContainer,
82 FuncT func )
84 bool bRet(false);
85 for( const auto& rCurr : rContainer )
87 if( FunctionApply< typename ::std::invoke_result< FuncT, const typename ContainerT::value_type& >::type,
88 typename ContainerT::value_type >::apply(
89 func,
90 rCurr) )
92 bRet = true;
96 // true: at least one handler returned true
97 // false: not a single handler returned true
98 return bRet;
101 /// Prune container from deceased listeners
102 template< typename ContainerT >
103 static void pruneListeners( ContainerT&, size_t )
108 template< typename ListenerTargetT >
109 struct ListenerOperations< std::weak_ptr<ListenerTargetT> >
111 template< typename ContainerT,
112 typename FuncT >
113 static bool notifySingleListener( ContainerT& rContainer,
114 FuncT func )
116 for( const auto& rCurr : rContainer )
118 std::shared_ptr<ListenerTargetT> pListener( rCurr.lock() );
120 if( pListener && func(pListener) )
121 return true;
124 return false;
127 template< typename ContainerT,
128 typename FuncT >
129 static bool notifyAllListeners( ContainerT& rContainer,
130 FuncT func )
132 bool bRet(false);
133 for( const auto& rCurr : rContainer )
135 std::shared_ptr<ListenerTargetT> pListener( rCurr.lock() );
137 if( pListener.get() &&
138 FunctionApply<typename ::std::invoke_result<FuncT, std::shared_ptr<ListenerTargetT> const&>::type,
139 std::shared_ptr<ListenerTargetT> >::apply(func,pListener) )
141 bRet = true;
145 return bRet;
147 template< typename ContainerT >
148 static void pruneListeners( ContainerT& rContainer,
149 size_t nSizeThreshold )
151 if( rContainer.size() <= nSizeThreshold )
152 return;
154 ContainerT aAliveListeners;
155 aAliveListeners.reserve(rContainer.size());
157 for( const auto& rCurr : rContainer )
159 if( !rCurr.expired() )
160 aAliveListeners.push_back( rCurr );
163 std::swap( rContainer, aAliveListeners );
167 /** Container for objects that can be notified.
169 This templatized container holds listener objects, then can get
170 notified (by calling certain methods on them).
172 @tpl Listener
173 Type for the listener objects to be held
175 @tpl ContainerT
176 Full type of the container to store the listener objects. Defaults
177 to std::vector<ListenerT>
179 @tpl MaxDeceasedListenerUllage
180 Threshold, from which upwards the listener container gets
181 pruned. Avoids frequent copying of nearly empty containers.
183 @attention internal class, not to be used. functionality is
184 incomplete, please use the Thread(Un)safeListenerContainer types
185 from below
187 template< typename ListenerT,
188 typename MutexHolderBaseT,
189 typename ContainerT=std::vector<ListenerT>,
190 size_t MaxDeceasedListenerUllage=16 > class ListenerContainerBase : public MutexHolderBaseT
192 typedef typename MutexHolderBaseT::Guard Guard;
193 typedef typename MutexHolderBaseT::ClearableGuard ClearableGuard;
195 public:
196 typedef ListenerT listener_type;
197 typedef ContainerT container_type;
199 /** Check whether listener container is empty
201 @return true, if currently no listeners registered. Note that
202 in a multi-threaded scenario, without external synchronisation
203 to this object, the return value might become wrong at any time.
205 bool isEmpty() const
207 Guard aGuard(*this);
208 return maListeners.empty();
211 /** Check whether given listener is already added
213 @return true, if given listener is already added.
215 bool isAdded( listener_type const& rListener ) const
217 Guard aGuard(*this);
219 const typename container_type::const_iterator aEnd( maListeners.end() );
220 if( std::find( maListeners.begin(),
221 aEnd,
222 rListener ) != aEnd )
224 return true; // already added
227 return false;
230 /** Add new listener
232 @param rListener
233 Listener to add
235 void add( listener_type const& rListener )
237 Guard aGuard(*this);
239 // ensure uniqueness
240 if( isAdded(rListener) )
241 return; // already added
243 maListeners.push_back( rListener );
245 ListenerOperations<ListenerT>::pruneListeners(
246 maListeners,
247 MaxDeceasedListenerUllage);
250 /** Add new listener into sorted container
252 The stored listeners are kept sorted (using this method
253 requires listener_type to have operator< defined on it). Make
254 sure to call addSorted() for <em>each</em> listener to add to
255 this container - sorting is performed under the assumption
256 that existing entries are already sorted.
258 @param rListener
259 Listener to add
261 @return false, if the listener is already added, true
262 otherwise
264 bool addSorted( listener_type const& rListener )
266 Guard aGuard(*this);
268 // ensure uniqueness
269 if( isAdded(rListener) )
270 return false; // already added
272 maListeners.push_back( rListener );
274 // a single entry does not need to be sorted
275 if( maListeners.size() > 1 )
277 std::inplace_merge(
278 maListeners.begin(),
279 std::prev(maListeners.end()),
280 maListeners.end() );
283 ListenerOperations<ListenerT>::pruneListeners(
284 maListeners,
285 MaxDeceasedListenerUllage);
287 return true;
290 /** Remove listener from container
292 @param rListener
293 The listener to remove
295 @return false, if listener not found in container, true
296 otherwise
298 bool remove( listener_type const& rListener )
300 Guard aGuard(*this);
302 // TODO : needed for the moment since ANDROID doesn't know size_t return from std::erase
303 #if defined ANDROID
304 const typename container_type::iterator aEnd( maListeners.end() );
305 typename container_type::iterator aIter;
306 if( (aIter=std::remove(maListeners.begin(),
307 aEnd,
308 rListener)) == aEnd )
310 return false; // listener not found
313 maListeners.erase( aIter, aEnd );
315 return true;
316 #else
317 size_t nb = std::erase(maListeners, rListener);
318 // if nb == 0, it means listener wasn't found
319 return (nb != 0);
320 #endif
323 /// Removes all listeners in one go
324 void clear()
326 Guard aGuard(*this);
328 maListeners.clear();
331 /** Apply functor to one listener
333 This method applies functor to one of the listeners. Starting
334 with the first entry of the container, the functor is called
335 with the listener entries, until it returns true.
337 @param func
338 Given functor is called with listeners, until it returns true
340 @return true, if functor was successfully applied to a
341 listener
343 template< typename FuncT > bool apply( FuncT func ) const
345 ClearableGuard aGuard(*this);
347 // generate a local copy of all handlers, to make method
348 // reentrant and thread-safe.
349 container_type const local( maListeners );
350 aGuard.clear();
352 const bool bRet(
353 ListenerOperations<ListenerT>::notifySingleListener(
354 local,
355 func ));
358 Guard aGuard2(*this);
359 ListenerOperations<ListenerT>::pruneListeners(
360 const_cast<container_type&>(maListeners),
361 MaxDeceasedListenerUllage);
364 return bRet;
367 /** Apply functor to all listeners
369 This method applies functor to all of the listeners. Starting
370 with the first entry of the container, the functor is called
371 with the listener entries.
373 @param func
374 Given functor is called with listeners.
376 @return true, if functor was successfully applied to at least
377 one listener
379 template< typename FuncT > bool applyAll( FuncT func ) const
381 ClearableGuard aGuard(*this);
383 // generate a local copy of all handlers, to make method
384 // reentrant and thread-safe.
385 container_type const local( maListeners );
386 aGuard.clear();
388 const bool bRet(
389 ListenerOperations<ListenerT>::notifyAllListeners(
390 local,
391 func ));
394 Guard aGuard2(*this);
395 ListenerOperations<ListenerT>::pruneListeners(
396 const_cast<container_type&>(maListeners),
397 MaxDeceasedListenerUllage);
400 return bRet;
403 private:
404 ContainerT maListeners;
408 /** ListenerContainer variant that does not serialize access
410 This ListenerContainer version is not safe to use in a
411 multi-threaded scenario, but has less overhead.
413 template< typename ListenerT,
414 typename ContainerT=std::vector<ListenerT> >
415 class ThreadUnsafeListenerContainer : public ListenerContainerBase<ListenerT,
416 EmptyBase,
417 ContainerT>
421 } // namespace slideshow::internal
423 #endif // INCLUDED_SLIDESHOW_SOURCE_INC_LISTENERCONTAINER_HXX
425 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */