summaryrefslogtreecommitdiff
path: root/include/CommonAPI/DBus/DBusMultiEvent.hpp
blob: cd9be83a24241bcdc58bf32329a5e9a8ef3d9e8d (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-2020 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_