I just got into java annotation processing and I wanted to create an annotation that does a similar job to @Value. I already created the annotation and annotation processor. so is there a way to inject the value from the process method? or am i going in the wrong direction? here is my annotation and processor
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomValue {
String value();
}
import com.google.auto.service.AutoService;
import report.configurations.PropertyLoader;
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.lang.model.element.VariableElement;
import javax.tools.*;
import java.io.File;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
@SupportedAnnotationTypes("report.annotations.CustomValue")
@SupportedSourceVersion(SourceVersion.RELEASE_17)
@AutoService(javax.annotation.processing.Processor.class)
public class CustomValueProcessor extends AbstractProcessor {
private Messager messager;
private Map<String, String> PropMap = PropertyLoader.parsePropertiesFile();
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
messager = processingEnv.getMessager();
// Retrieve the property name from options
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
for (TypeElement annotation : annotations) {
for (Element element : roundEnv.getElementsAnnotatedWith(annotation)) {
if (element.getKind() == ElementKind.FIELD) {
CustomValue annotationValue = element.getAnnotation(CustomValue.class);
String paramName = annotationValue.value();
String newParaName = paramName.substring(2, paramName.length() - 1);
messager.printMessage(Diagnostic.Kind.NOTE, "Injecting: " + newParaName);
String propertyValue = PropMap.get(newParaName);
messager.printMessage(Diagnostic.Kind.NOTE, "Injecting: " + propertyValue);
if (element instanceof VariableElement) {
VariableElement variableElement = (VariableElement) element;
generateAndExecuteCode(variableElement, propertyValue);
}
}
}
}
return true;
}
private void generateAndExecuteCode(VariableElement variableElement, String propertyValue) {
String fieldName = variableElement.getSimpleName().toString();
String setterMethodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
String setterCode = String.format("%s.%s(\"%s\");", variableElement.getEnclosingElement(), setterMethodName, propertyValue);
try {
String generatedClassName = "GeneratedCode";
String javaSourceCode = "public class " + generatedClassName + " {\n"
+ " public static void run() {\n"
+ " " + setterCode + "\n"
+ " }\n"
+ "}\n";
// Obtain a Filer instance
Filer filer = processingEnv.getFiler();
JavaFileObject sourceFileObject = filer.createSourceFile(generatedClassName);
try (Writer writer = sourceFileObject.openWriter()) {
writer.write(javaSourceCode);
}
File sourceFile = new File(".", generatedClassName + ".java");
try (PrintWriter writer = new PrintWriter(sourceFile)) {
writer.write(javaSourceCode);
}
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromFiles(Collections.singletonList(sourceFile));
compiler.getTask(null, fileManager, null, null, null, compilationUnits).call();
// Load and execute the compiled class
Class<?> generatedClass = Class.forName(generatedClassName);
generatedClass.getMethod("run").invoke(null);
// Cleanup: Delete the temporary source file
sourceFile.delete();
} catch (Exception e) {
e.printStackTrace();
}
}
}
I tried to generated a file to inject it at compile time but I suppose that`s not the right way to do it