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?
filerUtils.createSourceFile(elementUtils.getBinaryName(enclosingClass) + "$Nested")