All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.divide.otto.SubscriberHandlerFinder Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2014 Divide.io
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 */

package io.divide.otto;

import io.divide.shared.event.Event;
import io.divide.shared.event.Subscriber;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class SubscriberHandlerFinder implements HandlerFinder {

    private static final Map, Map, Method>> PRODUCERS_CACHE =
            new HashMap, Map, Method>>();

    /** Cache event bus subscriber methods for each class. */
    private static final Map, Map, Set>> SUBSCRIBERS_CACHE =
            new HashMap, Map, Set>>();

    @Override
    public Map, EventProducer> findAllProducers(Object listener) {
        final Class listenerClass = listener.getClass();
        Map, EventProducer> handlersInMethod = new HashMap, EventProducer>();

        if (!PRODUCERS_CACHE.containsKey(listenerClass)) {
            loadProducers(listenerClass);
        }
        Map, Method> methods = PRODUCERS_CACHE.get(listenerClass);
        if (!methods.isEmpty()) {
            for (Map.Entry, Method> e : methods.entrySet()) {
                EventProducer producer = new EventProducer(listener, e.getValue());
                handlersInMethod.put(e.getKey(), producer);
            }
        }

        return handlersInMethod;
    }

    @Override
    public Map, Set> findAllSubscribers(Object listener) {
        Class listenerClass = listener.getClass();
        Map, Set> handlersInMethod = new HashMap, Set>();

        if (!SUBSCRIBERS_CACHE.containsKey(listenerClass)) {
            loadSubscribers(listenerClass);
        }
        Map, Set> methods = SUBSCRIBERS_CACHE.get(listenerClass);
        if (!methods.isEmpty()) {
            for (Map.Entry, Set> e : methods.entrySet()) {
                Set handlers = new HashSet();
                for (Method m : e.getValue()) {
                    handlers.add(new EventHandler(listener, m));
                }
                handlersInMethod.put(e.getKey(), handlers);
            }
        }

        return handlersInMethod;
    }

    private static boolean isSubscriberMethod(Method m){
        if(!m.getName().equals("onEvent"))return false;
        Class param = m.getParameterTypes()[0];
        if(!Event.class.isAssignableFrom(param) || param.equals(Event.class))return false;
        return true;
    }

    private static boolean isSubscriberClass(Class clazz){
        if(clazz.equals(Subscriber.class) || Subscriber.class.isAssignableFrom(clazz))return true;
        return false;
    }

    private static void loadSubscribers(Class listenerClass) {
        Map, Set> subscriberMethods = new HashMap, Set>();

        if(!isSubscriberClass(listenerClass))return; // no need checking this for each method...

        for (Method method : listenerClass.getDeclaredMethods()) {
            if (isSubscriberMethod(method)) {
                Class[] parameterTypes = method.getParameterTypes();
                if (parameterTypes.length != 1) {
                    throw new IllegalArgumentException("Method " + method + " has @Subscribe annotation but requires "
                            + parameterTypes.length + " arguments.  Methods must require a single argument.");
                }

                Class eventType = parameterTypes[0];
                if (eventType.isInterface()) {
                    throw new IllegalArgumentException("Method " + method + " has @Subscribe annotation on " + eventType
                            + " which is an interface.  Subscription must be on a concrete class type.");
                }

                if ((method.getModifiers() & Modifier.PUBLIC) == 0) {
                    throw new IllegalArgumentException("Method " + method + " has @Subscribe annotation on " + eventType
                            + " but is not 'public'.");
                }

                System.out.println("EventType: " + eventType);
                Set methods = subscriberMethods.get(eventType);
                if (methods == null) {
                    methods = new HashSet();
                    subscriberMethods.put(eventType, methods);
                }
                methods.add(method);
            }
        }

        SUBSCRIBERS_CACHE.put(listenerClass, subscriberMethods);
    }

    private static void loadProducers(Class listenerClass) {
        Map, Method> producerMethods = new HashMap, Method>();

        for (Method method : listenerClass.getDeclaredMethods()) {
            if (method.isAnnotationPresent(Produce.class)) {
                Class[] parameterTypes = method.getParameterTypes();
                if (parameterTypes.length != 0) {
                    throw new IllegalArgumentException("Method " + method + "has @Produce annotation but requires "
                            + parameterTypes.length + " arguments.  Methods must require zero arguments.");
                }
                if (method.getReturnType() == Void.class) {
                    throw new IllegalArgumentException("Method " + method
                            + " has a return type of void.  Must declare a non-void type.");
                }

                Class eventType = method.getReturnType();
                if (eventType.isInterface()) {
                    throw new IllegalArgumentException("Method " + method + " has @Produce annotation on " + eventType
                            + " which is an interface.  Producers must return a concrete class type.");
                }
                if (eventType.equals(Void.TYPE)) {
                    throw new IllegalArgumentException("Method " + method + " has @Produce annotation but has no return type.");
                }

                if ((method.getModifiers() & Modifier.PUBLIC) == 0) {
                    throw new IllegalArgumentException("Method " + method + " has @Produce annotation on " + eventType
                            + " but is not 'public'.");
                }

                if (producerMethods.containsKey(eventType)) {
                    throw new IllegalArgumentException("Producer for type " + eventType + " has already been registered.");
                }
                producerMethods.put(eventType, method);
            }
        }

        PRODUCERS_CACHE.put(listenerClass, producerMethods);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy