diff --git a/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java b/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java index a3d0d968b97..8146b2ec2ef 100644 --- a/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java +++ b/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java @@ -337,10 +337,25 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor if (hasAnnotation(field, nestedConfigurationPropertyAnnotation())) { return true; } - return this.typeUtils.isEnclosedIn(returnType, element) + return (isParentTheSame(returnType, element)) && returnType.getKind() != ElementKind.ENUM; } + private boolean isParentTheSame(Element returnType, TypeElement element) { + if (returnType == null || element == null) { + return false; + } + return getTopLevelType(returnType).equals(getTopLevelType(element)); + } + + private Element getTopLevelType(Element element) { + if ((element.getEnclosingElement() == null) || + !(element.getEnclosingElement() instanceof TypeElement)) { + return element; + } + return getTopLevelType(element.getEnclosingElement()); + } + private boolean isDeprecated(Element element) { if (isElementDeprecated(element)) { return true; diff --git a/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessorTests.java b/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessorTests.java index 10108d81f3b..94647aebc28 100644 --- a/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessorTests.java +++ b/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessorTests.java @@ -65,6 +65,7 @@ import org.springframework.boot.configurationsample.specific.DoubleRegistrationP import org.springframework.boot.configurationsample.specific.ExcludedTypesPojo; import org.springframework.boot.configurationsample.specific.GenericConfig; import org.springframework.boot.configurationsample.specific.InnerClassAnnotatedGetterConfig; +import org.springframework.boot.configurationsample.specific.InnerClassHierachicalProperties; import org.springframework.boot.configurationsample.specific.InnerClassProperties; import org.springframework.boot.configurationsample.specific.InnerClassRootConfig; import org.springframework.boot.configurationsample.specific.InvalidAccessorProperties; @@ -348,6 +349,19 @@ public class ConfigurationMetadataAnnotationProcessorTests { assertThat(metadata).isNotEqualTo(Metadata.withGroup("config.fourth")); } + @Test + public void innerClassPropertiesHierachical() throws Exception { + ConfigurationMetadata metadata = compile(InnerClassHierachicalProperties.class); + assertThat(metadata) + .has(Metadata.withGroup("config.foo").ofType(InnerClassHierachicalProperties.Foo.class)); + assertThat(metadata).has( + Metadata.withGroup("config.foo.bar").ofType(InnerClassHierachicalProperties.Bar.class)); + assertThat(metadata).has( + Metadata.withGroup("config.foo.bar.baz").ofType(InnerClassHierachicalProperties.Foo.Baz.class)); + assertThat(metadata).has(Metadata.withProperty("config.foo.bar.baz.blah")); + assertThat(metadata).has(Metadata.withProperty("config.foo.bar.bling")); + } + @Test public void innerClassAnnotatedGetterConfig() throws Exception { ConfigurationMetadata metadata = compile(InnerClassAnnotatedGetterConfig.class); diff --git a/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/specific/InnerClassHierachicalProperties.java b/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/specific/InnerClassHierachicalProperties.java new file mode 100644 index 00000000000..9ba9bd8c94d --- /dev/null +++ b/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/specific/InnerClassHierachicalProperties.java @@ -0,0 +1,90 @@ +/* + * Copyright 2012-2017 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.configurationsample.specific; + +import org.springframework.boot.configurationsample.ConfigurationProperties; + +/** + * Demonstrate inner classes end up in metadata regardless of + * position in hierarchy and without the use of + * {@link org.springframework.boot.configurationsample.NestedConfigurationProperty}. + * + * @author Madhura Bhave + */ +@ConfigurationProperties(prefix = "config") +public class InnerClassHierachicalProperties { + + private Foo foo; + + public Foo getFoo() { + return this.foo; + } + + public void setFoo(Foo foo) { + this.foo = foo; + } + + public static class Foo { + + private Bar bar; + + public Bar getBar() { + return this.bar; + } + + public void setBar(Bar bar) { + this.bar = bar; + } + + public static class Baz { + + private String blah; + + public String getBlah() { + return this.blah; + } + + public void setBlah(String blah) { + this.blah = blah; + } + } + } + + public static class Bar { + + private String bling; + + private Foo.Baz baz; + + public String getBling() { + return this.bling; + } + + public void setBling(String foo) { + this.bling = foo; + } + + public Foo.Baz getBaz() { + return this.baz; + } + + public void setBaz(Foo.Baz baz) { + this.baz = baz; + } + } + +}