I would like to create a static nested class using annotation Processor. Is it possible?
I have created @MyAnnotation
annotation:
package annotationprocessing;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
}
and annotation processor:
package annotationprocessing;
import java.io.BufferedWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.processing.Filer;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.util.Elements;
import javax.tools.JavaFileObject;
@SupportedAnnotationTypes({"annotationprocessing.MyAnnotation"})
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class MyAnnotationProcessor extends javax.annotation.processing.AbstractProcessor {
private Filer filerUtils;
private Elements elementUtils;
private TypeElement myAnnotationTypeElement;
private Map<TypeElement, List<VariableElement>> annotatedFields;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
filerUtils = processingEnv.getFiler();
elementUtils = processingEnv.getElementUtils();
myAnnotationTypeElement = elementUtils.getTypeElement(MyAnnotation.class.getCanonicalName());
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
annotatedFields = new HashMap<>();
roundEnv.getElementsAnnotatedWith(myAnnotationTypeElement)
.stream()
.map(element -> (VariableElement) element)
.forEach(this::processAnnotation);
if (annotatedFields.isEmpty()) {
return true;
}
System.err.println(annotatedFields);
for (Map.Entry<TypeElement, List<VariableElement>> entry : annotatedFields.entrySet()) {
TypeElement enclosingClass = entry.getKey();
try {
JavaFileObject javaFileObject = filerUtils.createSourceFile(enclosingClass.getQualifiedName().toString() + "$Nested");
try (BufferedWriter writer = new BufferedWriter(javaFileObject.openWriter())) {
if (elementUtils.getPackageOf(enclosingClass).getQualifiedName().length() > 0) {
writer.write("package " + elementUtils.getPackageOf(enclosingClass).getQualifiedName() + ";");
writer.newLine();
}
writer.write("public /*static*/ class " + enclosingClass.getSimpleName() + "$Nested {");
writer.newLine();
for (VariableElement varElement : entry.getValue()) {
writer.write("static int " + varElement.getSimpleName() + ";");
writer.newLine();
}
writer.newLine();
writer.write("}");
}
} catch (IOException ex) {
Logger.getLogger(MyAnnotationProcessor.class.getName()).log(Level.SEVERE, null, ex);
}
}
return true;
}
private void processAnnotation(VariableElement sharedElement) {
TypeElement enclosingClass = (TypeElement) sharedElement.getEnclosingElement();
annotatedFields.putIfAbsent(enclosingClass, new ArrayList<>());
annotatedFields.get(enclosingClass).add(sharedElement);
}
}
While compiling class (javac -processor annotationprocessing.MyAnnotationProcessor -cp annotationprocessing.jar TestClass.java
)
package org.full.path;
import annotationprocessing.MyAnnotation;
public class TestClass {
public static class OtherNested {
static int staticInt;
}
@MyAnnotation
int staticInt;
public static void main(String[] args) {
System.out.println("other: " + TestClass.OtherNested.staticInt);
//System.out.println("not working: " + TestClass.Nested.staticInt);
System.out.println("generated: "+TestClass$Nested.staticInt);
}
}
the MyAnnotationProcessor
processes the source file and generated TestClass$Nested.class
file, but it can be only accessed by using TestClass$Nested
name instead of TestClass.Nested
like in nested classes. Moreover, I cannot use static
keyword in the generation code (because it is fully treated as highest level class).
Maybe there is a way to fully rewrite input source code with addition of static nested class?