AI2 Component  (Version nb184)
EventDispatcher.java
Go to the documentation of this file.
1 // -*- mode: java; c-basic-offset: 2; -*-
2 // Copyright 2009-2011 Google, All Rights reserved
3 // Copyright 2011-2012 MIT, All rights reserved
4 // Released under the Apache License, Version 2.0
5 // http://www.apache.org/licenses/LICENSE-2.0
6 
7 package com.google.appinventor.components.runtime;
8 
9 import android.util.Log;
10 
11 import java.util.HashMap;
12 import java.util.HashSet;
13 import java.util.Map;
14 import java.util.Set;
15 
22 public class EventDispatcher {
23  private static final class EventClosure {
24  private final String componentId;
25  private final String eventName;
26 
27  private EventClosure(String componentId, String eventName) {
28  this.componentId = componentId;
29  this.eventName = eventName;
30  }
31 
32  @Override
33  public boolean equals(Object o) {
34  if (this == o) {
35  return true;
36  }
37  if (o == null || getClass() != o.getClass()) {
38  return false;
39  }
40 
41  EventClosure that = (EventClosure) o;
42 
43  if (!componentId.equals(that.componentId)) {
44  return false;
45  }
46  if (!eventName.equals(that.eventName)) {
47  return false;
48  }
49 
50  return true;
51  }
52 
53  @Override
54  public int hashCode() {
55  return 31 * eventName.hashCode() + componentId.hashCode();
56  }
57  }
58 
59  /*
60  * Each EventRegistry is associated with one dispatchDelegate.
61  * It contains all the event closures for a single form.
62  */
63  private static final class EventRegistry {
64  private final HandlesEventDispatching dispatchDelegate;
65 
66  // Mapping of event names to a set of event closures.
67  // Note that by using a Set here, we'll only have one closure corresponding to a
68  // given componentId-eventName. We do not support invoking multiple handlers for a
69  // single event.
70  private final HashMap<String, Set<EventClosure>> eventClosuresMap =
71  new HashMap<String, Set<EventClosure>>();
72 
73  EventRegistry(HandlesEventDispatching dispatchDelegate) {
74  this.dispatchDelegate = dispatchDelegate;
75  }
76  }
77 
78  private static final boolean DEBUG = false;
79 
81  mapDispatchDelegateToEventRegistry = new HashMap<HandlesEventDispatching, EventRegistry>();
82 
83  private EventDispatcher() {
84  }
85 
86  private static EventRegistry getEventRegistry(HandlesEventDispatching dispatchDelegate) {
87  EventRegistry er = mapDispatchDelegateToEventRegistry.get(dispatchDelegate);
88  if (er == null) {
89  er = new EventRegistry(dispatchDelegate);
90  mapDispatchDelegateToEventRegistry.put(dispatchDelegate, er);
91  }
92  return er;
93  }
94 
95  private static EventRegistry removeEventRegistry(HandlesEventDispatching dispatchDelegate) {
96  return mapDispatchDelegateToEventRegistry.remove(dispatchDelegate);
97  }
98 
99 
108  // Don't delete this method. It's called from runtime.scm.
109  public static void registerEventForDelegation(HandlesEventDispatching dispatchDelegate,
110  String componentId, String eventName) {
111  EventRegistry er = getEventRegistry(dispatchDelegate);
112  Set<EventClosure> eventClosures = er.eventClosuresMap.get(eventName);
113  if (eventClosures == null) {
114  eventClosures = new HashSet<EventClosure>();
115  er.eventClosuresMap.put(eventName, eventClosures);
116  }
117 
118  eventClosures.add(new EventClosure(componentId, eventName));
119  if (DEBUG) {
120  Log.i("EventDispatcher", "Registered event closure for " +
121  componentId + "." + eventName);
122  }
123  }
124 
133  // Don't delete this method. It's called from runtime.scm.
134  public static void unregisterEventForDelegation(HandlesEventDispatching dispatchDelegate,
135  String componentId, String eventName) {
136  EventRegistry er = getEventRegistry(dispatchDelegate);
137  Set<EventClosure> eventClosures = er.eventClosuresMap.get(eventName);
138  if (eventClosures == null || eventClosures.isEmpty()) {
139  return;
140  }
141  Set<EventClosure> toDelete = new HashSet<EventClosure>();
142  for (EventClosure eventClosure : eventClosures) {
143  if (eventClosure.componentId.equals(componentId)) {
144  toDelete.add(eventClosure);
145  }
146  }
147  for (EventClosure eventClosure : toDelete) {
148  if (DEBUG) {
149  Log.i("EventDispatcher", "Deleting event closure for " +
150  eventClosure.componentId + "." + eventClosure.eventName);
151  }
152  eventClosures.remove(eventClosure);
153  }
154  }
155 
160  // Don't delete this method. It's called from runtime.scm.
161  public static void unregisterAllEventsForDelegation() {
162  for (EventRegistry er : mapDispatchDelegateToEventRegistry.values()) {
163  er.eventClosuresMap.clear();
164  }
165  }
166 
174  public static void removeDispatchDelegate(HandlesEventDispatching dispatchDelegate) {
175  EventRegistry er = removeEventRegistry(dispatchDelegate);
176  if (er != null) {
177  er.eventClosuresMap.clear();
178  }
179  }
180 
188  public static boolean dispatchEvent(Component component, String eventName, Object...args) {
189  if (DEBUG) {
190  Log.i("EventDispatcher", "Trying to dispatch event " + eventName);
191  }
192  boolean dispatched = false;
193  HandlesEventDispatching dispatchDelegate = component.getDispatchDelegate();
194  if (dispatchDelegate.canDispatchEvent(component, eventName)) {
195  EventRegistry er = getEventRegistry(dispatchDelegate);
196  Set<EventClosure> eventClosures = er.eventClosuresMap.get(eventName);
197  if (eventClosures != null && eventClosures.size() > 0) {
198  dispatched = delegateDispatchEvent(dispatchDelegate, eventClosures, component, args);
199  }
200  dispatchDelegate.dispatchGenericEvent(component, eventName, !dispatched, args);
201  }
202  return dispatched;
203  }
204 
212  private static boolean delegateDispatchEvent(HandlesEventDispatching dispatchDelegate,
213  Set<EventClosure> eventClosures,
214  Component component, Object... args) {
215  // The event closures set will contain all event closures matching the event name.
216  // We depend on the delegate's dispatchEvent method to check the registered event closure and
217  // only dispatch the event if the registered component matches the component that generated the
218  // event. This should only be true for one (or zero) of the closures.
219  boolean dispatched = false;
220  for (EventClosure eventClosure : eventClosures) {
221  if (dispatchDelegate.dispatchEvent(component,
222  eventClosure.componentId,
223  eventClosure.eventName,
224  args)) {
225  if (DEBUG) {
226  Log.i("EventDispatcher", "Successfully dispatched event " +
227  eventClosure.componentId + "." + eventClosure.eventName);
228  }
229  dispatched = true; // break here or keep iterating through loop?
230  }
231  }
232  return dispatched;
233  }
234 
235  // Don't delete this method. It's called from runtime.scm.
236  public static String makeFullEventName(String componentId, String eventName) {
237  if (DEBUG) {
238  Log.i("EventDispatcher", "makeFullEventName componentId=" + componentId + ", " +
239  "eventName=" + eventName);
240  }
241  return componentId + '$' + eventName;
242  }
243 }
com.google.appinventor.components.runtime.EventDispatcher
Definition: EventDispatcher.java:22
com.google.appinventor.components.runtime.HandlesEventDispatching
Definition: HandlesEventDispatching.java:15
com.google.appinventor.components.runtime.HandlesEventDispatching.canDispatchEvent
boolean canDispatchEvent(Component component, String eventName)
com.google.appinventor.components.runtime.Component.getDispatchDelegate
HandlesEventDispatching getDispatchDelegate()
com.google.appinventor.components.runtime.EventDispatcher.registerEventForDelegation
static void registerEventForDelegation(HandlesEventDispatching dispatchDelegate, String componentId, String eventName)
Definition: EventDispatcher.java:109
com.google.appinventor.components.runtime.HandlesEventDispatching.dispatchEvent
boolean dispatchEvent(Component component, String componentName, String eventName, Object[] args)
com.google.appinventor.components.runtime.EventDispatcher.removeDispatchDelegate
static void removeDispatchDelegate(HandlesEventDispatching dispatchDelegate)
Definition: EventDispatcher.java:174
com.google.appinventor.components.runtime.EventDispatcher.dispatchEvent
static boolean dispatchEvent(Component component, String eventName, Object...args)
Definition: EventDispatcher.java:188
com.google.appinventor.components.runtime.EventDispatcher.unregisterAllEventsForDelegation
static void unregisterAllEventsForDelegation()
Definition: EventDispatcher.java:161
com.google.appinventor.components.runtime.Component
Definition: Component.java:17
com.google.appinventor.components.runtime.Map
Definition: Map.java:84
com.google.appinventor.components.runtime.EventDispatcher.makeFullEventName
static String makeFullEventName(String componentId, String eventName)
Definition: EventDispatcher.java:236
com.google.appinventor.components.runtime.HandlesEventDispatching.dispatchGenericEvent
void dispatchGenericEvent(Component component, String eventName, boolean notAlreadyHandled, Object[] args)
com.google.appinventor.components.runtime.EventDispatcher.unregisterEventForDelegation
static void unregisterEventForDelegation(HandlesEventDispatching dispatchDelegate, String componentId, String eventName)
Definition: EventDispatcher.java:134