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.Arrays; 029import java.util.HashMap; 030import org.jgrapes.core.Channel; 031import org.jgrapes.core.ComponentType; 032import org.jgrapes.core.Event; 033import org.jgrapes.core.HandlerScope; 034 035/** 036 * This annotation tags some other annotation as a handler annotation. 037 * The tagged annotation can then be used to mark a method as a handler. 038 * 039 * Every handler definition annotation must provide an {@link Evaluator} 040 * to allow access to the properties of the handler annotation in a 041 * uniform way. 042 */ 043@Documented 044@Retention(RetentionPolicy.RUNTIME) 045@Target(ElementType.ANNOTATION_TYPE) 046public @interface HandlerDefinition { 047 048 /** 049 * Returns the evaluator for the annotated handler annotation. 050 * 051 * @return the evaluator 052 */ 053 Class<? extends Evaluator> evaluator(); 054 055 /** 056 * This interface allows access to the properties defined by arbitrary 057 * handler annotations in a uniform way. Handler annotations 058 * must specify the scope of a handler, i.e. for which events and 059 * channels the handler should be invoked, and the priority of 060 * the handler. 061 */ 062 interface Evaluator { 063 064 /** 065 * Returns the information about the events and channels handled 066 * by the handler that annotates the given method of the given 067 * comonent as a {@link HandlerScope} object. This method 068 * is invoked during object initialization. It may return 069 * {@code null} if a handler is not supposed to be added for 070 * this method during initialization (dynamic handler, 071 * see {@link Handler#dynamic()}). 072 * 073 * @param component the component 074 * @param method the annotated method 075 * @param channelReplacements replacements for channel classes in 076 * the annotation's `channels` element 077 * @return the scope or {@code null} if a handler for the method 078 * should not be created 079 */ 080 @SuppressWarnings("PMD.LooseCoupling") 081 HandlerScope scope(ComponentType component, Method method, 082 ChannelReplacements channelReplacements); 083 084 /** 085 * Returns the priority defined by the annotation 086 * 087 * @param annotation the annotation 088 * @return the priority 089 */ 090 int priority(Annotation annotation); 091 092 /** 093 * Utility method for checking if the method can be used as handler. 094 * 095 * @param method the method 096 * @return the result 097 */ 098 @SuppressWarnings("PMD.UselessParentheses") 099 static boolean checkMethodSignature(Method method) { 100 return method.getParameterTypes().length == 0 101 || method.getParameterTypes().length == 1 102 && Event.class.isAssignableFrom( 103 method.getParameterTypes()[0]) 104 || (method.getParameterTypes().length == 2 105 && Event.class.isAssignableFrom( 106 method.getParameterTypes()[0])) 107 && Channel.class.isAssignableFrom( 108 method.getParameterTypes()[1]); 109 } 110 } 111 112 /** 113 * Represents channel (criteria) replacements that are to 114 * be applied to `channels` elements of {@link Handler} 115 * annotations. 116 */ 117 @SuppressWarnings("serial") 118 class ChannelReplacements // NOPMD (for missing serialVersionUID) 119 extends HashMap<Class<? extends Channel>, Object[]> { 120 121 /** 122 * Create a new replacements specification object. 123 * 124 * @return the channel replacements 125 */ 126 @SuppressWarnings("PMD.LooseCoupling") 127 public static ChannelReplacements create() { 128 return new ChannelReplacements(); 129 } 130 131 /** 132 * Adds a replacements to the replacements. 133 * 134 * @param annotationCriterion the criterion used in the annotation 135 * @param replacements the replacements 136 * @return the channel replacements for easy chaining 137 */ 138 @SuppressWarnings("PMD.LooseCoupling") 139 public ChannelReplacements add( 140 Class<? extends Channel> annotationCriterion, 141 Channel... replacements) { 142 var criteria = Arrays.stream(replacements) 143 .map(Channel::defaultCriterion).toArray(Object[]::new); 144 put(annotationCriterion, criteria); 145 return this; 146 } 147 } 148}