66import org .springframework .boot .configurationprocessor .metadata .ItemMetadata ;
77import org .springframework .boot .configurationprocessor .metadata .JsonMarshaller ;
88import org .springframework .boot .configurationprocessor .support .ConventionUtils ;
9+ import org .springframework .boot .context .properties .NestedConfigurationProperty ;
910
1011import javax .annotation .processing .AbstractProcessor ;
1112import javax .annotation .processing .ProcessingEnvironment ;
1213import javax .annotation .processing .RoundEnvironment ;
1314import javax .annotation .processing .SupportedSourceVersion ;
1415import javax .lang .model .SourceVersion ;
16+ import javax .lang .model .element .Element ;
1517import javax .lang .model .element .ElementKind ;
1618import javax .lang .model .element .TypeElement ;
1719import javax .lang .model .element .VariableElement ;
20+ import javax .lang .model .type .TypeKind ;
21+ import javax .lang .model .type .TypeMirror ;
1822import javax .tools .FileObject ;
1923import javax .tools .StandardLocation ;
2024import java .io .IOException ;
2125import java .io .OutputStream ;
26+ import java .util .HashSet ;
2227import java .util .List ;
2328import java .util .Map ;
2429import java .util .Set ;
2530
2631@ SupportedSourceVersion (SourceVersion .RELEASE_21 )
2732public class AdditionalMetadataProcessor extends AbstractProcessor {
2833
29- private static final String ADDITIONAL_METADATA_PATH = "META-INF/additional -spring-configuration-metadata.json" ;
34+ private static final String ADDITIONAL_METADATA_PATH = "META-INF/documentation -spring-configuration-metadata.json" ;
3035
3136 private final ConfigurationMetadata configurationMetadata = new ConfigurationMetadata ();
37+ private final Set <ElementWithGroup > processedElements = new HashSet <>();
3238
3339 private FieldValuesParser fieldValuesParser ;
3440
@@ -55,53 +61,97 @@ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment
5561
5662 roundEnv .getElementsAnnotatedWith (AdditionalConfigurationMetadata .class ).forEach (element -> {
5763
58- String group = element .getAnnotation (AdditionalConfigurationMetadata .class ).group ();
59- String elementType = ((TypeElement ) element ).getQualifiedName ().toString ();
64+ String [] groups = element .getAnnotation (AdditionalConfigurationMetadata .class ).groups ();
6065
61- Map < String , Object > fieldValues ;
66+ for ( String group : groups ) {
6267
63- try {
64- fieldValues = fieldValuesParser .getFieldValues ((TypeElement ) element );
65- } catch (Exception e ) {
66- throw new RuntimeException (e );
67- }
68+ String elementType = ((TypeElement ) element ).getQualifiedName ().toString ();
69+
70+ configurationMetadata .addIfMissing (ItemMetadata .newGroup (ConventionUtils .toDashedCase (group ), elementType , elementType , null ));
6871
69- configurationMetadata .add (ItemMetadata .newGroup (ConventionUtils .toDashedCase (group ), elementType , elementType , null ));
70-
71- List <VariableElement > variableElements = element .getEnclosedElements ().stream ()
72- .filter (e -> e .getKind () == ElementKind .FIELD )
73- .map (e -> (VariableElement ) e )
74- .toList ();
75-
76- for (VariableElement variableElement : variableElements ) {
77-
78- String fieldName = variableElement .getSimpleName ().toString ();
79- String name = group + "." + fieldName ;
80- String docComment = getDocComment (variableElement );
81-
82- configurationMetadata .add (
83- ItemMetadata .newProperty (
84- null ,
85- name ,
86- variableElement .asType ().toString (),
87- elementType ,
88- null ,
89- docComment ,
90- fieldValues .get (fieldName ),
91- null
92- )
93- );
72+ try {
73+ processElement (element , group , elementType );
74+ } catch (Exception e ) {
75+ processingEnv .getMessager ().printError ("[AdditionalMetadataProcessor]: " + e .getMessage (), element );
76+ }
9477 }
9578 });
9679
9780 if (roundEnv .processingOver ()) {
9881
99- writeJson ();
82+ try {
83+ writeJson ();
84+ } catch (Exception e ) {
85+ processingEnv .getMessager ().printError ("[AdditionalMetadataProcessor]: " + e .getMessage ());
86+ }
10087 }
10188
10289 return false ;
10390 }
10491
92+ private void processElement (Element element , String group , String elementType ) {
93+
94+ ElementWithGroup elementWithGroup = new ElementWithGroup (group , element );
95+
96+ if (processedElements .contains (elementWithGroup )) {
97+ return ;
98+ }
99+
100+ processedElements .add (elementWithGroup );
101+
102+ Map <String , Object > fieldValues ;
103+
104+ try {
105+ fieldValues = fieldValuesParser .getFieldValues ((TypeElement ) element );
106+ } catch (Exception e ) {
107+ throw new RuntimeException (e );
108+ }
109+
110+ List <VariableElement > variableElements = element .getEnclosedElements ().stream ()
111+ .filter (e -> e .getKind () == ElementKind .FIELD )
112+ .map (e -> (VariableElement ) e )
113+ .toList ();
114+
115+ for (VariableElement variableElement : variableElements ) {
116+
117+ String fieldName = variableElement .getSimpleName ().toString ();
118+
119+ NestedConfigurationProperty nestedConfigurationProperty = variableElement .getAnnotation (NestedConfigurationProperty .class );
120+
121+ if (nestedConfigurationProperty != null ) {
122+ Element nestedElement = processingEnv .getTypeUtils ().asElement (variableElement .asType ());
123+ processElement (nestedElement , group + "." + fieldName , elementType );
124+ } else {
125+ addProperty (group , elementType , variableElement , fieldValues , fieldName );
126+ }
127+ }
128+ }
129+
130+ private void addProperty (
131+ String group ,
132+ String elementType ,
133+ VariableElement variableElement ,
134+ Map <String , Object > fieldValues ,
135+ String fieldName
136+ ) {
137+
138+ String name = group + "." + fieldName ;
139+ String docComment = getDocComment (variableElement );
140+
141+ configurationMetadata .addIfMissing (
142+ ItemMetadata .newProperty (
143+ null ,
144+ name ,
145+ variableElement .asType ().toString (),
146+ elementType ,
147+ null ,
148+ docComment ,
149+ fieldValues .get (fieldName ),
150+ null
151+ )
152+ );
153+ }
154+
105155 private void writeJson () {
106156 try {
107157 FileObject resource = processingEnv .getFiler ().createResource (StandardLocation .CLASS_OUTPUT , "" , ADDITIONAL_METADATA_PATH );
@@ -120,6 +170,10 @@ private String getDocComment(VariableElement variableElement) {
120170
121171 String docComment = processingEnv .getElementUtils ().getDocComment (variableElement );
122172
173+ if (docComment == null ) {
174+ return null ;
175+ }
176+
123177 docComment = docComment .replaceAll ("\r " , " " );
124178 docComment = docComment .replaceAll ("\n " , " " );
125179
0 commit comments