001/*
002 * JGrapes Event Driven Framework
003 * Copyright (C) 2016-2018 Michael N. Lipp
004 * 
005 * This program is free software; you can redistribute it and/or modify it 
006 * under the terms of the GNU Affero General Public License as published by 
007 * the Free Software Foundation; either version 3 of the License, or 
008 * (at your option) any later version.
009 * 
010 * This program is distributed in the hope that it will be useful, but 
011 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
012 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License 
013 * for more details.
014 * 
015 * You should have received a copy of the GNU Affero General Public License along 
016 * with this program; if not, see <http://www.gnu.org/licenses/>.
017 */
018
019package org.jgrapes.core.annotation;
020
021import java.lang.annotation.Annotation;
022import java.lang.annotation.Documented;
023import java.lang.annotation.ElementType;
024import java.lang.annotation.Retention;
025import java.lang.annotation.RetentionPolicy;
026import java.lang.annotation.Target;
027import java.lang.reflect.Method;
028import java.util.HashMap;
029import org.jgrapes.core.Channel;
030import org.jgrapes.core.ComponentType;
031import org.jgrapes.core.Event;
032import org.jgrapes.core.HandlerScope;
033
034/**
035 * This annotation tags some other annotation as a handler annotation. 
036 * The tagged annotation can then be used to mark a method as a handler.
037 *  
038 * Every handler definition annotation must provide an {@link Evaluator} 
039 * to allow access to the properties of the handler annotation in a 
040 * uniform way. 
041 */
042@Documented
043@Retention(RetentionPolicy.RUNTIME)
044@Target(ElementType.ANNOTATION_TYPE)
045public @interface HandlerDefinition {
046
047    /**
048     * Returns the evaluator for the annotated handler annotation.
049     * 
050     * @return the evaluator
051     */
052    Class<? extends Evaluator> evaluator();
053
054    /**
055     * This interface allows access to the properties defined by arbitrary
056     * handler annotations in a uniform way. Handler annotations
057     * must specify the scope of a handler, i.e. for which events and
058     * channels the handler should be invoked, and the priority of
059     * the handler.  
060     */
061    interface Evaluator {
062
063        /**
064         * Returns the information about the events and channels handled
065         * by the handler that annotates the given method of the given
066         * comonent as a {@link HandlerScope} object. This method
067         * is invoked during object initialization. It may return
068         * {@code null} if a handler is not supposed to be added for
069         * this method during initialization (dynamic handler,
070         * see {@link Handler#dynamic()}). 
071         *
072         * @param component the component
073         * @param method the annotated method
074         * @param channelReplacements replacements for channel classes in 
075         * the annotation's `channels` element
076         * @return the scope or {@code null} if a handler for the method
077         * should not be created
078         */
079        HandlerScope scope(ComponentType component, Method method,
080                ChannelReplacements channelReplacements);
081
082        /**
083         * Returns the priority defined by the annotation
084         * 
085         * @param annotation the annotation
086         * @return the priority
087         */
088        int priority(Annotation annotation);
089
090        /**
091         * Utility method for checking if the method can be used as handler.
092         * 
093         * @param method the method
094         * @return the result
095         */
096        static boolean checkMethodSignature(Method method) {
097            return method.getParameterTypes().length == 0
098                || method.getParameterTypes().length == 1
099                    && Event.class.isAssignableFrom(
100                        method.getParameterTypes()[0])
101                || (method.getParameterTypes().length == 2
102                    && Event.class.isAssignableFrom(
103                        method.getParameterTypes()[0]))
104                    && Channel.class.isAssignableFrom(
105                        method.getParameterTypes()[1]);
106        }
107    }
108
109    /**
110     * Represents channel (criteria) replacements that are to
111     * be applied to `channels` elements of {@link Handler}
112     * annotations.
113     */
114    @SuppressWarnings("serial")
115    class ChannelReplacements // NOPMD (for missing serialVersionUID)
116            extends HashMap<Class<? extends Channel>, Object> {
117
118        /**
119         * Create a new replacements specification object.
120         *
121         * @return the channel replacements
122         */
123        public static ChannelReplacements create() {
124            return new ChannelReplacements();
125        }
126
127        /**
128         * Adds a replacements to the replacements.
129         *
130         * @param annotationCriterion the criterion used in the annotation
131         * @param replacement the replacement
132         * @return the channel replacements for easy chaining
133         */
134        public ChannelReplacements add(
135                Class<? extends Channel> annotationCriterion,
136                Channel replacement) {
137            put(annotationCriterion, replacement.defaultCriterion());
138            return this;
139        }
140    }
141}