2828
2929import org .spongepowered .api .event .Event ;
3030import org .spongepowered .api .event .Listener ;
31+ import org .spongepowered .api .event .filter .IsCancelled ;
32+ import org .spongepowered .api .event .filter .type .Exclude ;
33+ import org .spongepowered .api .event .filter .type .Include ;
3134
35+ import java .util .Collections ;
36+ import java .util .HashSet ;
3237import java .util .List ;
38+ import java .util .Map ;
3339import java .util .Set ;
3440import javax .annotation .processing .AbstractProcessor ;
35- import javax .annotation .processing .Messager ;
3641import javax .annotation .processing .RoundEnvironment ;
3742import javax .annotation .processing .SupportedAnnotationTypes ;
3843import javax .annotation .processing .SupportedSourceVersion ;
3944import javax .lang .model .SourceVersion ;
45+ import javax .lang .model .element .AnnotationMirror ;
46+ import javax .lang .model .element .AnnotationValue ;
4047import javax .lang .model .element .Element ;
4148import javax .lang .model .element .ElementKind ;
4249import javax .lang .model .element .ExecutableElement ;
4350import javax .lang .model .element .Modifier ;
4451import javax .lang .model .element .TypeElement ;
4552import javax .lang .model .element .VariableElement ;
53+ import javax .lang .model .type .DeclaredType ;
4654import javax .lang .model .type .TypeKind ;
4755import javax .lang .model .type .TypeMirror ;
4856import javax .lang .model .util .Elements ;
4957import javax .lang .model .util .Types ;
50- import javax .tools .Diagnostic ;
5158
5259@ SupportedAnnotationTypes (ListenerProcessor .LISTENER_ANNOTATION_CLASS )
5360@ SupportedSourceVersion (SourceVersion .RELEASE_8 )
5461public class ListenerProcessor extends AbstractProcessor {
5562
5663 static final String LISTENER_ANNOTATION_CLASS = "org.spongepowered.api.event.Listener" ;
5764 private static final String EVENT_CLASS = Event .class .getName ();
65+ private static final String IS_CANCELLED_ANNOTATION = IsCancelled .class .getName ();
66+ private static final String INCLUDE_ANNOTATION = Include .class .getName ();
67+ private static final String EXCLUDE_ANNOTATION = Exclude .class .getName ();
68+
69+ @ Override
70+ public Set <String > getSupportedAnnotationTypes () {
71+ final Set <String > types = new HashSet <>(super .getSupportedAnnotationTypes ());
72+ for (final ListenerParameterAnnotation annotation : ListenerParameterAnnotation .values ()) {
73+ types .add (annotation .className ());
74+ }
75+ return Collections .unmodifiableSet (types );
76+ }
5877
5978 @ Override
6079 public boolean process (final Set <? extends TypeElement > annotations , final RoundEnvironment roundEnv ) {
@@ -66,32 +85,94 @@ public boolean process(final Set<? extends TypeElement> annotations, final Round
6685 }
6786 final ExecutableElement method = (ExecutableElement ) e ;
6887
69- final Messager msg = this .processingEnv .getMessager ();
7088 if (method .getModifiers ().contains (Modifier .STATIC )) {
71- msg . printMessage ( Diagnostic . Kind . ERROR , "method must not be static" , method );
89+ this . error ( "method must not be static" , method );
7290 }
7391 if (!method .getModifiers ().contains (Modifier .PUBLIC )) {
74- msg . printMessage ( Diagnostic . Kind . ERROR , "method must be public" , method );
92+ this . error ( "method must be public" , method );
7593 }
7694 if (method .getModifiers ().contains (Modifier .ABSTRACT )) {
77- msg . printMessage ( Diagnostic . Kind . ERROR , "method must not be abstract" , method );
95+ this . error ( "method must not be abstract" , method );
7896 }
7997 if (method .getEnclosingElement ().getKind ().isInterface ()) {
80- msg . printMessage ( Diagnostic . Kind . ERROR , "interfaces cannot declare listeners" , method );
98+ this . error ( "interfaces cannot declare listeners" , method );
8199 }
82100 if (method .getReturnType ().getKind () != TypeKind .VOID ) {
83- msg . printMessage ( Diagnostic . Kind . ERROR , "method must return void" , method );
101+ this . error ( "method must return void" , method );
84102 }
85103 final List <? extends VariableElement > parameters = method .getParameters ();
104+ final DeclaredType eventType ;
86105 if (parameters .isEmpty () || !this .isTypeSubclass (parameters .get (0 ), ListenerProcessor .EVENT_CLASS )) {
87- msg .printMessage (Diagnostic .Kind .ERROR , "method must have an Event as its first parameter" , method );
106+ this .error ( "method must have an Event as its first parameter" , method );
107+ eventType = null ;
108+ } else {
109+ eventType = (DeclaredType ) parameters .get (0 ).asType ();
110+ }
111+
112+ final Types types = this .processingEnv .getTypeUtils ();
113+ if (eventType != null ) {
114+ for (final AnnotationMirror annotation : method .getAnnotationMirrors ()) {
115+ final String name = this .processingEnv .getElementUtils ()
116+ .getBinaryName ((TypeElement ) annotation .getAnnotationType ().asElement ()).toString ();
117+ if (name .equals (ListenerProcessor .IS_CANCELLED_ANNOTATION )) {
118+ // ensure the event parameter inherits from Cancellable
119+ final TypeElement cancellable =
120+ this .processingEnv .getElementUtils ().getTypeElement ("org.spongepowered.api.event.Cancellable" );
121+ if (cancellable != null && !types .isAssignable (eventType , cancellable .asType ())) {
122+ this .error ("A listener for a non-Cancellable method cannot be annotated with @IsCancelled" , method );
123+ }
124+ } else if (name .equals (ListenerProcessor .INCLUDE_ANNOTATION ) || name .equals (ListenerProcessor .EXCLUDE_ANNOTATION )) {
125+ // ensure that all referenced types are subtypes of Cancellable
126+ for (final Map .Entry <? extends ExecutableElement , ? extends AnnotationValue > entry
127+ : annotation .getElementValues ().entrySet ()) {
128+ if (entry .getKey ().getSimpleName ().contentEquals ("value" )) {
129+ @ SuppressWarnings ("unchecked" ) final List <? extends AnnotationValue > values =
130+ (List <? extends AnnotationValue >) entry .getValue ().getValue ();
131+ for (final AnnotationValue subtype : values ) {
132+ if (!types .isAssignable ((TypeMirror ) subtype .getValue (), eventType )) {
133+ this .error (
134+ "All filtered types must be subtypes of the event type '" + eventType .asElement ().getSimpleName () + "'" ,
135+ method ,
136+ annotation ,
137+ subtype
138+ );
139+ }
140+ }
141+ }
142+ }
143+ }
144+ }
145+ }
146+
147+
148+ final ParameterContext ctx = new ParameterContext (this .processingEnv , eventType );
149+ for (int i = 1 ; i < parameters .size (); ++i ) {
150+ this .checkParameter (ctx , parameters .get (i ));
88151 }
89152 }
90153 }
91154
92155 return false ;
93156 }
94157
158+ /**
159+ * Check method parameters of event listeners for valid uses of event handler annotations
160+ *
161+ * @param element element to check
162+ */
163+ private void checkParameter (final ParameterContext ctx , final VariableElement element ) {
164+ // for every filtering annotation registered, check if this element is annotated
165+ // then, we get to
166+ for (final AnnotationMirror annotation : element .getAnnotationMirrors ()) {
167+ ctx .init (element , annotation );
168+ final CharSequence name = this .processingEnv .getElementUtils ().getBinaryName ((TypeElement ) annotation .getAnnotationType ().asElement ());
169+ final ListenerParameterAnnotation anno = ListenerParameterAnnotation .byClassName (name .toString ());
170+ if (anno != null ) {
171+ anno .validate (ctx );
172+ }
173+ }
174+ }
175+
95176 private boolean isTypeSubclass (final Element typedElement , final String subclass ) {
96177 final Elements elements = this .processingEnv .getElementUtils ();
97178 final Types types = this .processingEnv .getTypeUtils ();
@@ -100,4 +181,14 @@ private boolean isTypeSubclass(final Element typedElement, final String subclass
100181 return types .isAssignable (typedElement .asType (), event );
101182 }
102183
184+ // Error collection
185+
186+ private void error (final CharSequence message , final Element element ) {
187+ this .processingEnv .getMessager ().printMessage (ERROR , message , element );
188+ }
189+
190+ private void error (final CharSequence message , final Element element , final AnnotationMirror annotation , final AnnotationValue value ) {
191+ this .processingEnv .getMessager ().printMessage (ERROR , message , element , annotation , value );
192+ }
193+
103194}
0 commit comments