From 968fb8f219b34d7156cc98b5ec52b1d3492ed530 Mon Sep 17 00:00:00 2001 From: Gunnar Wagenknecht Date: Thu, 29 Feb 2024 11:50:53 +0100 Subject: [PATCH] Found another ECJ Annotation Processor Bug --- ecj-test/.gitignore | 3 +- ecj-test/compile-proc.params | 11 +++ ecj-test/compile-procsample.params | 16 +++++ .../GeneratorAnnotationProcessor.java | 72 +++++++++++++++++++ .../GeneratedResourceFamilyDefinition.java | 16 +++++ .../processor/api/ResourceDefinition.java | 9 +++ .../processor/api/WrapperDefinition.java | 16 +++++ .../sample/processor/usage/MyResources.java | 13 ++++ .../processor/usage/checks/ISomeChecks.java | 6 ++ .../usage/testing/ITestResource.java | 19 +++++ 10 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 ecj-test/compile-proc.params create mode 100644 ecj-test/compile-procsample.params create mode 100644 ecj-test/src-proc/sample/processor/GeneratorAnnotationProcessor.java create mode 100644 ecj-test/src-proc/sample/processor/api/GeneratedResourceFamilyDefinition.java create mode 100644 ecj-test/src-proc/sample/processor/api/ResourceDefinition.java create mode 100644 ecj-test/src-proc/sample/processor/api/WrapperDefinition.java create mode 100644 ecj-test/src/sample/processor/usage/MyResources.java create mode 100644 ecj-test/src/sample/processor/usage/checks/ISomeChecks.java create mode 100644 ecj-test/src/sample/processor/usage/testing/ITestResource.java diff --git a/ecj-test/.gitignore b/ecj-test/.gitignore index 569fc52..cd2ed36 100644 --- a/ecj-test/.gitignore +++ b/ecj-test/.gitignore @@ -2,4 +2,5 @@ remotejdk* zulu* *.jar *.tar.gz -bin/ +bin*/ +src-gen/ \ No newline at end of file diff --git a/ecj-test/compile-proc.params b/ecj-test/compile-proc.params new file mode 100644 index 0000000..05ed83c --- /dev/null +++ b/ecj-test/compile-proc.params @@ -0,0 +1,11 @@ +-d +bin-proc +-s +src-gen +-verbose +--release +11 +src-proc/sample/processor/api/GeneratedResourceFamilyDefinition.java +src-proc/sample/processor/api/WrapperDefinition.java +src-proc/sample/processor/api/ResourceDefinition.java +src-proc/sample/processor/GeneratorAnnotationProcessor.java diff --git a/ecj-test/compile-procsample.params b/ecj-test/compile-procsample.params new file mode 100644 index 0000000..e7718b1 --- /dev/null +++ b/ecj-test/compile-procsample.params @@ -0,0 +1,16 @@ +-d +bin +-s +src-gen +-verbose +--release +11 +-processorpath +bin-proc +-processor +sample.processor.GeneratorAnnotationProcessor +-classpath +bin-proc +src/sample/processor/usage/testing/ITestResource.java +src/sample/processor/usage/checks/ISomeChecks.java +src/sample/processor/usage/MyResources.java diff --git a/ecj-test/src-proc/sample/processor/GeneratorAnnotationProcessor.java b/ecj-test/src-proc/sample/processor/GeneratorAnnotationProcessor.java new file mode 100644 index 0000000..e4b5354 --- /dev/null +++ b/ecj-test/src-proc/sample/processor/GeneratorAnnotationProcessor.java @@ -0,0 +1,72 @@ +package sample.processor; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Set; + +import javax.annotation.processing.AbstractProcessor; +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.Element; +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; +import javax.tools.JavaFileObject; + +import sample.processor.api.GeneratedResourceFamilyDefinition; + +@SupportedAnnotationTypes({ "sample.processor.api.GeneratedResourceFamilyDefinition", + "sample.processor.api.WrapperDefinition", "sample.processor.api.ResourceDefinition" }) +@SupportedSourceVersion(SourceVersion.RELEASE_8) +public class GeneratorAnnotationProcessor extends AbstractProcessor { + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + + for (TypeElement annotation : annotations) { + Set annotatedElements = roundEnv.getElementsAnnotatedWith(annotation); + + for (Element element : annotatedElements) { + GeneratedResourceFamilyDefinition annotation2 = element + .getAnnotation(GeneratedResourceFamilyDefinition.class); + if (annotation2 != null) { + String wrapperClassName = annotation2.wrapper().className(); + if (wrapperClassName == null || wrapperClassName.trim().isEmpty()) + continue; + + try { + writeWrapperFile(wrapperClassName, (TypeElement) element); + } catch (IOException e) { + throw new IllegalStateException("Unable to generate output. " + e.getMessage(), e); + } + } + + } + } + + return true; + } + + private void writeWrapperFile(String simpleClassName, TypeElement annotatedElement) throws IOException { + String packageName = ((PackageElement) annotatedElement.getEnclosingElement()).getQualifiedName() + ".wrappers"; + + String wrapperClassName = packageName + "." + simpleClassName; + JavaFileObject builderFile = processingEnv.getFiler().createSourceFile(wrapperClassName, annotatedElement); + + try (PrintWriter out = new PrintWriter(builderFile.openWriter())) { + + out.print("package "); + out.print(packageName); + out.println(";"); + out.println(); + + out.print("public class "); + out.print(simpleClassName); + out.println(" {"); + out.println(); + + out.println("}"); + } + } +} \ No newline at end of file diff --git a/ecj-test/src-proc/sample/processor/api/GeneratedResourceFamilyDefinition.java b/ecj-test/src-proc/sample/processor/api/GeneratedResourceFamilyDefinition.java new file mode 100644 index 0000000..23725a6 --- /dev/null +++ b/ecj-test/src-proc/sample/processor/api/GeneratedResourceFamilyDefinition.java @@ -0,0 +1,16 @@ +package sample.processor.api; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE}) +public @interface GeneratedResourceFamilyDefinition { + + Class feature(); + + WrapperDefinition wrapper() default @WrapperDefinition(className=""); + +} diff --git a/ecj-test/src-proc/sample/processor/api/ResourceDefinition.java b/ecj-test/src-proc/sample/processor/api/ResourceDefinition.java new file mode 100644 index 0000000..16d3cba --- /dev/null +++ b/ecj-test/src-proc/sample/processor/api/ResourceDefinition.java @@ -0,0 +1,9 @@ +package sample.processor.api; + +public @interface ResourceDefinition { + + Class family(); + + String[] checks() default {}; + +} diff --git a/ecj-test/src-proc/sample/processor/api/WrapperDefinition.java b/ecj-test/src-proc/sample/processor/api/WrapperDefinition.java new file mode 100644 index 0000000..b514d08 --- /dev/null +++ b/ecj-test/src-proc/sample/processor/api/WrapperDefinition.java @@ -0,0 +1,16 @@ +package sample.processor.api; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface WrapperDefinition { + + /** + * name of the wrapper class to generate. + */ + String className(); +} \ No newline at end of file diff --git a/ecj-test/src/sample/processor/usage/MyResources.java b/ecj-test/src/sample/processor/usage/MyResources.java new file mode 100644 index 0000000..bdf185c --- /dev/null +++ b/ecj-test/src/sample/processor/usage/MyResources.java @@ -0,0 +1,13 @@ +package sample.processor.usage; + +import sample.processor.api.GeneratedResourceFamilyDefinition; +import sample.processor.api.WrapperDefinition; +import sample.processor.usage.testing.ITestResource; + +@GeneratedResourceFamilyDefinition(wrapper = @WrapperDefinition(className = "MyResourceWrapperForTesting"), feature = ITestResource.class) +public class MyResources { + + public void doSomething() { + // whatever + } +} diff --git a/ecj-test/src/sample/processor/usage/checks/ISomeChecks.java b/ecj-test/src/sample/processor/usage/checks/ISomeChecks.java new file mode 100644 index 0000000..6317688 --- /dev/null +++ b/ecj-test/src/sample/processor/usage/checks/ISomeChecks.java @@ -0,0 +1,6 @@ +package sample.processor.usage.checks; + +public interface ISomeChecks { + public final static String CHECK_FOO = "Is Foo Present"; + public final static String CHECK_BAR = "Is Bar Absent"; +} diff --git a/ecj-test/src/sample/processor/usage/testing/ITestResource.java b/ecj-test/src/sample/processor/usage/testing/ITestResource.java new file mode 100644 index 0000000..40465ac --- /dev/null +++ b/ecj-test/src/sample/processor/usage/testing/ITestResource.java @@ -0,0 +1,19 @@ +package sample.processor.usage.testing; + +import sample.processor.api.ResourceDefinition; +import sample.processor.usage.MyResources; +import sample.processor.usage.checks.ISomeChecks; +import sample.processor.usage.wrappers.MyResourceWrapperForTesting; + +/** + * A Test resource + */ +@ResourceDefinition(family = MyResources.class, checks = { ISomeChecks.CHECK_FOO }) +public interface ITestResource { + + /** + * For testing use the generated {@link MyResourceWrapperForTesting} + */ + String TEST_NAME = "foobar"; + +}