1717package io .helidon .codegen .apt ;
1818
1919import java .util .HashMap ;
20+ import java .util .HashSet ;
2021import java .util .List ;
2122import java .util .Map ;
2223import java .util .Optional ;
2324import java .util .Set ;
2425import java .util .stream .Collectors ;
25- import java .util .stream .Stream ;
2626
2727import javax .annotation .processing .AbstractProcessor ;
2828import javax .annotation .processing .ProcessingEnvironment ;
3535
3636import io .helidon .codegen .Codegen ;
3737import io .helidon .codegen .CodegenEvent ;
38+ import io .helidon .codegen .CodegenException ;
3839import io .helidon .codegen .Option ;
40+ import io .helidon .common .types .Annotation ;
3941import io .helidon .common .types .TypeInfo ;
4042import io .helidon .common .types .TypeName ;
4143
4547/**
4648 * Annotation processor that maps APT types to Helidon types, and invokes {@link io.helidon.codegen.Codegen}.
4749 */
50+ @ SuppressWarnings ("removal" )
4851public final class AptProcessor extends AbstractProcessor {
4952 private static final TypeName GENERATOR = TypeName .create (AptProcessor .class );
5053
51- private AptContext ctx ;
54+ private AptContextImpl ctx ;
5255 private Codegen codegen ;
5356
5457 /**
@@ -66,13 +69,8 @@ public SourceVersion getSupportedSourceVersion() {
6669
6770 @ Override
6871 public Set <String > getSupportedAnnotationTypes () {
69- return Stream .concat (codegen .supportedAnnotations ()
70- .stream ()
71- .map (TypeName ::fqName ),
72- codegen .supportedAnnotationPackagePrefixes ()
73- .stream ()
74- .map (it -> it + "*" ))
75- .collect (Collectors .toSet ());
72+ // we need to support all annotations, to be able to use meta-annotations
73+ return Set .of ("*" );
7674 }
7775
7876 @ Override
@@ -87,12 +85,14 @@ public Set<String> getSupportedOptions() {
8785 public synchronized void init (ProcessingEnvironment processingEnv ) {
8886 super .init (processingEnv );
8987
90- this .ctx = AptContext .create (processingEnv , Codegen .supportedOptions ());
88+ this .ctx = AptContextImpl .create (processingEnv , Codegen .supportedOptions ());
9189 this .codegen = Codegen .create (ctx , GENERATOR );
9290 }
9391
9492 @ Override
9593 public boolean process (Set <? extends TypeElement > annotations , RoundEnvironment roundEnv ) {
94+ this .ctx .resetCache ();
95+
9696 Thread thread = Thread .currentThread ();
9797 ClassLoader previousClassloader = thread .getContextClassLoader ();
9898 thread .setContextClassLoader (AptProcessor .class .getClassLoader ());
@@ -102,6 +102,19 @@ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment
102102 try {
103103 doProcess (annotations , roundEnv );
104104 return true ;
105+ } catch (CodegenException e ) {
106+ Object originatingElement = e .originatingElement ()
107+ .orElse (null );
108+ if (originatingElement instanceof Element element ) {
109+ processingEnv .getMessager ().printError (e .getMessage (), element );
110+ } else if (originatingElement instanceof TypeName typeName ) {
111+ processingEnv .getMessager ().printError (e .getMessage () + ", source: " + typeName .fqName ());
112+ } else {
113+ if (originatingElement != null ) {
114+ processingEnv .getMessager ().printError (e .getMessage () + ", source: " + originatingElement );
115+ }
116+ }
117+ throw e ;
105118 } finally {
106119 thread .setContextClassLoader (previousClassloader );
107120 }
@@ -115,32 +128,96 @@ private void doProcess(Set<? extends TypeElement> annotations, RoundEnvironment
115128 return ;
116129 }
117130
118- if (annotations .isEmpty ()) {
131+ Set <UsedAnnotation > usedAnnotations = usedAnnotations (annotations );
132+
133+ if (usedAnnotations .isEmpty ()) {
119134 // no annotations, no types, still call the codegen, maybe it has something to do
120135 codegen .process (List .of ());
121136 return ;
122137 }
123138
124- List <TypeInfo > allTypes = discoverTypes (annotations , roundEnv );
139+ List <TypeInfo > allTypes = discoverTypes (usedAnnotations , roundEnv );
125140 codegen .process (allTypes );
126141 }
127142
128- private List <TypeInfo > discoverTypes (Set <? extends TypeElement > annotations , RoundEnvironment roundEnv ) {
143+ private Set <UsedAnnotation > usedAnnotations (Set <? extends TypeElement > annotations ) {
144+ var exactTypes = codegen .supportedAnnotations ()
145+ .stream ()
146+ .map (TypeName ::fqName )
147+ .collect (Collectors .toSet ());
148+ var prefixes = codegen .supportedAnnotationPackagePrefixes ();
149+
150+ Set <UsedAnnotation > result = new HashSet <>();
151+
152+ for (TypeElement annotation : annotations ) {
153+ TypeName typeName = TypeName .create (annotation .getQualifiedName ().toString ());
154+
155+ /*
156+ find meta annotations that are supported:
157+ - annotation that annotates the current annotation
158+ */
159+ Set <TypeName > supportedAnnotations = new HashSet <>();
160+ if (supportedAnnotation (exactTypes , prefixes , typeName )) {
161+ supportedAnnotations .add (typeName );
162+ }
163+ addSupportedAnnotations (exactTypes , prefixes , supportedAnnotations , typeName );
164+ if (!supportedAnnotations .isEmpty ()) {
165+ result .add (new UsedAnnotation (typeName , annotation , supportedAnnotations ));
166+ }
167+ }
168+
169+ return result ;
170+ }
171+
172+ private boolean supportedAnnotation (Set <String > exactTypes , Set <String > prefixes , TypeName annotationType ) {
173+ if (exactTypes .contains (annotationType .fqName ())) {
174+ return true ;
175+ }
176+ String packagePrefix = annotationType .packageName () + "." ;
177+ for (String prefix : prefixes ) {
178+ if (packagePrefix .startsWith (prefix )) {
179+ return true ;
180+ }
181+ }
182+ return false ;
183+ }
184+
185+ private void addSupportedAnnotations (Set <String > exactTypes ,
186+ Set <String > prefixes ,
187+ Set <TypeName > supportedAnnotations ,
188+ TypeName annotationType ) {
189+ Optional <TypeInfo > foundInfo = AptTypeInfoFactory .create (ctx , annotationType );
190+ if (foundInfo .isPresent ()) {
191+ TypeInfo annotationInfo = foundInfo .get ();
192+ List <Annotation > annotations = annotationInfo .annotations ();
193+ for (Annotation annotation : annotations ) {
194+ TypeName typeName = annotation .typeName ();
195+ if (supportedAnnotation (exactTypes , prefixes , typeName )) {
196+ if (supportedAnnotations .add (typeName )) {
197+ addSupportedAnnotations (exactTypes , prefixes , supportedAnnotations , typeName );
198+ }
199+ }
200+ }
201+ }
202+ }
203+
204+ private List <TypeInfo > discoverTypes (Set <UsedAnnotation > annotations , RoundEnvironment roundEnv ) {
129205 // we must discover all types that should be handled, create TypeInfo and only then check if these should be processed
130206 // as we may replace annotations, elements, and whole types.
131207
132208 // first collect all types (group by type name, so we do not have duplicity)
133209 Map <TypeName , TypeElement > types = new HashMap <>();
134210
135- for (TypeElement annotation : annotations ) {
136- Set <? extends Element > elementsAnnotatedWith = roundEnv .getElementsAnnotatedWith (annotation );
211+ for (UsedAnnotation annotation : annotations ) {
212+ TypeElement annotationElement = annotation .annotationElement ();
213+ Set <? extends Element > elementsAnnotatedWith = roundEnv .getElementsAnnotatedWith (annotationElement );
137214 for (Element element : elementsAnnotatedWith ) {
138215 ElementKind kind = element .getKind ();
139216 switch (kind ) {
140- case ENUM , INTERFACE , CLASS , ANNOTATION_TYPE , RECORD -> addType (types , element , element , annotation );
217+ case ENUM , INTERFACE , CLASS , ANNOTATION_TYPE , RECORD -> addType (types , element , element , annotationElement );
141218 case ENUM_CONSTANT , CONSTRUCTOR , METHOD , FIELD , STATIC_INIT , INSTANCE_INIT , RECORD_COMPONENT ->
142- addType (types , element .getEnclosingElement (), element , annotation );
143- case PARAMETER -> addType (types , element .getEnclosingElement ().getEnclosingElement (), element , annotation );
219+ addType (types , element .getEnclosingElement (), element , annotationElement );
220+ case PARAMETER -> addType (types , element .getEnclosingElement ().getEnclosingElement (), element , annotationElement );
144221 default -> ctx .logger ().log (TRACE , "Ignoring annotated element, not supported: " + element + ", kind: " + kind );
145222 }
146223 }
@@ -177,4 +254,16 @@ private void addType(Map<TypeName, TypeElement> types,
177254 processedElement );
178255 }
179256 }
257+
258+ /**
259+ * Annotation that annotates a processed type and that must be processed.
260+ *
261+ * @param annotationType annotation on processed type
262+ * @param annotationElement element of the annotation
263+ * @param supportedAnnotations annotations that are supported (either the actual annotation, or meta-annotations)
264+ */
265+ private record UsedAnnotation (TypeName annotationType ,
266+ TypeElement annotationElement ,
267+ Set <TypeName > supportedAnnotations ) {
268+ }
180269}
0 commit comments