Skip to content

Commit 34f28b4

Browse files
committed
Merge branch '2.1.x'
2 parents 0c45019 + 91a005f commit 34f28b4

File tree

8 files changed

+448
-24
lines changed

8 files changed

+448
-24
lines changed

spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2018 the original author or authors.
2+
* Copyright 2012-2019 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.
@@ -292,7 +292,7 @@ private void processSimpleTypes(String prefix, TypeElement element,
292292
boolean isNested = isNested(returnTypeElement, field, element);
293293
boolean isCollection = this.typeUtils.isCollectionOrMap(returnType);
294294
if (!isExcluded && !isNested && (setter != null || isCollection)) {
295-
String dataType = this.typeUtils.getType(returnType);
295+
String dataType = this.typeUtils.getType(element, returnType);
296296
String sourceType = this.typeUtils.getQualifiedName(element);
297297
String description = this.typeUtils.getJavaDoc(field);
298298
Object defaultValue = fieldValues.get(name);
@@ -335,7 +335,7 @@ private void processSimpleLombokTypes(String prefix, TypeElement element,
335335
boolean isCollection = this.typeUtils.isCollectionOrMap(returnType);
336336
boolean hasSetter = hasLombokSetter(field, element);
337337
if (!isExcluded && !isNested && (hasSetter || isCollection)) {
338-
String dataType = this.typeUtils.getType(returnType);
338+
String dataType = this.typeUtils.getType(element, returnType);
339339
String sourceType = this.typeUtils.getQualifiedName(element);
340340
String description = this.typeUtils.getJavaDoc(field);
341341
Object defaultValue = fieldValues.get(name);

spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/TypeUtils.java

Lines changed: 129 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2018 the original author or authors.
2+
* Copyright 2012-2019 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.
@@ -21,7 +21,9 @@
2121
import java.util.Collections;
2222
import java.util.EnumMap;
2323
import java.util.HashMap;
24+
import java.util.List;
2425
import java.util.Map;
26+
import java.util.Map.Entry;
2527
import java.util.regex.Pattern;
2628
import java.util.stream.Collectors;
2729

@@ -33,8 +35,10 @@
3335
import javax.lang.model.type.PrimitiveType;
3436
import javax.lang.model.type.TypeKind;
3537
import javax.lang.model.type.TypeMirror;
38+
import javax.lang.model.type.TypeVariable;
3639
import javax.lang.model.util.SimpleTypeVisitor8;
3740
import javax.lang.model.util.Types;
41+
import javax.tools.Diagnostic.Kind;
3842

3943
/**
4044
* Type Utilities.
@@ -73,18 +77,22 @@ class TypeUtils {
7377

7478
private final ProcessingEnvironment env;
7579

80+
private final Types types;
81+
7682
private final TypeExtractor typeExtractor;
7783

7884
private final TypeMirror collectionType;
7985

8086
private final TypeMirror mapType;
8187

88+
private final Map<TypeElement, TypeDescriptor> typeDescriptors = new HashMap<>();
89+
8290
TypeUtils(ProcessingEnvironment env) {
8391
this.env = env;
84-
Types types = env.getTypeUtils();
85-
this.typeExtractor = new TypeExtractor(types);
86-
this.collectionType = getDeclaredType(types, Collection.class, 1);
87-
this.mapType = getDeclaredType(types, Map.class, 2);
92+
this.types = env.getTypeUtils();
93+
this.typeExtractor = new TypeExtractor(this.types);
94+
this.collectionType = getDeclaredType(this.types, Collection.class, 1);
95+
this.mapType = getDeclaredType(this.types, Map.class, 2);
8896
}
8997

9098
private TypeMirror getDeclaredType(Types types, Class<?> typeClass,
@@ -115,14 +123,15 @@ public String getQualifiedName(Element element) {
115123
/**
116124
* Return the type of the specified {@link TypeMirror} including all its generic
117125
* information.
126+
* @param element the {@link TypeElement} in which this {@code type} is declared
118127
* @param type the type to handle
119128
* @return a representation of the type including all its generic information
120129
*/
121-
public String getType(TypeMirror type) {
130+
public String getType(TypeElement element, TypeMirror type) {
122131
if (type == null) {
123132
return null;
124133
}
125-
return type.accept(this.typeExtractor, null);
134+
return type.accept(this.typeExtractor, createTypeDescriptor(element));
126135
}
127136

128137
public boolean isCollectionOrMap(TypeMirror type) {
@@ -160,11 +169,49 @@ private TypeKind getPrimitiveFor(TypeMirror type) {
160169
return WRAPPER_TO_PRIMITIVE.get(type.toString());
161170
}
162171

172+
TypeDescriptor resolveTypeDescriptor(TypeElement element) {
173+
if (this.typeDescriptors.containsKey(element)) {
174+
return this.typeDescriptors.get(element);
175+
}
176+
return createTypeDescriptor(element);
177+
}
178+
179+
private TypeDescriptor createTypeDescriptor(TypeElement element) {
180+
TypeDescriptor descriptor = new TypeDescriptor();
181+
process(descriptor, element.asType());
182+
this.typeDescriptors.put(element, descriptor);
183+
return descriptor;
184+
}
185+
186+
private void process(TypeDescriptor descriptor, TypeMirror type) {
187+
try {
188+
if (type.getKind() == TypeKind.DECLARED) {
189+
DeclaredType declaredType = (DeclaredType) type;
190+
DeclaredType freshType = (DeclaredType) this.env.getElementUtils()
191+
.getTypeElement(this.types.asElement(type).toString()).asType();
192+
List<? extends TypeMirror> arguments = declaredType.getTypeArguments();
193+
for (int i = 0; i < arguments.size(); i++) {
194+
TypeMirror specificType = arguments.get(i);
195+
TypeMirror signatureType = freshType.getTypeArguments().get(i);
196+
descriptor.registerIfNecessary(signatureType, specificType);
197+
}
198+
TypeElement element = (TypeElement) this.types.asElement(type);
199+
process(descriptor, element.getSuperclass());
200+
}
201+
}
202+
catch (Exception ex) {
203+
this.env.getMessager().printMessage(Kind.WARNING,
204+
"Failed to generated type descriptor for " + type,
205+
this.types.asElement(type));
206+
}
207+
}
208+
163209
/**
164210
* A visitor that extracts the fully qualified name of a type, including generic
165211
* information.
166212
*/
167-
private static class TypeExtractor extends SimpleTypeVisitor8<String, Void> {
213+
private static class TypeExtractor
214+
extends SimpleTypeVisitor8<String, TypeDescriptor> {
168215

169216
private final Types types;
170217

@@ -173,34 +220,60 @@ private static class TypeExtractor extends SimpleTypeVisitor8<String, Void> {
173220
}
174221

175222
@Override
176-
public String visitDeclared(DeclaredType type, Void none) {
223+
public String visitDeclared(DeclaredType type, TypeDescriptor descriptor) {
177224
TypeElement enclosingElement = getEnclosingTypeElement(type);
178-
if (enclosingElement != null) {
179-
return getQualifiedName(enclosingElement) + "$"
180-
+ type.asElement().getSimpleName();
181-
}
182-
String qualifiedName = getQualifiedName(type.asElement());
225+
String qualifiedName = determineQualifiedName(type, enclosingElement);
183226
if (type.getTypeArguments().isEmpty()) {
184227
return qualifiedName;
185228
}
186229
StringBuilder name = new StringBuilder();
187230
name.append(qualifiedName);
188231
name.append("<").append(type.getTypeArguments().stream()
189-
.map(TypeMirror::toString).collect(Collectors.joining(",")))
232+
.map((t) -> visit(t, descriptor)).collect(Collectors.joining(",")))
190233
.append(">");
191234
return name.toString();
192235
}
193236

237+
private String determineQualifiedName(DeclaredType type,
238+
TypeElement enclosingElement) {
239+
if (enclosingElement != null) {
240+
return getQualifiedName(enclosingElement) + "$"
241+
+ type.asElement().getSimpleName();
242+
}
243+
return getQualifiedName(type.asElement());
244+
}
245+
246+
@Override
247+
public String visitTypeVariable(TypeVariable t, TypeDescriptor descriptor) {
248+
TypeMirror typeMirror = descriptor.resolveGeneric(t);
249+
if (typeMirror != null) {
250+
if (typeMirror instanceof TypeVariable) {
251+
// Still unresolved, let's use upper bound
252+
return visit(((TypeVariable) typeMirror).getUpperBound(), descriptor);
253+
}
254+
else {
255+
return visit(typeMirror, descriptor);
256+
}
257+
}
258+
// Unresolved generics, use upper bound
259+
return visit(t.getUpperBound(), descriptor);
260+
}
261+
194262
@Override
195-
public String visitArray(ArrayType t, Void none) {
196-
return t.getComponentType().accept(this, none) + "[]";
263+
public String visitArray(ArrayType t, TypeDescriptor descriptor) {
264+
return t.getComponentType().accept(this, descriptor) + "[]";
197265
}
198266

199267
@Override
200-
public String visitPrimitive(PrimitiveType t, Void none) {
268+
public String visitPrimitive(PrimitiveType t, TypeDescriptor descriptor) {
201269
return this.types.boxedClass(t).getQualifiedName().toString();
202270
}
203271

272+
@Override
273+
protected String defaultAction(TypeMirror t, TypeDescriptor descriptor) {
274+
return t.toString();
275+
}
276+
204277
public String getQualifiedName(Element element) {
205278
if (element == null) {
206279
return null;
@@ -230,4 +303,42 @@ private TypeElement getEnclosingTypeElement(TypeMirror type) {
230303

231304
}
232305

306+
/**
307+
* Descriptor for a given type.
308+
*/
309+
static class TypeDescriptor {
310+
311+
private final Map<TypeVariable, TypeMirror> generics = new HashMap<>();
312+
313+
public Map<TypeVariable, TypeMirror> getGenerics() {
314+
return Collections.unmodifiableMap(this.generics);
315+
}
316+
317+
public TypeMirror resolveGeneric(TypeVariable typeVariable) {
318+
return resolveGeneric(getParameterName(typeVariable));
319+
}
320+
321+
public TypeMirror resolveGeneric(String parameterName) {
322+
return this.generics.entrySet().stream()
323+
.filter((e) -> getParameterName(e.getKey()).equals(parameterName))
324+
.findFirst().map(Entry::getValue).orElse(null);
325+
}
326+
327+
private void registerIfNecessary(TypeMirror variable, TypeMirror resolution) {
328+
if (variable instanceof TypeVariable) {
329+
TypeVariable typeVariable = (TypeVariable) variable;
330+
if (this.generics.keySet().stream()
331+
.noneMatch((candidate) -> getParameterName(candidate)
332+
.equals(getParameterName(typeVariable)))) {
333+
this.generics.put(typeVariable, resolution);
334+
}
335+
}
336+
}
337+
338+
private String getParameterName(TypeVariable typeVariable) {
339+
return typeVariable.asElement().getSimpleName().toString();
340+
}
341+
342+
}
343+
233344
}

spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessorTests.java

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2018 the original author or authors.
2+
* Copyright 2012-2019 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.
@@ -45,6 +45,9 @@
4545
import org.springframework.boot.configurationsample.endpoint.SimpleEndpoint;
4646
import org.springframework.boot.configurationsample.endpoint.SpecificEndpoint;
4747
import org.springframework.boot.configurationsample.endpoint.incremental.IncrementalEndpoint;
48+
import org.springframework.boot.configurationsample.generic.AbstractGenericProperties;
49+
import org.springframework.boot.configurationsample.generic.SimpleGenericProperties;
50+
import org.springframework.boot.configurationsample.generic.UnresolvedGenericProperties;
4851
import org.springframework.boot.configurationsample.incremental.BarProperties;
4952
import org.springframework.boot.configurationsample.incremental.FooProperties;
5053
import org.springframework.boot.configurationsample.incremental.RenamedBarProperties;
@@ -292,7 +295,7 @@ public void parseCollectionConfig() {
292295
assertThat(metadata).has(Metadata.withProperty("collection.doubles",
293296
"java.util.List<java.lang.Double>"));
294297
assertThat(metadata).has(Metadata.withProperty("collection.names-to-holders",
295-
"java.util.Map<java.lang.String,org.springframework.boot.configurationsample.simple.SimpleCollectionProperties.Holder<java.lang.String>>"));
298+
"java.util.Map<java.lang.String,org.springframework.boot.configurationsample.simple.SimpleCollectionProperties$Holder<java.lang.String>>"));
296299
}
297300

298301
@Test
@@ -504,6 +507,40 @@ public void invalidDoubleRegistration() {
504507
.withMessageContaining("Compilation failed");
505508
}
506509

510+
@Test
511+
public void simpleGenericProperties() {
512+
ConfigurationMetadata metadata = compile(AbstractGenericProperties.class,
513+
SimpleGenericProperties.class);
514+
assertThat(metadata).has(
515+
Metadata.withGroup("generic").fromSource(SimpleGenericProperties.class));
516+
assertThat(metadata).has(Metadata.withProperty("generic.name", String.class)
517+
.fromSource(SimpleGenericProperties.class)
518+
.withDescription("Generic name.").withDefaultValue(null));
519+
assertThat(metadata).has(Metadata
520+
.withProperty("generic.mappings",
521+
"java.util.Map<java.lang.Integer,java.time.Duration>")
522+
.fromSource(SimpleGenericProperties.class)
523+
.withDescription("Generic mappings.").withDefaultValue(null));
524+
assertThat(metadata.getItems()).hasSize(3);
525+
}
526+
527+
@Test
528+
public void unresolvedGenericProperties() {
529+
ConfigurationMetadata metadata = compile(AbstractGenericProperties.class,
530+
UnresolvedGenericProperties.class);
531+
assertThat(metadata).has(Metadata.withGroup("generic")
532+
.fromSource(UnresolvedGenericProperties.class));
533+
assertThat(metadata).has(Metadata.withProperty("generic.name", String.class)
534+
.fromSource(UnresolvedGenericProperties.class)
535+
.withDescription("Generic name.").withDefaultValue(null));
536+
assertThat(metadata).has(Metadata
537+
.withProperty("generic.mappings",
538+
"java.util.Map<java.lang.Number,java.lang.Object>")
539+
.fromSource(UnresolvedGenericProperties.class)
540+
.withDescription("Generic mappings.").withDefaultValue(null));
541+
assertThat(metadata.getItems()).hasSize(3);
542+
}
543+
507544
@Test
508545
public void genericTypes() {
509546
ConfigurationMetadata metadata = compile(GenericConfig.class);
@@ -518,7 +555,7 @@ public void genericTypes() {
518555
assertThat(metadata).has(Metadata.withProperty("generic.foo.name")
519556
.ofType(String.class).fromSource(GenericConfig.Foo.class));
520557
assertThat(metadata).has(Metadata.withProperty("generic.foo.string-to-bar")
521-
.ofType("java.util.Map<java.lang.String,org.springframework.boot.configurationsample.specific.GenericConfig.Bar<java.lang.Integer>>")
558+
.ofType("java.util.Map<java.lang.String,org.springframework.boot.configurationsample.specific.GenericConfig$Bar<java.lang.Integer>>")
522559
.fromSource(GenericConfig.Foo.class));
523560
assertThat(metadata).has(Metadata.withProperty("generic.foo.string-to-integer")
524561
.ofType("java.util.Map<java.lang.String,java.lang.Integer>")

0 commit comments

Comments
 (0)