From ca13fffd0e356a4a0c73563ff90a145acb5d7706 Mon Sep 17 00:00:00 2001 From: zaaarf Date: Thu, 24 Aug 2023 01:50:14 +0200 Subject: [PATCH] feat: proper error handling --- .../BadListenerArgumentsException.java | 52 +++++++++++++++++++ .../exceptions/MissingInterfaceException.java | 17 ++++++ .../ftbsc/geb/processor/GEBProcessor.java | 32 +++++++++--- 3 files changed, 94 insertions(+), 7 deletions(-) create mode 100644 src/main/java/ftbsc/geb/exceptions/BadListenerArgumentsException.java create mode 100644 src/main/java/ftbsc/geb/exceptions/MissingInterfaceException.java diff --git a/src/main/java/ftbsc/geb/exceptions/BadListenerArgumentsException.java b/src/main/java/ftbsc/geb/exceptions/BadListenerArgumentsException.java new file mode 100644 index 0000000..a15b4e5 --- /dev/null +++ b/src/main/java/ftbsc/geb/exceptions/BadListenerArgumentsException.java @@ -0,0 +1,52 @@ +package ftbsc.geb.exceptions; + +/** + * Thrown when there is something wrong with a listener method. + */ +public class BadListenerArgumentsException extends RuntimeException { + + /** + * The constructor. It's not meant to be used as is, refer to the subclasses + * @param message the message to pass along to the superclass + */ + protected BadListenerArgumentsException(String message) { + super(message); + } + + /** + * Thrown when the number of arguments is off. + */ + public static class Count extends BadListenerArgumentsException { + + /** + * The public constructor. + * @param clazz the fully-qualified name of the parent class + * @param method the annotated listener method + * @param count the number of arguments found + */ + public Count(String clazz, String method, int count) { + super(String.format( + "An error occurred while evaluating method %s::%s: found %d arguments, expected 1!", + clazz, method, count)); + } + } + + /** + * Thrown when the argument is of the wrong type. + */ + public static class Type extends BadListenerArgumentsException { + + /** + * The public constructor. + * @param clazz the fully-qualified name of the parent class + * @param method the annotated listener method + * @param parameterName the name of the parameter + */ + public Type(String clazz, String method, String parameterName) { + super(String.format( + "The parameter %s of %s::%s does not implement the IEvent interface!", + parameterName, clazz, method + )); + } + } +} diff --git a/src/main/java/ftbsc/geb/exceptions/MissingInterfaceException.java b/src/main/java/ftbsc/geb/exceptions/MissingInterfaceException.java new file mode 100644 index 0000000..e792729 --- /dev/null +++ b/src/main/java/ftbsc/geb/exceptions/MissingInterfaceException.java @@ -0,0 +1,17 @@ +package ftbsc.geb.exceptions; + +/** + * Thrown when a parent of a listener method does not implement the + * appropriate interface, + */ +public class MissingInterfaceException extends RuntimeException { + + /** + * The public constructor. + * @param clazz the fully-qualified name of the parent class + * @param method the annotated listener method + */ + public MissingInterfaceException(String clazz, String method) { + super(String.format("The parent of %s::%s does not implement the IListener interface!", clazz, method)); + } +} diff --git a/src/main/java/ftbsc/geb/processor/GEBProcessor.java b/src/main/java/ftbsc/geb/processor/GEBProcessor.java index 37d6072..5ea4c38 100644 --- a/src/main/java/ftbsc/geb/processor/GEBProcessor.java +++ b/src/main/java/ftbsc/geb/processor/GEBProcessor.java @@ -5,12 +5,16 @@ import ftbsc.geb.api.IEvent; import ftbsc.geb.api.IEventDispatcher; import ftbsc.geb.api.IListener; import ftbsc.geb.api.annotations.Listen; +import ftbsc.geb.exceptions.BadListenerArgumentsException; +import ftbsc.geb.exceptions.MissingInterfaceException; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.lang.model.element.*; +import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; +import javax.tools.Diagnostic; import javax.tools.FileObject; import javax.tools.JavaFileObject; import javax.tools.StandardLocation; @@ -88,23 +92,37 @@ public class GEBProcessor extends AbstractProcessor { * @param target the {@link Element} that was annotated with {@link Listen} */ private void processListener(Element target) { - if(!(target instanceof ExecutableElement)) - return; //TODO throw error - ExecutableElement listener = (ExecutableElement) target; //this cast will never fail //ensure the parent is instance of IListener TypeMirror parentType = listener.getEnclosingElement().asType(); if(!this.processingEnv.getTypeUtils().isAssignable(parentType, this.listenerInterface)) - return; //TODO throw error, parent doesn't implement the interface + throw new MissingInterfaceException( + listener.getEnclosingElement().getSimpleName().toString(), + listener.getSimpleName().toString()); - //ensure the listener method has only a single IEvent parameter + //ensure the listener method has only one parameter List params = listener.getParameters(); if(listener.getParameters().size() != 1) - return; //TODO throw error, bad parameter amount + throw new BadListenerArgumentsException.Count( + listener.getEnclosingElement().getSimpleName().toString(), + listener.getSimpleName().toString(), + params.size()); + + //ensure said parameter implements IEvent TypeMirror event = params.get(0).asType(); if(!this.processingEnv.getTypeUtils().isAssignable(event, this.eventInterface)) - return; //TODO throw error, bad parameter type + throw new BadListenerArgumentsException.Type( + listener.getEnclosingElement().getSimpleName().toString(), + listener.getSimpleName().toString(), + params.get(0).getSimpleName().toString()); + + //warn about return type + if(!listener.getReturnType().getKind().equals(TypeKind.VOID)) + this.processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, String.format( + "The method %s::%s has a return type: please note that it will be ignored.", + listener.getEnclosingElement().getSimpleName().toString(), + listener.getSimpleName().toString())); if(!this.listenerMap.containsKey(event)) this.listenerMap.put(event, new HashSet<>());