11/*
2- * Copyright 2012-2023 the original author or authors.
2+ * Copyright 2012-2025 the original author or authors.
33 *
44 * Licensed under the Apache License, Version 2.0 (the "License");
55 * you may not use this file except in compliance with the License.
1717package org .springframework .boot .configurationprocessor ;
1818
1919import java .time .Duration ;
20+ import java .util .Map ;
2021import java .util .function .BiConsumer ;
2122
23+ import javax .lang .model .element .TypeElement ;
24+ import javax .lang .model .element .VariableElement ;
25+ import javax .lang .model .type .TypeMirror ;
26+ import javax .lang .model .util .ElementFilter ;
27+
2228import org .junit .jupiter .api .Test ;
2329
24- import org .springframework .boot .configurationprocessor .TypeUtils .TypeDescriptor ;
2530import org .springframework .boot .configurationprocessor .test .RoundEnvironmentTester ;
2631import org .springframework .boot .configurationprocessor .test .TestableAnnotationProcessor ;
2732import org .springframework .boot .configurationsample .generic .AbstractGenericProperties ;
2833import org .springframework .boot .configurationsample .generic .AbstractIntermediateGenericProperties ;
34+ import org .springframework .boot .configurationsample .generic .MixGenericNameProperties ;
2935import org .springframework .boot .configurationsample .generic .SimpleGenericProperties ;
36+ import org .springframework .boot .configurationsample .generic .UnresolvedGenericProperties ;
3037import org .springframework .core .test .tools .SourceFile ;
3138import org .springframework .core .test .tools .TestCompiler ;
3239
4148class TypeUtilsTests {
4249
4350 @ Test
44- void resolveTypeDescriptorOnConcreteClass () {
51+ void resolveTypeOnConcreteClass () {
4552 process (SimpleGenericProperties .class , (roundEnv , typeUtils ) -> {
46- TypeDescriptor typeDescriptor = typeUtils
47- .resolveTypeDescriptor (roundEnv .getRootElement (SimpleGenericProperties .class ));
48- assertThat (typeDescriptor .getGenerics ().keySet ().stream ().map (Object ::toString )).containsOnly ("A" , "B" ,
49- "C" );
50- assertThat (typeDescriptor .resolveGeneric ("A" )).hasToString (String .class .getName ());
51- assertThat (typeDescriptor .resolveGeneric ("B" )).hasToString (Integer .class .getName ());
52- assertThat (typeDescriptor .resolveGeneric ("C" )).hasToString (Duration .class .getName ());
53+ TypeElement clazz = roundEnv .getRootElement (SimpleGenericProperties .class );
54+ assertThat (getFieldType (typeUtils , clazz , "name" )).hasToString (String .class .getName ());
55+ assertThat (getFieldType (typeUtils , clazz , "mappings" ))
56+ .hasToString (constructMapType (Integer .class , Duration .class ));
5357
5458 });
5559 }
5660
5761 @ Test
58- void resolveTypeDescriptorOnIntermediateClass () {
62+ void resolveTypeOnIntermediateClass () {
5963 process (AbstractIntermediateGenericProperties .class , (roundEnv , typeUtils ) -> {
60- TypeDescriptor typeDescriptor = typeUtils
61- .resolveTypeDescriptor (roundEnv .getRootElement (AbstractIntermediateGenericProperties .class ));
62- assertThat (typeDescriptor .getGenerics ().keySet ().stream ().map (Object ::toString )).containsOnly ("A" , "B" ,
63- "C" );
64- assertThat (typeDescriptor .resolveGeneric ("A" )).hasToString (String .class .getName ());
65- assertThat (typeDescriptor .resolveGeneric ("B" )).hasToString (Integer .class .getName ());
66- assertThat (typeDescriptor .resolveGeneric ("C" )).hasToString ("C" );
64+ TypeElement clazz = roundEnv .getRootElement (AbstractIntermediateGenericProperties .class );
65+ assertThat (getFieldType (typeUtils , clazz , "name" )).hasToString (String .class .getName ());
66+ assertThat (getFieldType (typeUtils , clazz , "mappings" ))
67+ .hasToString (constructMapType (Integer .class , Object .class ));
6768 });
6869 }
6970
7071 @ Test
71- void resolveTypeDescriptorWithOnlyGenerics () {
72+ void resolveTypeWithOnlyGenerics () {
7273 process (AbstractGenericProperties .class , (roundEnv , typeUtils ) -> {
73- TypeDescriptor typeDescriptor = typeUtils
74- .resolveTypeDescriptor (roundEnv .getRootElement (AbstractGenericProperties .class ));
75- assertThat (typeDescriptor .getGenerics ().keySet ().stream ().map (Object ::toString )).containsOnly ("A" , "B" ,
76- "C" );
74+ TypeElement clazz = roundEnv .getRootElement (AbstractGenericProperties .class );
75+ assertThat (getFieldType (typeUtils , clazz , "name" )).hasToString (Object .class .getName ());
76+ assertThat (getFieldType (typeUtils , clazz , "mappings" ))
77+ .hasToString (constructMapType (Object .class , Object .class ));
78+ });
79+ }
80+
81+ @ Test
82+ void resolveTypeWithUnresolvedGenericProperties () {
83+ process (UnresolvedGenericProperties .class , (roundEnv , typeUtils ) -> {
84+ TypeElement clazz = roundEnv .getRootElement (UnresolvedGenericProperties .class );
85+ assertThat (getFieldType (typeUtils , clazz , "name" )).hasToString (String .class .getName ());
86+ assertThat (getFieldType (typeUtils , clazz , "mappings" ))
87+ .hasToString (constructMapType (Number .class , Object .class ));
88+ });
89+ }
7790
91+ @ Test
92+ void resolvedTypeMixGenericNamePropertiesProperties () {
93+ process (MixGenericNameProperties .class , (roundEnv , typeUtils ) -> {
94+ TypeElement clazz = roundEnv .getRootElement (MixGenericNameProperties .class );
95+ assertThat (getFieldType (typeUtils , clazz , "name" )).hasToString (String .class .getName ());
96+ assertThat (getFieldType (typeUtils , clazz , "mappings" ))
97+ .hasToString (constructMapType (Number .class , Object .class ));
7898 });
7999 }
80100
@@ -87,4 +107,29 @@ private void process(Class<?> target, BiConsumer<RoundEnvironmentTester, TypeUti
87107 });
88108 }
89109
110+ private String constructMapType (Class <?> keyType , Class <?> valueType ) {
111+ return "%s<%s,%s>" .formatted (Map .class .getName (), keyType .getName (), valueType .getName ());
112+ }
113+
114+ private String getFieldType (TypeUtils typeUtils , TypeElement clazz , String name ) {
115+ TypeMirror field = getField (typeUtils , clazz , name );
116+ if (field == null ) {
117+ throw new IllegalStateException ("Unable to find field '" + name + "' in " + clazz );
118+ }
119+ return typeUtils .getType (clazz , field );
120+ }
121+
122+ private TypeMirror getField (TypeUtils typeUtils , TypeElement clazz , String name ) {
123+ for (VariableElement variableElement : ElementFilter .fieldsIn (clazz .getEnclosedElements ())) {
124+ if (variableElement .getSimpleName ().contentEquals (name )) {
125+ return variableElement .asType ();
126+ }
127+ }
128+ TypeMirror superclass = clazz .getSuperclass ();
129+ if (superclass != null && !superclass .toString ().equals (Object .class .getName ())) {
130+ return getField (typeUtils , (TypeElement ) typeUtils .asElement (superclass ), name );
131+ }
132+ return null ;
133+ }
134+
90135}
0 commit comments