util.events: Fix comparison of event_name with nil instead of false (fixes #554)
[prosody.git] / util / events.lua
1 -- Prosody IM
2 -- Copyright (C) 2008-2010 Matthew Wild
3 -- Copyright (C) 2008-2010 Waqas Hussain
4 --
5 -- This project is MIT/X11 licensed. Please see the
6 -- COPYING file in the source package for more information.
7 --
8
9
10 local pairs = pairs;
11 local t_insert = table.insert;
12 local t_remove = table.remove;
13 local t_sort = table.sort;
14 local setmetatable = setmetatable;
15 local next = next;
16
17 local _ENV = nil;
18
19 local function new()
20         -- Map event name to ordered list of handlers (lazily built): handlers[event_name] = array_of_handler_functions
21         local handlers = {};
22         -- Array of wrapper functions that wrap all events (nil if empty)
23         local global_wrappers;
24         -- Per-event wrappers: wrappers[event_name] = wrapper_function
25         local wrappers = {};
26         -- Event map: event_map[handler_function] = priority_number
27         local event_map = {};
28         -- Called on-demand to build handlers entries
29         local function _rebuild_index(handlers, event)
30                 local _handlers = event_map[event];
31                 if not _handlers or next(_handlers) == nil then return; end
32                 local index = {};
33                 for handler in pairs(_handlers) do
34                         t_insert(index, handler);
35                 end
36                 t_sort(index, function(a, b) return _handlers[a] > _handlers[b]; end);
37                 handlers[event] = index;
38                 return index;
39         end;
40         setmetatable(handlers, { __index = _rebuild_index });
41         local function add_handler(event, handler, priority)
42                 local map = event_map[event];
43                 if map then
44                         map[handler] = priority or 0;
45                 else
46                         map = {[handler] = priority or 0};
47                         event_map[event] = map;
48                 end
49                 handlers[event] = nil;
50         end;
51         local function remove_handler(event, handler)
52                 local map = event_map[event];
53                 if map then
54                         map[handler] = nil;
55                         handlers[event] = nil;
56                         if next(map) == nil then
57                                 event_map[event] = nil;
58                         end
59                 end
60         end;
61         local function get_handlers(event)
62                 return handlers[event];
63         end;
64         local function add_handlers(handlers)
65                 for event, handler in pairs(handlers) do
66                         add_handler(event, handler);
67                 end
68         end;
69         local function remove_handlers(handlers)
70                 for event, handler in pairs(handlers) do
71                         remove_handler(event, handler);
72                 end
73         end;
74         local function _fire_event(event_name, event_data)
75                 local h = handlers[event_name];
76                 if h then
77                         for i=1,#h do
78                                 local ret = h[i](event_data);
79                                 if ret ~= nil then return ret; end
80                         end
81                 end
82         end;
83         local function fire_event(event_name, event_data)
84                 local w = wrappers[event_name] or global_wrappers;
85                 if w then
86                         local curr_wrapper = #w;
87                         local function c(event_name, event_data)
88                                 curr_wrapper = curr_wrapper - 1;
89                                 if curr_wrapper == 0 then
90                                         if global_wrappers == nil or w == global_wrappers then
91                                                 return _fire_event(event_name, event_data);
92                                         end
93                                         w, curr_wrapper = global_wrappers, #global_wrappers;
94                                         return w[curr_wrapper](c, event_name, event_data);
95                                 else
96                                         return w[curr_wrapper](c, event_name, event_data);
97                                 end
98                         end
99                         return w[curr_wrapper](c, event_name, event_data);
100                 end
101                 return _fire_event(event_name, event_data);
102         end
103         local function add_wrapper(event_name, wrapper)
104                 local w;
105                 if event_name == false then
106                         w = global_wrappers;
107                         if not w then
108                                 w = {};
109                                 global_wrappers = w;
110                         end
111                 else
112                         w = wrappers[event_name];
113                         if not w then
114                                 w = {};
115                                 wrappers[event_name] = w;
116                         end
117                 end
118                 w[#w+1] = wrapper;
119         end
120         local function remove_wrapper(event_name, wrapper)
121                 local w;
122                 if event_name == false then
123                         w = global_wrappers;
124                 else
125                         w = wrappers[event_name];
126                 end
127                 if not w then return; end
128                 for i = #w, 1 do
129                         if w[i] == wrapper then
130                                 t_remove(w, i);
131                         end
132                 end
133                 if #w == 0 then
134                         if event_name == false then
135                                 global_wrappers = nil;
136                         else
137                                 wrappers[event_name] = nil;
138                         end
139                 end
140         end
141         return {
142                 add_handler = add_handler;
143                 remove_handler = remove_handler;
144                 add_handlers = add_handlers;
145                 remove_handlers = remove_handlers;
146                 get_handlers = get_handlers;
147                 wrappers = {
148                         add_handler = add_wrapper;
149                         remove_handler = remove_wrapper;
150                 };
151                 add_wrapper = add_wrapper;
152                 remove_wrapper = remove_wrapper;
153                 fire_event = fire_event;
154                 _handlers = handlers;
155                 _event_map = event_map;
156         };
157 end
158
159 return {
160         new = new;
161 };