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 HandlerScope scope(ComponentType component, Method method, 081 ChannelReplacements channelReplacements); 082 083 /** 084 * Returns the priority defined by the annotation 085 * 086 * @param annotation the annotation 087 * @return the priority 088 */ 089 int priority(Annotation annotation); 090 091 /** 092 * Utility method for checking if the method can be used as handler. 093 * 094 * @param method the method 095 * @return the result 096 */ 097 static boolean checkMethodSignature(Method method) { 098 return method.getParameterTypes().length == 0 099 || method.getParameterTypes().length == 1 100 && Event.class.isAssignableFrom( 101 method.getParameterTypes()[0]) 102 || (method.getParameterTypes().length == 2 103 && Event.class.isAssignableFrom( 104 method.getParameterTypes()[0])) 105 && Channel.class.isAssignableFrom( 106 method.getParameterTypes()[1]); 107 } 108 } 109 110 /** 111 * Represents channel (criteria) replacements that are to 112 * be applied to `channels` elements of {@link Handler} 113 * annotations. 114 */ 115 @SuppressWarnings("serial") 116 class ChannelReplacements // NOPMD (for missing serialVersionUID) 117 extends HashMap<Class<? extends Channel>, Object[]> { 118 119 /** 120 * Create a new replacements specification object. 121 * 122 * @return the channel replacements 123 */ 124 public static ChannelReplacements create() { 125 return new ChannelReplacements(); 126 } 127 128 /** 129 * Adds a replacements to the replacements. 130 * 131 * @param annotationCriterion the criterion used in the annotation 132 * @param replacements the replacements 133 * @return the channel replacements for easy chaining 134 */ 135 public ChannelReplacements add( 136 Class<? extends Channel> annotationCriterion, 137 Channel... replacements) { 138 var criteria = Arrays.stream(replacements) 139 .map(Channel::defaultCriterion).toArray(Object[]::new); 140 put(annotationCriterion, criteria); 141 return this; 142 } 143 } 144}