public class EventHandlerAnnotationChecker extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
final Types types = processingEnv.getTypeUtils();
final Elements elements = processingEnv.getElementUtils();
final TypeMirror gwtWidgetType = elements.getTypeElement(TypeNames.GWT_WIDGET).asType();
for (TypeElement annotation : annotations) {
for (Element target : roundEnv.getElementsAnnotatedWith(annotation)) {
if (((ExecutableElement) target).getReturnType().getKind() != TypeKind.VOID) {
processingEnv.getMessager().printMessage(
Kind.ERROR, "@EventHandler methods must return void", target);
}
AnnotationMirror eventHandlerAnnotation = getAnnotation(target, TypeNames.EVENT_HANDLER);
TypeElement enclosingClassElement = (TypeElement) target.getEnclosingElement();
boolean hasSinkNative = hasAnnotation(target, TypeNames.SINK_NATIVE);
AnnotationValue eventHandlerAnnotationValue = getAnnotationParamValueWithoutDefaults(target, TypeNames.EVENT_HANDLER, "value");
// if there is no annotation parameter value, this method handles events from the templated widget itself: nothing more to check.
// if the method is also annotated with @SinkNative, the values refer to template elements and we can't (easily) check them
if (eventHandlerAnnotationValue != null && !hasSinkNative) {
@SuppressWarnings("unchecked")
List<AnnotationValue> eventHandlerAnnotationValues = (List<AnnotationValue>) eventHandlerAnnotationValue.getValue();
for (AnnotationValue av : eventHandlerAnnotationValues) {
String referencedFieldName = (String) av.getValue();
Element referencedField = getField(enclosingClassElement, referencedFieldName);
if (referencedField == null || !types.isAssignable(referencedField.asType(), gwtWidgetType)) {
processingEnv.getMessager().printMessage(
Kind.ERROR, "\"" + referencedFieldName + "\" must refer to a field of type Widget. To reference template elements directly, use @SinkNative.",
target, eventHandlerAnnotation, av);
}
}
}
List<? extends VariableElement> methodParams = ((ExecutableElement) target).getParameters();
TypeMirror requiredArgType = hasSinkNative ?
elements.getTypeElement(TypeNames.GWT_OPAQUE_DOM_EVENT).asType() :
types.getDeclaredType(elements.getTypeElement(TypeNames.GWT_EVENT), types.getWildcardType(null, null));
if (methodParams.size() != 1 || !types.isAssignable(methodParams.get(0).asType(), requiredArgType)) {
if (hasSinkNative) {
processingEnv.getMessager().printMessage(
Kind.ERROR, "Native event handling methods must take exactly one argument of type " + requiredArgType,
target);