summaryrefslogtreecommitdiff
path: root/include/CommonAPI/DBus/DBusMultiEvent.hpp
blob: 8215d4f3e1e252505294c0a82450cff158bc4b34 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// Copyright (C) 2013-2015 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#if !defined (COMMONAPI_INTERNAL_COMPILATION)
#error "Only <CommonAPI/CommonAPI.hpp> can be included directly, this file may disappear or change contents."
#endif

#ifndef COMMONAPI_DBUS_DBUSMULTIEVENT_HPP_
#define COMMONAPI_DBUS_DBUSMULTIEVENT_HPP_

#include <string>
#include <unordered_map>

#include <CommonAPI/Event.hpp>

namespace CommonAPI {
namespace DBus {

template <typename... _Arguments>
class DBusMultiEvent {
 public:
	typedef std::function<SubscriptionStatus(const std::string&, const _Arguments&...)> Listener;
	typedef std::unordered_multimap<std::string, Listener> ListenersMap;
	typedef typename ListenersMap::iterator Subscription;

	virtual ~DBusMultiEvent() {}

	Subscription subscribeAll(const Listener& listener);
	Subscription subscribe(const std::string& eventName, const Listener& listener);

	void unsubscribe(Subscription listenerSubscription);

 protected:
	SubscriptionStatus notifyListeners(const std::string& name, const _Arguments&... eventArguments);

	virtual void onFirstListenerAdded(const std::string& name, const Listener& listener) { }
	virtual void onListenerAdded(const std::string& name, const Listener& listener) { }

	virtual void onListenerRemoved(const std::string& name, const Listener& listener) { }
	virtual void onLastListenerRemoved(const std::string& name, const Listener& listener) { }

 private:
	typedef std::pair<typename ListenersMap::iterator, typename ListenersMap::iterator> IteratorRange;
	SubscriptionStatus notifyListenersRange(const std::string& name, IteratorRange listenersRange, const _Arguments&... eventArguments);

	ListenersMap listenersMap_;
};

template <typename... _Arguments>
typename DBusMultiEvent<_Arguments...>::Subscription
DBusMultiEvent<_Arguments...>::subscribeAll(const Listener& listener) {
	return subscribe(std::string(), listener);
}

template <typename... _Arguments>
typename DBusMultiEvent<_Arguments...>::Subscription
DBusMultiEvent<_Arguments...>::subscribe(const std::string& eventName, const Listener& listener) {
	const bool firstListenerAdded = listenersMap_.empty();

	auto listenerSubscription = listenersMap_.insert({eventName, listener});

	if (firstListenerAdded) {
		onFirstListenerAdded(eventName, listener);
	}

	onListenerAdded(eventName, listener);

	return listenerSubscription;
}

template <typename... _Arguments>
void DBusMultiEvent<_Arguments...>::unsubscribe(Subscription listenerSubscription) {
	const std::string name = listenerSubscription->first;
	const Listener listener = listenerSubscription->second;

	listenersMap_.erase(listenerSubscription);

	onListenerRemoved(name, listener);

	const bool lastListenerRemoved = listenersMap_.empty();
	if (lastListenerRemoved)
		onLastListenerRemoved(name, listener);
}

template <typename... _Arguments>
SubscriptionStatus DBusMultiEvent<_Arguments...>::notifyListeners(const std::string& name, const _Arguments&... eventArguments) {
	const SubscriptionStatus subscriptionStatus = notifyListenersRange(name, listenersMap_.equal_range(name), eventArguments...);

	if (subscriptionStatus == SubscriptionStatus::CANCEL)
		return SubscriptionStatus::CANCEL;

	return notifyListenersRange(name, listenersMap_.equal_range(std::string()), eventArguments...);
}

template <typename... _Arguments>
SubscriptionStatus DBusMultiEvent<_Arguments...>::notifyListenersRange(
		const std::string& name,
		IteratorRange listenersRange,
		const _Arguments&... eventArguments) {
	for (auto iterator = listenersRange.first; iterator != listenersRange.second; iterator++) {
		const Listener& listener = iterator->second;
		const SubscriptionStatus listenerSubcriptionStatus = listener(name, eventArguments...);

		if (listenerSubcriptionStatus == SubscriptionStatus::CANCEL) {
			auto listenerIterator = iterator;
			listenersMap_.erase(listenerIterator);
		}
	}

	return listenersMap_.empty() ? SubscriptionStatus::CANCEL : SubscriptionStatus::RETAIN;
}

} // namespace DBus
} // namespace CommonAPI

#endif // COMMONAPI_DBUS_DBUSMULTIEVENT_HPP_