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

io.github.pustike.inject.events.ObserverRegistry Maven / Gradle / Ivy

/*
 * Copyright (C) 2016-2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.github.pustike.inject.events;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import io.github.pustike.inject.BindingKey;
import io.github.pustike.inject.utils.ReflectionUtils;

final class ObserverRegistry {
    private final Map> eventObserversMap;

    ObserverRegistry() {
        eventObserversMap = new HashMap<>();
    }

    /**
     * Find all methods in the given class and all its super-classes, that are annotated with {@code @Observes}.
     * @param targetClass the target listener class
     * @return a list of observer methods
     */
    private static Iterable findObserverMethods(Class targetClass) {
        Map observerMethods = new HashMap<>();
        for (Class clazz = targetClass; clazz != null && clazz != Object.class; clazz = clazz.getSuperclass()) {
            for (Method method : ReflectionUtils.getDeclaredMethods(clazz)) {
                if (method.isAnnotationPresent(Observes.class) && !method.isSynthetic()) {
                    Class[] parameterTypes = method.getParameterTypes();
                    if (parameterTypes.length != 1) {
                        String message = "Method %s has @Observes annotation but has %d parameters."
                                + " EventHandler methods must have exactly 1 parameter.";
                        throw new IllegalArgumentException(String.format(message, method, parameterTypes.length));
                    }
                    int hashCode = Objects.hash(method.getName(), parameterTypes);
                    if (!observerMethods.containsKey(hashCode)) {
                        observerMethods.put(hashCode, method);
                    }
                }
            }
        }
        return Collections.unmodifiableCollection(observerMethods.values());
    }

    private static int computeParameterHashCode(Method method) {
        Class parameterClass = method.getParameterTypes()[0];
        Type parameterType = method.getGenericParameterTypes()[0];
        if (parameterType instanceof ParameterizedType) {
            ParameterizedType firstParam = (ParameterizedType) parameterType;
            Type[] typeArguments = firstParam.getActualTypeArguments();
            return Objects.hash(firstParam.getRawType().getTypeName(), typeArguments[0].getTypeName());
        }
        return parameterClass.getName().hashCode();
    }

    private static int computeEventHashCode(Object eventObject) {
        Class eventClass = eventObject.getClass();
        if (eventObject instanceof Event) {
            return Objects.hash(eventClass.getName(), ((Event) eventObject).getSourceType().getName());
        }// REVIEW: doesn't support complex generic events!
        return eventClass.getName().hashCode();
    }

    void register(BindingKey bindingKey, Class targetClass) {
        Iterable observerMethods = findObserverMethods(targetClass);
        for (Method method : observerMethods) {
            int eventHashCode = computeParameterHashCode(method);
            List observerList = eventObserversMap.get(eventHashCode);
            if (observerList == null) {
                observerList = new ArrayList<>();
                eventObserversMap.put(eventHashCode, observerList);
            }
            observerList.add(new Observer(bindingKey, method));
        }
    }

    // REVIEW: should it support event hierarchy?
    Iterator findObservers(Object eventObject) {
        int eventHashCode = computeEventHashCode(eventObject);
        List observers = eventObserversMap.get(eventHashCode);
        return observers == null ? Collections.emptyIterator()
                : Collections.unmodifiableCollection(observers).iterator();
    }

    /**
     * Discards all entries in the cache.
     */
    void invalidateAll() {
        eventObserversMap.clear();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy