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-
53+ TypeElement typeElement = roundEnv .getRootElement (SimpleGenericProperties .class );
54+ assertThat (getTypeOfField (typeUtils , typeElement , "name" )).hasToString (String .class .getName ());
55+ assertThat (getTypeOfField (typeUtils , typeElement , "mappings" ))
56+ .hasToString (constructMapType (Integer .class , Duration .class ));
5457 });
5558 }
5659
5760 @ Test
58- void resolveTypeDescriptorOnIntermediateClass () {
61+ void resolveTypeOnIntermediateClass () {
5962 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" );
63+ TypeElement typeElement = roundEnv .getRootElement (AbstractIntermediateGenericProperties .class );
64+ assertThat (getTypeOfField (typeUtils , typeElement , "name" )).hasToString (String .class .getName ());
65+ assertThat (getTypeOfField (typeUtils , typeElement , "mappings" ))
66+ .hasToString (constructMapType (Integer .class , Object .class ));
6767 });
6868 }
6969
7070 @ Test
71- void resolveTypeDescriptorWithOnlyGenerics () {
71+ void resolveTypeWithOnlyGenerics () {
7272 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" );
73+ TypeElement typeElement = roundEnv .getRootElement (AbstractGenericProperties .class );
74+ assertThat (getTypeOfField (typeUtils , typeElement , "name" )).hasToString (Object .class .getName ());
75+ assertThat (getTypeOfField (typeUtils , typeElement , "mappings" ))
76+ .hasToString (constructMapType (Object .class , Object .class ));
77+ });
78+ }
7779
80+ @ Test
81+ void resolveTypeWithUnresolvedGenericProperties () {
82+ process (UnresolvedGenericProperties .class , (roundEnv , typeUtils ) -> {
83+ TypeElement typeElement = roundEnv .getRootElement (UnresolvedGenericProperties .class );
84+ assertThat (getTypeOfField (typeUtils , typeElement , "name" )).hasToString (String .class .getName ());
85+ assertThat (getTypeOfField (typeUtils , typeElement , "mappings" ))
86+ .hasToString (constructMapType (Number .class , Object .class ));
87+ });
88+ }
89+
90+ @ Test
91+ void resolvedTypeMixGenericNamePropertiesProperties () {
92+ process (MixGenericNameProperties .class , (roundEnv , typeUtils ) -> {
93+ TypeElement typeElement = roundEnv .getRootElement (MixGenericNameProperties .class );
94+ assertThat (getTypeOfField (typeUtils , typeElement , "name" )).hasToString (String .class .getName ());
95+ assertThat (getTypeOfField (typeUtils , typeElement , "mappings" ))
96+ .hasToString (constructMapType (Number .class , Object .class ));
7897 });
7998 }
8099
@@ -87,4 +106,29 @@ private void process(Class<?> target, BiConsumer<RoundEnvironmentTester, TypeUti
87106 });
88107 }
89108
109+ private String constructMapType (Class <?> keyType , Class <?> valueType ) {
110+ return "%s<%s,%s>" .formatted (Map .class .getName (), keyType .getName (), valueType .getName ());
111+ }
112+
113+ private String getTypeOfField (TypeUtils typeUtils , TypeElement typeElement , String name ) {
114+ TypeMirror field = findField (typeUtils , typeElement , name );
115+ if (field == null ) {
116+ throw new IllegalStateException ("Unable to find field '" + name + "' in " + typeElement );
117+ }
118+ return typeUtils .getType (typeElement , field );
119+ }
120+
121+ private TypeMirror findField (TypeUtils typeUtils , TypeElement typeElement , String name ) {
122+ for (VariableElement variableElement : ElementFilter .fieldsIn (typeElement .getEnclosedElements ())) {
123+ if (variableElement .getSimpleName ().contentEquals (name )) {
124+ return variableElement .asType ();
125+ }
126+ }
127+ TypeMirror superclass = typeElement .getSuperclass ();
128+ if (superclass != null && !superclass .toString ().equals (Object .class .getName ())) {
129+ return findField (typeUtils , (TypeElement ) typeUtils .asElement (superclass ), name );
130+ }
131+ return null ;
132+ }
133+
90134}
0 commit comments