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  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 ));
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  typeElement  = roundEnv .getRootElement (AbstractIntermediateGenericProperties .class );
65+ 			assertThat (getTypeOfField (typeUtils , typeElement , "name" )).hasToString (String .class .getName ());
66+ 			assertThat (getTypeOfField (typeUtils , typeElement , "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  typeElement  = roundEnv .getRootElement (AbstractGenericProperties .class );
75+ 			assertThat (getTypeOfField (typeUtils , typeElement , "name" )).hasToString (Object .class .getName ());
76+ 			assertThat (getTypeOfField (typeUtils , typeElement , "mappings" ))
77+ 				.hasToString (constructMapType (Object .class , Object .class ));
78+ 		});
79+ 	}
80+ 
81+ 	@ Test 
82+ 	void  resolveTypeWithUnresolvedGenericProperties () {
83+ 		process (UnresolvedGenericProperties .class , (roundEnv , typeUtils ) -> {
84+ 			TypeElement  typeElement  = roundEnv .getRootElement (UnresolvedGenericProperties .class );
85+ 			assertThat (getTypeOfField (typeUtils , typeElement , "name" )).hasToString (String .class .getName ());
86+ 			assertThat (getTypeOfField (typeUtils , typeElement , "mappings" ))
87+ 				.hasToString (constructMapType (Number .class , Object .class ));
88+ 		});
89+ 	}
7790
91+ 	@ Test 
92+ 	void  resolvedTypeMixGenericNamePropertiesProperties () {
93+ 		process (MixGenericNameProperties .class , (roundEnv , typeUtils ) -> {
94+ 			TypeElement  typeElement  = roundEnv .getRootElement (MixGenericNameProperties .class );
95+ 			assertThat (getTypeOfField (typeUtils , typeElement , "name" )).hasToString (String .class .getName ());
96+ 			assertThat (getTypeOfField (typeUtils , typeElement , "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  getTypeOfField (TypeUtils  typeUtils , TypeElement  typeElement , String  name ) {
115+ 		TypeMirror  field  = findField (typeUtils , typeElement , name );
116+ 		if  (field  == null ) {
117+ 			throw  new  IllegalStateException ("Unable to find field '"  + name  + "' in "  + typeElement );
118+ 		}
119+ 		return  typeUtils .getType (typeElement , field );
120+ 	}
121+ 
122+ 	private  TypeMirror  findField (TypeUtils  typeUtils , TypeElement  typeElement , String  name ) {
123+ 		for  (VariableElement  variableElement  : ElementFilter .fieldsIn (typeElement .getEnclosedElements ())) {
124+ 			if  (variableElement .getSimpleName ().contentEquals (name )) {
125+ 				return  variableElement .asType ();
126+ 			}
127+ 		}
128+ 		TypeMirror  superclass  = typeElement .getSuperclass ();
129+ 		if  (superclass  != null  && !superclass .toString ().equals (Object .class .getName ())) {
130+ 			return  findField (typeUtils , (TypeElement ) typeUtils .asElement (superclass ), name );
131+ 		}
132+ 		return  null ;
133+ 	}
134+ 
90135}
0 commit comments