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 package org.vectomatic.dom.svg.impl;
34
35 import java.util.ArrayList;
36 import java.util.Collections;
37 import java.util.HashMap;
38 import java.util.HashSet;
39 import java.util.List;
40 import java.util.ListIterator;
41 import java.util.Map;
42 import java.util.Set;
43
44 import org.vectomatic.dom.svg.OMElement;
45 import org.vectomatic.dom.svg.utils.DOMHelper;
46
47 import com.google.gwt.dom.client.Element;
48 import com.google.gwt.event.dom.client.DomEvent;
49 import com.google.gwt.event.shared.EventBus;
50 import com.google.gwt.event.shared.EventHandler;
51 import com.google.gwt.event.shared.GwtEvent;
52 import com.google.gwt.event.shared.GwtEvent.Type;
53 import com.google.gwt.event.shared.HandlerRegistration;
54 import com.google.web.bindery.event.shared.Event;
55 import com.google.web.bindery.event.shared.UmbrellaException;
56
57
58
59
60
61
62
63 public class DOMEventBus extends EventBus {
64 private interface Command {
65 void execute();
66 }
67
68 private int firingDepth = 0;
69
70
71
72
73 private List<Command> deferredDeltas;
74
75
76
77
78 private final Map<Event.Type<?>, Map<Object, List<?>>> map = new HashMap<Event.Type<?>, Map<Object, List<?>>>();
79
80 @Override
81 public <H extends EventHandler> HandlerRegistration addHandler(Type<H> type, H handler) {
82 return doAdd(type, null, handler);
83 }
84
85 @Override
86 public <H extends EventHandler> HandlerRegistration addHandlerToSource(Type<H> type, Object source, H handler) {
87 if (source == null) {
88 throw new NullPointerException("Cannot add a handler with a null source");
89 }
90 return doAdd(type, source, handler);
91 }
92
93 @Override
94 public void fireEvent(GwtEvent<?> event) {
95 doFire(event, null);
96 }
97
98 @Override
99 public void fireEventFromSource(GwtEvent<?> event, Object source) {
100 if (source == null) {
101 throw new NullPointerException("Cannot fire from a null source");
102 }
103 doFire(event, source);
104 }
105
106
107 @Override
108 public <H> HandlerRegistration addHandlerToSource(final Event.Type<H> type,
109 final Object source, final H handler) {
110 if (source == null) {
111 throw new NullPointerException("Cannot add a handler with a null source");
112 }
113 return doAdd(type, source, handler);
114 }
115
116 protected <H> void doRemove(Event.Type<H> type, Object source, H handler) {
117 if (firingDepth > 0) {
118 enqueueRemove(type, source, handler);
119 } else {
120 doRemoveNow(type, source, handler);
121 }
122 }
123
124 protected <H> H getHandler(Event.Type<H> type, int index) {
125 assert index < getHandlerCount(type) : "handlers for "
126 + type.getClass() + " have size: " + getHandlerCount(type)
127 + " so do not have a handler at index: " + index;
128
129 List<H> l = getHandlerList(type, null);
130 return l.get(index);
131 }
132
133 protected int getHandlerCount(Event.Type<?> eventKey) {
134 return getHandlerList(eventKey, null).size();
135 }
136
137 protected boolean isEventHandled(Event.Type<?> eventKey) {
138 return map.containsKey(eventKey);
139 }
140
141 private void defer(Command command) {
142 if (deferredDeltas == null) {
143 deferredDeltas = new ArrayList<Command>();
144 }
145 deferredDeltas.add(command);
146 }
147
148 private <H> HandlerRegistration doAdd(final Event.Type<H> type,
149 final Object source, final H handler) {
150 if (type == null) {
151 throw new NullPointerException(
152 "Cannot add a handler with a null type");
153 }
154 if (handler == null) {
155 throw new NullPointerException("Cannot add a null handler");
156 }
157
158 if (firingDepth > 0) {
159 enqueueAdd(type, source, handler);
160 } else {
161 doAddNow(type, source, handler);
162 }
163
164 return new HandlerRegistration() {
165 public void removeHandler() {
166 doRemove(type, source, handler);
167 }
168 };
169 }
170
171 private <H> void doAddNow(Event.Type<H> type, Object source, H handler) {
172 List<H> l = ensureHandlerList(type, source);
173 l.add(handler);
174 }
175
176 private <H> void doFire(Event<H> event, Object source) {
177 if (event == null) {
178 throw new NullPointerException("Cannot fire null event");
179 }
180 try {
181 firingDepth++;
182
183 if (source != null) {
184 setSourceOfEvent(event, source);
185 }
186
187 List<H> handlers = getDispatchList(event.getAssociatedType(), source);
188 Set<Throwable> causes = null;
189
190 ListIterator<H> it = handlers.listIterator();
191 while (it.hasNext()) {
192 H handler = it.next();
193
194 try {
195 dispatchEvent(event, handler);
196 } catch (Throwable e) {
197 if (causes == null) {
198 causes = new HashSet<Throwable>();
199 }
200 causes.add(e);
201 }
202 }
203
204 if (causes != null) {
205 throw new UmbrellaException(causes);
206 }
207 } finally {
208 firingDepth--;
209 if (firingDepth == 0) {
210 handleQueuedAddsAndRemoves();
211 }
212 }
213 }
214
215 private <H> void doRemoveNow(Event.Type<H> type, Object source, H handler) {
216 List<H> l = getHandlerList(type, source);
217
218 boolean removed = l.remove(handler);
219 assert removed : "redundant remove call";
220 if (removed && l.isEmpty()) {
221 if (type instanceof DomEvent.Type && source instanceof OMElement) {
222 Element elem = ((OMElement)source).getElement();
223 String eventName = ((DomEvent.Type)type).getName();
224 DOMHelper.unbindEventListener(elem, eventName);
225 }
226 prune(type, source);
227 }
228 }
229
230 private <H> void enqueueAdd(final Event.Type<H> type, final Object source, final H handler) {
231 defer(new Command() {
232 public void execute() {
233 doAddNow(type, source, handler);
234 }
235 });
236 }
237
238 private <H> void enqueueRemove(final Event.Type<H> type, final Object source, final H handler) {
239 defer(new Command() {
240 public void execute() {
241 doRemoveNow(type, source, handler);
242 }
243 });
244 }
245
246 private <H> List<H> ensureHandlerList(Event.Type<H> type, Object source) {
247 Map<Object, List<?>> sourceMap = map.get(type);
248 if (sourceMap == null) {
249 sourceMap = new HashMap<Object, List<?>>();
250 map.put(type, sourceMap);
251 }
252
253
254 @SuppressWarnings("unchecked")
255 List<H> handlers = (List<H>) sourceMap.get(source);
256 if (handlers == null) {
257 handlers = new ArrayList<H>();
258 sourceMap.put(source, handlers);
259 }
260
261 return handlers;
262 }
263
264 private <H> List<H> getDispatchList(Event.Type<H> type, Object source) {
265 List<H> directHandlers = getHandlerList(type, source);
266 if (source == null) {
267 return directHandlers;
268 }
269
270 List<H> globalHandlers = getHandlerList(type, null);
271
272 List<H> rtn = new ArrayList<H>(directHandlers);
273 rtn.addAll(globalHandlers);
274 return rtn;
275 }
276
277 private <H> List<H> getHandlerList(Event.Type<H> type, Object source) {
278 Map<Object, List<?>> sourceMap = map.get(type);
279 if (sourceMap == null) {
280 return Collections.emptyList();
281 }
282
283
284 @SuppressWarnings("unchecked")
285 List<H> handlers = (List<H>) sourceMap.get(source);
286 if (handlers == null) {
287 return Collections.emptyList();
288 }
289
290 return handlers;
291 }
292
293 private void handleQueuedAddsAndRemoves() {
294 if (deferredDeltas != null) {
295 try {
296 for (Command c : deferredDeltas) {
297 c.execute();
298 }
299 } finally {
300 deferredDeltas = null;
301 }
302 }
303 }
304
305 private void prune(Event.Type<?> type, Object source) {
306 Map<Object, List<?>> sourceMap = map.get(type);
307
308 List<?> pruned = sourceMap.remove(source);
309
310 assert pruned != null : "Can't prune what wasn't there";
311 assert pruned.isEmpty() : "Pruned unempty list!";
312
313 if (sourceMap.isEmpty()) {
314 map.remove(type);
315 }
316 }
317
318 }