jokarl
jokarl

Reputation: 2225

Enforce method signature using annotations

I have written a custom annotation that I use to find methods that can be invoked via a IoT platform. It's a method level annotation:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DirectMethod {

    String value();

}

I look this annotation up in runtime, and to have the call succeed, the expected signature must be:

@DeviceMethod("metod")
public ReturnType methodName(final String data) {...}

i.e, the return type and the input parameters are crucial.

Is there any way to have an annotation be "smart" when its target type is METHOD? Like integrated IDE warnings and such. Or do I simply have to process each annotation manually at startup and have the startup procedure fail if any method breaks my intended method contract?

Upvotes: 2

Views: 1294

Answers (1)

GotoFinal
GotoFinal

Reputation: 3675

Yes, you can write annotation processor to validate your calls, the only downside of this is that annotation processors needs to be passed to javac (gradle and maven support easy syntax to register them) so someone could just not do it and not see any warnings/errors.

But otherwise all you need to do is create special annotation and processor, like that:

@SupportedAnnotationTypes("com.gotofinal.direct.DirectMethod")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class DirectAnnProcessor extends AbstractProcessor {

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        TypeElement stringType = processingEnv.getElementUtils().getTypeElement("java.lang.String");
        TypeElement expectedReturnType = processingEnv.getElementUtils().getTypeElement("com.gotofinal.direct.ReturnType");

        for (Element element : roundEnv.getElementsAnnotatedWith(DirectMethod.class)) {
            if (! (element instanceof ExecutableElement)) {
                processingEnv.getMessager().printMessage(Kind.ERROR, "Annotation should be on method.");
                continue;
            }
            ExecutableElement executableElement = (ExecutableElement) element;
            if (! executableElement.getReturnType().equals(expectedReturnType)) {
                processingEnv.getMessager().printMessage(Kind.ERROR, "Method should return ReturnType");
            }
            List<? extends VariableElement> parameters = executableElement.getParameters();
            if (parameters.size() != 1 && parameters.get(0).asType().equals(stringType)) {
                processingEnv.getMessager().printMessage(Kind.ERROR, "Method should have single String argument");
            }
        }
        return true; // no further processing of this annotation type
    }
}

And register it in META-INF/services/javax.annotation.processing.Processor file:

com.gotofinal.direct.DirectAnnProcessor

And then you can add such lib to maven/gradle as annotation processor and it should report any issues. In gradle such library must be added using annotationProcessor "my:lib:0.1" declaration.

Upvotes: 1

Related Questions