diff --git a/spring-boot-integration-tests/pom.xml b/spring-boot-integration-tests/pom.xml
index e9938802ff..83035f030a 100644
--- a/spring-boot-integration-tests/pom.xml
+++ b/spring-boot-integration-tests/pom.xml
@@ -21,6 +21,7 @@
1.8
+ spring-boot-configuration-processor-tests
spring-boot-devtools-tests
spring-boot-integration-tests-embedded-servlet-container
spring-boot-gradle-tests
diff --git a/spring-boot-integration-tests/spring-boot-configuration-processor-tests/pom.xml b/spring-boot-integration-tests/spring-boot-configuration-processor-tests/pom.xml
new file mode 100644
index 0000000000..b53545a47d
--- /dev/null
+++ b/spring-boot-integration-tests/spring-boot-configuration-processor-tests/pom.xml
@@ -0,0 +1,49 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-integration-tests
+ 1.5.10.BUILD-SNAPSHOT
+
+ spring-boot-configuration-processor-tests
+ Spring Boot Configuration Processor Tests
+ ${project.name}
+ http://projects.spring.io/spring-boot/
+
+ Pivotal Software, Inc.
+ http://www.spring.io
+
+
+ ${basedir}/../..
+
+
+
+ org.springframework.boot
+ spring-boot
+
+
+ org.springframework.boot
+ spring-boot-configuration-metadata
+
+
+ javax.validation
+ validation-api
+
+ 2.0.1.Final
+
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
diff --git a/spring-boot-integration-tests/spring-boot-configuration-processor-tests/src/main/java/com/example/AnnotatedSample.java b/spring-boot-integration-tests/spring-boot-configuration-processor-tests/src/main/java/com/example/AnnotatedSample.java
new file mode 100644
index 0000000000..cd13018e8c
--- /dev/null
+++ b/spring-boot-integration-tests/spring-boot-configuration-processor-tests/src/main/java/com/example/AnnotatedSample.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2012-2018 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example;
+
+import javax.validation.Valid;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * Test that a valid type is generated if an annotation is present.
+ *
+ * @author Stephane Nicoll
+ */
+@ConfigurationProperties("annotated")
+public class AnnotatedSample {
+
+ /**
+ * A valid name.
+ */
+ private String name;
+
+ @Valid
+ public String getName() {
+ return this.name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+}
diff --git a/spring-boot-integration-tests/spring-boot-configuration-processor-tests/src/test/java/org/springframework/boot/configurationprocessor/tests/ConfigurationProcessorIntegrationTests.java b/spring-boot-integration-tests/spring-boot-configuration-processor-tests/src/test/java/org/springframework/boot/configurationprocessor/tests/ConfigurationProcessorIntegrationTests.java
new file mode 100644
index 0000000000..8c65e06790
--- /dev/null
+++ b/spring-boot-integration-tests/spring-boot-configuration-processor-tests/src/test/java/org/springframework/boot/configurationprocessor/tests/ConfigurationProcessorIntegrationTests.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2012-2018 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.boot.configurationprocessor.tests;
+
+import java.io.IOException;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty;
+import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepository;
+import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepositoryJsonBuilder;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.Resource;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Integration tests for the configuration metadata annotation processor.
+ *
+ * @author Stephane Nicoll
+ */
+public class ConfigurationProcessorIntegrationTests {
+
+ private static ConfigurationMetadataRepository repository;
+
+ @BeforeClass
+ public static void readMetadata() throws IOException {
+ Resource resource = new ClassPathResource(
+ "META-INF/spring-configuration-metadata.json");
+ assertThat(resource.exists()).isTrue();
+ // Make sure the right file is detected
+ assertThat(resource.getURL().toString()).contains(
+ "spring-boot-configuration-processor-tests");
+ repository = ConfigurationMetadataRepositoryJsonBuilder
+ .create(resource.getInputStream()).build();
+
+ }
+
+ @Test
+ public void extractTypeFromAnnotatedGetter() {
+ ConfigurationMetadataProperty property = repository.getAllProperties().get(
+ "annotated.name");
+ assertThat(property).isNotNull();
+ assertThat(property.getType()).isEqualTo("java.lang.String");
+ }
+
+}
diff --git a/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/TypeUtils.java b/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/TypeUtils.java
index 0fd0c7479b..8bf3cb28ee 100644
--- a/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/TypeUtils.java
+++ b/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/TypeUtils.java
@@ -25,8 +25,10 @@ import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.PrimitiveType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.SimpleTypeVisitor6;
import javax.lang.model.util.Types;
/**
@@ -65,6 +67,8 @@ class TypeUtils {
private final ProcessingEnvironment env;
+ private final TypeExtractor typeExtractor;
+
private final TypeMirror collectionType;
private final TypeMirror mapType;
@@ -72,6 +76,7 @@ class TypeUtils {
TypeUtils(ProcessingEnvironment env) {
this.env = env;
Types types = env.getTypeUtils();
+ this.typeExtractor = new TypeExtractor(types);
this.collectionType = getDeclaredType(types, Collection.class, 1);
this.mapType = getDeclaredType(types, Map.class, 2);
}
@@ -100,20 +105,7 @@ class TypeUtils {
* {@link Class#forName(String)}
*/
public String getQualifiedName(Element element) {
- if (element == null) {
- return null;
- }
- TypeElement enclosingElement = getEnclosingTypeElement(element.asType());
- if (enclosingElement != null) {
- return getQualifiedName(enclosingElement) + "$"
- + ((DeclaredType) element.asType()).asElement().getSimpleName()
- .toString();
- }
- if (element instanceof TypeElement) {
- return ((TypeElement) element).getQualifiedName().toString();
- }
- throw new IllegalStateException(
- "Could not extract qualified name from " + element);
+ return this.typeExtractor.getQualifiedName(element);
}
/**
@@ -126,27 +118,7 @@ class TypeUtils {
if (type == null) {
return null;
}
- Class> wrapper = getWrapperFor(type);
- if (wrapper != null) {
- return wrapper.getName();
- }
- TypeElement enclosingElement = getEnclosingTypeElement(type);
- if (enclosingElement != null) {
- return getQualifiedName(enclosingElement) + "$"
- + ((DeclaredType) type).asElement().getSimpleName().toString();
- }
- return type.toString();
- }
-
- private TypeElement getEnclosingTypeElement(TypeMirror type) {
- if (type instanceof DeclaredType) {
- DeclaredType declaredType = (DeclaredType) type;
- Element enclosingElement = declaredType.asElement().getEnclosingElement();
- if (enclosingElement != null && enclosingElement instanceof TypeElement) {
- return (TypeElement) enclosingElement;
- }
- }
- return null;
+ return type.accept(this.typeExtractor, null);
}
public boolean isCollectionOrMap(TypeMirror type) {
@@ -194,4 +166,62 @@ class TypeUtils {
return WRAPPER_TO_PRIMITIVE.get(type.toString());
}
+
+ /**
+ * A visitor that extracts the full qualified name of a type, including generic
+ * information.
+ */
+ private static class TypeExtractor extends SimpleTypeVisitor6 {
+
+ private final Types types;
+
+ TypeExtractor(Types types) {
+ this.types = types;
+ }
+
+ @Override
+ public String visitDeclared(DeclaredType type, Void none) {
+ TypeElement enclosingElement = getEnclosingTypeElement(type);
+ if (enclosingElement != null) {
+ return getQualifiedName(enclosingElement) + "$"
+ + type.asElement().getSimpleName().toString();
+ }
+ return type.toString();
+ }
+
+ @Override
+ public String visitPrimitive(PrimitiveType t, Void none) {
+ return this.types.boxedClass(t).getQualifiedName().toString();
+ }
+
+ public String getQualifiedName(Element element) {
+ if (element == null) {
+ return null;
+ }
+ TypeElement enclosingElement = getEnclosingTypeElement(element.asType());
+ if (enclosingElement != null) {
+ return getQualifiedName(enclosingElement) + "$"
+ + ((DeclaredType) element.asType()).asElement().getSimpleName()
+ .toString();
+ }
+ if (element instanceof TypeElement) {
+ return ((TypeElement) element).getQualifiedName().toString();
+ }
+ throw new IllegalStateException(
+ "Could not extract qualified name from " + element);
+ }
+
+ private TypeElement getEnclosingTypeElement(TypeMirror type) {
+ if (type instanceof DeclaredType) {
+ DeclaredType declaredType = (DeclaredType) type;
+ Element enclosingElement = declaredType.asElement().getEnclosingElement();
+ if (enclosingElement != null && enclosingElement instanceof TypeElement) {
+ return (TypeElement) enclosingElement;
+ }
+ }
+ return null;
+ }
+
+ }
+
}