11d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert/*
21d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Copyright (C) 2007 The Guava Authors
31d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert *
41d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Licensed under the Apache License, Version 2.0 (the "License");
51d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * you may not use this file except in compliance with the License.
61d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * You may obtain a copy of the License at
71d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert *
81d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * http://www.apache.org/licenses/LICENSE-2.0
91d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert *
101d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Unless required by applicable law or agreed to in writing, software
111d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * distributed under the License is distributed on an "AS IS" BASIS,
121d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * See the License for the specific language governing permissions and
141d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * limitations under the License.
151d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert */
161d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
171d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertpackage com.google.common.eventbus;
181d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
191d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport com.google.common.collect.HashMultimap;
201d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport com.google.common.collect.Multimap;
211d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport java.lang.reflect.Method;
221d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
231d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert/**
241d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * A {@link HandlerFindingStrategy} for collecting all event handler methods
251d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * that are marked with the {@link Subscribe} annotation.
261d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert *
271d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @author Cliff Biffle
281d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert */
291d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertclass AnnotatedHandlerFinder implements HandlerFindingStrategy {
301d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
311d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  /**
321d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * {@inheritDoc}
331d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   *
341d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * This implementation finds all methods marked with a {@link Subscribe}
351d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * annotation.
361d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   */
371d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  @Override
381d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public Multimap<Class<?>, EventHandler> findAllHandlers(Object listener) {
391d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    Multimap<Class<?>, EventHandler> methodsInListener =
401d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        HashMultimap.create();
411d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    Class clazz = listener.getClass();
421d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    while (clazz != null) {
431d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      for (Method method : clazz.getMethods()) {
441d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        Subscribe annotation = method.getAnnotation(Subscribe.class);
451d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
461d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        if (annotation != null) {
471d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          Class<?>[] parameterTypes = method.getParameterTypes();
481d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          if (parameterTypes.length != 1) {
491d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert            throw new IllegalArgumentException(
501d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert                "Method " + method + " has @Subscribe annotation, but requires " +
511d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert                parameterTypes.length + " arguments.  Event handler methods " +
521d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert                "must require a single argument.");
531d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          }
541d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          Class<?> eventType = parameterTypes[0];
551d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          EventHandler handler = makeHandler(listener, method);
561d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
571d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          methodsInListener.put(eventType, handler);
581d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        }
591d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      }
601d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      clazz = clazz.getSuperclass();
611d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
621d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    return methodsInListener;
631d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
641d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
651d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  /**
661d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * Creates an {@code EventHandler} for subsequently calling {@code method} on
671d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * {@code listener}.
681d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * Selects an EventHandler implementation based on the annotations on
691d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * {@code method}.
701d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   *
711d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * @param listener  object bearing the event handler method.
721d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * @param method  the event handler method to wrap in an EventHandler.
731d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * @return an EventHandler that will call {@code method} on {@code listener}
741d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   *         when invoked.
751d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   */
761d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private static EventHandler makeHandler(Object listener, Method method) {
771d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    EventHandler wrapper;
781d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    if (methodIsDeclaredThreadSafe(method)) {
791d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      wrapper = new EventHandler(listener, method);
801d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    } else {
811d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      wrapper = new SynchronizedEventHandler(listener, method);
821d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
831d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    return wrapper;
841d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
851d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
861d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  /**
871d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * Checks whether {@code method} is thread-safe, as indicated by the
881d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * {@link AllowConcurrentEvents} annotation.
891d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   *
901d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * @param method  handler method to check.
911d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * @return {@code true} if {@code handler} is marked as thread-safe,
921d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   *         {@code false} otherwise.
931d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   */
941d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private static boolean methodIsDeclaredThreadSafe(Method method) {
951d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    return method.getAnnotation(AllowConcurrentEvents.class) != null;
961d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
971d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert}
98