Create dynamic method using Annotation Processor

91 Views Asked by At

At the moment I am trying to create an annotation processor that creates a method called getValue in the class that is annotated. For this I created the following class:

package ro.Gabriel.AnnotationTest;

import com.google.auto.service.AutoService;

import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.tools.JavaFileObject;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Set;

@AutoService(javax.annotation.processing.Processor.class)
@SupportedAnnotationTypes("ro.Gabriel.AnnotationTest.CustomGetter")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class CustomGetterProcessor extends AbstractProcessor {

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        for (Element element : roundEnv.getElementsAnnotatedWith(CustomGetter.class)) {
            if (element.getKind() == ElementKind.CLASS) {
                generateGetValueMethod((TypeElement) element);
            }
        }
        return true;
    }

    private void generateGetValueMethod(TypeElement classElement) {
        String className = classElement.getSimpleName().toString();
        String packageName = processingEnv.getElementUtils().getPackageOf(classElement).getQualifiedName().toString();

        String generatedClassName = className + "Generated";
        
        String generatedCode = String.format(
                "package %s;\n\n" +
                        "public class %s {\n" +
                        "    public String getValue() {\n" +
                        "        return \"Hello from getValue method!\";\n" +
                        "    }\n" +
                        "}\n",
                packageName, generatedClassName
        );

        try {
            JavaFileObject fileObject = processingEnv.getFiler().createSourceFile(
                    packageName + "." + generatedClassName, classElement);
            try (PrintWriter writer = new PrintWriter(fileObject.openWriter())) {
                writer.print(generatedCode);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

here is @CustomGetter annotation class:

package ro.Gabriel.AnnotationTest;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface CustomGetter { }

In the resources folder I created the following directory: META-INF/services in the file resources/META-INF/services I created the following file: javax.annotation.processing.Processor

in javax.annotation.processing.Processor I added my processor: ro.Gabriel.AnnotationTest.CustomGetterProcessor

in the pom.xml file I added the following repository for @AutoService(Processor.class):

        <dependency>
            <groupId>com.google.auto.service</groupId>
            <artifactId>auto-service</artifactId>
            <version>1.0.1</version>
            <scope>provided</scope>
        </dependency>

now I have created the following class:

package ro.Gabriel.AnnotationTest;

@CustomGetter
public class ExampleClass {

}
ExampleClass e = new ExampleClass();
String value = e.getValue();// this method is not in the ExampleClass class even if it is annotated with @CustomGetter 

My question is why the getValue() method does not appear in the ExampleClass class?

I think that some configurations must be made in pom.xml, but I don't know exactly how.

Can someone help me?

Thanks!

1

There are 1 best solutions below

3
tquadrat On

With your setup, you will end up with two classes ExampleClass: first that one you annotated, the second one with the generated getter. That may cause funny results, but nothing useful.

It is a well known short-coming of annotation processing that it is unable to modify existing source code.

Tools/libraries like Lombok that generates methods like getters and setters on the fly will use other means to get their work done.