You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

EventRouter.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. /*
  2. * Copyright 2000-2016 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.event;
  17. import java.lang.reflect.Method;
  18. import java.util.ArrayList;
  19. import java.util.Collection;
  20. import java.util.EventObject;
  21. import java.util.Iterator;
  22. import java.util.LinkedHashSet;
  23. import java.util.List;
  24. import java.util.Objects;
  25. import com.vaadin.server.ErrorEvent;
  26. import com.vaadin.server.ErrorHandler;
  27. import com.vaadin.shared.Registration;
  28. import com.vaadin.shared.communication.SharedState;
  29. import com.vaadin.shared.ui.ComponentStateUtil;
  30. /**
  31. * <code>EventRouter</code> class implementing the inheritable event listening
  32. * model. For more information on the event model see the
  33. * {@link com.vaadin.event package documentation}.
  34. *
  35. * @author Vaadin Ltd.
  36. * @since 3.0
  37. */
  38. @SuppressWarnings("serial")
  39. public class EventRouter implements MethodEventSource {
  40. /**
  41. * List of registered listeners.
  42. */
  43. private LinkedHashSet<ListenerMethod> listenerList = null;
  44. /*
  45. * Registers a new listener with the specified activation method to listen
  46. * events generated by this component. Don't add a JavaDoc comment here, we
  47. * use the default documentation from implemented interface.
  48. */
  49. @Override
  50. public Registration addListener(Class<?> eventType, Object object,
  51. Method method) {
  52. Objects.requireNonNull(object, "Listener must not be null.");
  53. if (listenerList == null) {
  54. listenerList = new LinkedHashSet<>();
  55. }
  56. ListenerMethod listenerMethod = new ListenerMethod(eventType, object,
  57. method);
  58. listenerList.add(listenerMethod);
  59. return () -> listenerList.remove(listenerMethod);
  60. }
  61. /**
  62. * Registers a new event listener with the specified activation method to
  63. * listen events generated by this component. If the activation method does
  64. * not have any arguments the event object will not be passed to it when
  65. * it's called.
  66. *
  67. * <p>
  68. * This method additionally informs the event-api to stop routing events
  69. * with the given {@code eventIdentifier} to the components handleEvent
  70. * function call.
  71. * </p>
  72. *
  73. * <p>
  74. * The only way to remove the listener is to use the returned
  75. * {@link Registration}. The other methods, e.g.
  76. * {@link #removeAllListeners()} do not do that.
  77. * </p>
  78. *
  79. * <p>
  80. * For more information on the inheritable event mechanism see the
  81. * {@link com.vaadin.event com.vaadin.event package documentation}.
  82. * </p>
  83. *
  84. * @param eventType
  85. * the type of the listened event. Events of this type or its
  86. * subclasses activate the listener.
  87. * @param target
  88. * the object instance who owns the activation method.
  89. * @param method
  90. * the activation method.
  91. * @param eventIdentifier
  92. * the identifier of the event to listen for
  93. * @param state
  94. * The component State
  95. * @return a registration object for removing the listener
  96. * @throws IllegalArgumentException
  97. * unless {@code method} has exactly one match in {@code target}
  98. * @throws NullPointerException
  99. * if {@code target} is {@code null}
  100. * @since
  101. */
  102. public Registration addListener(Class<?> eventType, Object target,
  103. Method method, String eventIdentifier, SharedState state) {
  104. Objects.requireNonNull(target, "Listener must not be null.");
  105. if (listenerList == null) {
  106. listenerList = new LinkedHashSet<>();
  107. }
  108. ListenerMethod listenerMethod = new ListenerMethod(eventType, target,
  109. method);
  110. listenerList.add(listenerMethod);
  111. Registration registration = ComponentStateUtil
  112. .addRegisteredEventListener(state, eventIdentifier);
  113. return () -> {
  114. registration.remove();
  115. listenerList.remove(listenerMethod);
  116. };
  117. }
  118. /*
  119. * Registers a new listener with the specified named activation method to
  120. * listen events generated by this component. Don't add a JavaDoc comment
  121. * here, we use the default documentation from implemented interface.
  122. */
  123. @Override
  124. public Registration addListener(Class<?> eventType, Object object,
  125. String methodName) {
  126. Objects.requireNonNull(object, "Listener must not be null.");
  127. if (listenerList == null) {
  128. listenerList = new LinkedHashSet<>();
  129. }
  130. ListenerMethod listenerMethod = new ListenerMethod(eventType, object,
  131. methodName);
  132. listenerList.add(listenerMethod);
  133. return () -> listenerList.remove(listenerMethod);
  134. }
  135. /*
  136. * Removes all registered listeners matching the given parameters. Don't add
  137. * a JavaDoc comment here, we use the default documentation from implemented
  138. * interface.
  139. */
  140. @Override
  141. public void removeListener(Class<?> eventType, Object target) {
  142. if (listenerList != null) {
  143. final Iterator<ListenerMethod> i = listenerList.iterator();
  144. while (i.hasNext()) {
  145. final ListenerMethod lm = i.next();
  146. if (lm.matches(eventType, target)) {
  147. i.remove();
  148. return;
  149. }
  150. }
  151. }
  152. }
  153. /*
  154. * Removes the event listener methods matching the given given parameters.
  155. * Don't add a JavaDoc comment here, we use the default documentation from
  156. * implemented interface.
  157. */
  158. @Override
  159. public void removeListener(Class<?> eventType, Object target,
  160. Method method) {
  161. if (listenerList != null) {
  162. final Iterator<ListenerMethod> i = listenerList.iterator();
  163. while (i.hasNext()) {
  164. final ListenerMethod lm = i.next();
  165. if (lm.matches(eventType, target, method)) {
  166. i.remove();
  167. return;
  168. }
  169. }
  170. }
  171. }
  172. /*
  173. * Removes the event listener method matching the given given parameters.
  174. * Don't add a JavaDoc comment here, we use the default documentation from
  175. * implemented interface.
  176. */
  177. @Override
  178. public void removeListener(Class<?> eventType, Object target,
  179. String methodName) {
  180. // Find the correct method
  181. final Method[] methods = target.getClass().getMethods();
  182. Method method = null;
  183. for (Method m : methods) {
  184. if (m.getName().equals(methodName)) {
  185. method = m;
  186. break;
  187. }
  188. }
  189. if (method == null) {
  190. throw new IllegalArgumentException();
  191. }
  192. // Remove the listeners
  193. if (listenerList != null) {
  194. final Iterator<ListenerMethod> i = listenerList.iterator();
  195. while (i.hasNext()) {
  196. final ListenerMethod lm = i.next();
  197. if (lm.matches(eventType, target, method)) {
  198. i.remove();
  199. return;
  200. }
  201. }
  202. }
  203. }
  204. /**
  205. * Removes all listeners from event router.
  206. */
  207. public void removeAllListeners() {
  208. listenerList = null;
  209. }
  210. /**
  211. * Sends an event to all registered listeners. The listeners will decide if
  212. * the activation method should be called or not.
  213. *
  214. * @param event
  215. * the Event to be sent to all listeners.
  216. */
  217. public void fireEvent(EventObject event) {
  218. fireEvent(event, null);
  219. }
  220. /**
  221. * Sends an event to all registered listeners. The listeners will decide if
  222. * the activation method should be called or not.
  223. * <p>
  224. * If an error handler is set, the processing of other listeners will
  225. * continue after the error handler method call unless the error handler
  226. * itself throws an exception.
  227. *
  228. * @param event
  229. * the Event to be sent to all listeners.
  230. * @param errorHandler
  231. * error handler to use to handle any exceptions thrown by
  232. * listeners or null to let the exception propagate to the
  233. * caller, preventing further listener calls
  234. */
  235. public void fireEvent(EventObject event, ErrorHandler errorHandler) {
  236. // It is not necessary to send any events if there are no listeners
  237. if (listenerList != null) {
  238. // Make a copy of the listener list to allow listeners to be added
  239. // inside listener methods. Fixes #3605.
  240. // Send the event to all listeners. The listeners themselves
  241. // will filter out unwanted events.
  242. for (Object l : listenerList.toArray()) {
  243. ListenerMethod listenerMethod = (ListenerMethod) l;
  244. if (null != errorHandler) {
  245. try {
  246. listenerMethod.receiveEvent(event);
  247. } catch (Exception e) {
  248. errorHandler.error(new ErrorEvent(e));
  249. }
  250. } else {
  251. listenerMethod.receiveEvent(event);
  252. }
  253. }
  254. }
  255. }
  256. /**
  257. * Checks if the given Event type is listened by a listener registered to
  258. * this router.
  259. *
  260. * @param eventType
  261. * the event type to be checked
  262. * @return true if a listener is registered for the given event type
  263. */
  264. public boolean hasListeners(Class<?> eventType) {
  265. if (listenerList != null) {
  266. for (ListenerMethod lm : listenerList) {
  267. if (lm.isType(eventType)) {
  268. return true;
  269. }
  270. }
  271. }
  272. return false;
  273. }
  274. /**
  275. * Returns all listeners that match or extend the given event type.
  276. *
  277. * @param eventType
  278. * The type of event to return listeners for.
  279. * @return A collection with all registered listeners. Empty if no listeners
  280. * are found.
  281. */
  282. public Collection<?> getListeners(Class<?> eventType) {
  283. List<Object> listeners = new ArrayList<>();
  284. if (listenerList != null) {
  285. for (ListenerMethod lm : listenerList) {
  286. if (lm.isOrExtendsType(eventType)) {
  287. listeners.add(lm.getTarget());
  288. }
  289. }
  290. }
  291. return listeners;
  292. }
  293. }