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 14bb80f92d1..001bdf6d65c 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 @@ -56,6 +56,7 @@ import org.springframework.boot.configurationprocessor.metadata.ItemMetadata; * @author Stephane Nicoll * @author Phillip Webb * @author Kris De Volder + * @author Jonas Keßler * @since 1.2.0 */ @SupportedAnnotationTypes({ "*" }) @@ -76,6 +77,11 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor static final String LOMBOK_SETTER_ANNOTATION = "lombok.Setter"; + private static final String LOMBOK_ACCESS_LEVEL = "lombok.AccessLevel"; + + private static final String LOMBOK_ACCESS_LEVEL_PUBLIC = LOMBOK_ACCESS_LEVEL + + ".PUBLIC"; + private MetadataStore metadataStore; private MetadataCollector metadataCollector; @@ -302,16 +308,35 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor } private boolean isLombokField(VariableElement field, TypeElement element) { - return hasAnnotation(field, LOMBOK_GETTER_ANNOTATION) - || hasAnnotation(element, LOMBOK_GETTER_ANNOTATION) - || hasAnnotation(element, LOMBOK_DATA_ANNOTATION); + return hasLombokPublicMethod(field, element, LOMBOK_GETTER_ANNOTATION); } private boolean hasLombokSetter(VariableElement field, TypeElement element) { return !field.getModifiers().contains(Modifier.FINAL) - && (hasAnnotation(field, LOMBOK_SETTER_ANNOTATION) - || hasAnnotation(element, LOMBOK_SETTER_ANNOTATION) - || hasAnnotation(element, LOMBOK_DATA_ANNOTATION)); + && hasLombokPublicMethod(field, element, LOMBOK_SETTER_ANNOTATION); + } + + private boolean hasLombokPublicMethod(VariableElement field, TypeElement element, + String lombokMethodAnnotation) { + AnnotationMirror lombokMethodAnnotationOnField = getAnnotation(field, + lombokMethodAnnotation); + if (lombokMethodAnnotationOnField != null) { + return isLombokPublic(lombokMethodAnnotationOnField); + } + + AnnotationMirror lombokMethodAnnotationOnElement = getAnnotation(element, + lombokMethodAnnotation); + if (lombokMethodAnnotationOnElement != null) { + return isLombokPublic(lombokMethodAnnotationOnElement); + } + + return hasAnnotation(element, LOMBOK_DATA_ANNOTATION); + } + + private boolean isLombokPublic(AnnotationMirror lombokAnnotation) { + return lombokAnnotation.getElementValues().values().stream() + .noneMatch(e -> e.toString().startsWith(LOMBOK_ACCESS_LEVEL) + && !e.toString().equals(LOMBOK_ACCESS_LEVEL_PUBLIC)); } private void processNestedType(String prefix, TypeElement element, 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 9cfacf15652..df3f6b5c6dd 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 @@ -38,6 +38,10 @@ import org.springframework.boot.configurationprocessor.metadata.TestJsonConverte import org.springframework.boot.configurationsample.incremental.BarProperties; import org.springframework.boot.configurationsample.incremental.FooProperties; import org.springframework.boot.configurationsample.incremental.RenamedBarProperties; +import org.springframework.boot.configurationsample.lombok.LombokAccessLevelOverwriteDataProperties; +import org.springframework.boot.configurationsample.lombok.LombokAccessLevelOverwriteDefaultProperties; +import org.springframework.boot.configurationsample.lombok.LombokAccessLevelOverwriteExplicitProperties; +import org.springframework.boot.configurationsample.lombok.LombokAccessLevelProperties; import org.springframework.boot.configurationsample.lombok.LombokExplicitProperties; import org.springframework.boot.configurationsample.lombok.LombokInnerClassProperties; import org.springframework.boot.configurationsample.lombok.LombokInnerClassWithGetterProperties; @@ -83,6 +87,7 @@ import static org.assertj.core.api.Assertions.assertThat; * @author Phillip Webb * @author Andy Wilkinson * @author Kris De Volder + * @author Jonas Keßler */ public class ConfigurationMetadataAnnotationProcessorTests { @@ -483,6 +488,40 @@ public class ConfigurationMetadataAnnotationProcessorTests { assertSimpleLombokProperties(metadata, LombokSimpleProperties.class, "simple"); } + @Test + public void lombokAccessLevelOverwriteDataProperties() { + ConfigurationMetadata metadata = compile( + LombokAccessLevelOverwriteDataProperties.class); + assertAccessLevelOverwriteLombokProperties(metadata, + LombokAccessLevelOverwriteDataProperties.class, + "accesslevel.overwrite.data"); + } + + @Test + public void lombokAccessLevelOverwriteExplicitProperties() { + ConfigurationMetadata metadata = compile( + LombokAccessLevelOverwriteExplicitProperties.class); + assertAccessLevelOverwriteLombokProperties(metadata, + LombokAccessLevelOverwriteExplicitProperties.class, + "accesslevel.overwrite.explicit"); + } + + @Test + public void lombokAccessLevelOverwriteDefaultProperties() { + ConfigurationMetadata metadata = compile( + LombokAccessLevelOverwriteDefaultProperties.class); + assertAccessLevelOverwriteLombokProperties(metadata, + LombokAccessLevelOverwriteDefaultProperties.class, + "accesslevel.overwrite.default"); + } + + @Test + public void lombokAccessLevelProperties() { + ConfigurationMetadata metadata = compile(LombokAccessLevelProperties.class); + assertAccessLevelLombokProperties(metadata, LombokAccessLevelProperties.class, + "accesslevel", 2, 20); + } + @Test public void lombokExplicitProperties() throws Exception { ConfigurationMetadata metadata = compile(LombokExplicitProperties.class); @@ -789,7 +828,26 @@ public class ConfigurationMetadataAnnotationProcessorTests { assertThat(metadata).doesNotHave(Metadata.withProperty(prefix + ".ignored")); } - private ConfigurationMetadata compile(Class... types) throws IOException { + private void assertAccessLevelOverwriteLombokProperties( + ConfigurationMetadata metadata, Class source, String prefix) { + assertAccessLevelLombokProperties(metadata, source, prefix, 7, 15); + } + + private void assertAccessLevelLombokProperties(ConfigurationMetadata metadata, + Class source, String prefix, int countNameFields, int countIgnoredFields) { + assertThat(metadata).has(Metadata.withGroup(prefix).fromSource(source)); + for (int i = 0; i < countNameFields; i++) { + assertThat(metadata) + .has(Metadata.withProperty(prefix + ".name" + i, String.class)); + } + + for (int i = 0; i < countIgnoredFields; i++) { + assertThat(metadata) + .doesNotHave(Metadata.withProperty(prefix + ".ignored" + i)); + } + } + + private ConfigurationMetadata compile(Class... types) { TestConfigurationMetadataAnnotationProcessor processor = new TestConfigurationMetadataAnnotationProcessor( this.compiler.getOutputLocation()); this.compiler.getTask(types).call(processor); diff --git a/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/lombok/LombokAccessLevelOverwriteDataProperties.java b/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/lombok/LombokAccessLevelOverwriteDataProperties.java new file mode 100644 index 00000000000..f8eeeb5b3df --- /dev/null +++ b/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/lombok/LombokAccessLevelOverwriteDataProperties.java @@ -0,0 +1,122 @@ +/* + * 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.lombok; + +import lombok.AccessLevel; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; + +import org.springframework.boot.configurationsample.ConfigurationProperties; + +/** + * Configuration properties using lombok @Data on element level and overwriting behaviour + * with @Getter und @Setter at field level. + * + * @author Jonas Keßler + */ +@Data +@ConfigurationProperties(prefix = "accesslevel.overwrite.data") +public class LombokAccessLevelOverwriteDataProperties { + + private String name0; + + @Getter(AccessLevel.PUBLIC) + @Setter(AccessLevel.PUBLIC) + private String name1; + + @Getter(AccessLevel.PUBLIC) + private String name2; + + @Setter(AccessLevel.PUBLIC) + private String name3; + + @Getter + @Setter + private String name4; + + @Getter + private String name5; + + @Setter + private String name6; + + /* + * AccessLevel.NONE + */ + @Getter(AccessLevel.NONE) + @Setter(AccessLevel.NONE) + private String ignored0; + + @Getter(AccessLevel.NONE) + private String ignored1; + + @Setter(AccessLevel.NONE) + private String ignored2; + + /* + * AccessLevel.PRIVATE + */ + @Getter(AccessLevel.PRIVATE) + @Setter(AccessLevel.PRIVATE) + private String ignored3; + + @Getter(AccessLevel.PRIVATE) + private String ignored4; + + @Setter(AccessLevel.PRIVATE) + private String ignored5; + + /* + * AccessLevel.PACKAGE + */ + @Getter(AccessLevel.PACKAGE) + @Setter(AccessLevel.PACKAGE) + private String ignored6; + + @Getter(AccessLevel.PACKAGE) + private String ignored7; + + @Setter(AccessLevel.PACKAGE) + private String ignored8; + + /* + * AccessLevel.PROTECTED + */ + @Getter(AccessLevel.PROTECTED) + @Setter(AccessLevel.PROTECTED) + private String ignored9; + + @Getter(AccessLevel.PROTECTED) + private String ignored10; + + @Setter(AccessLevel.PROTECTED) + private String ignored11; + + /* + * AccessLevel.MODULE + */ + @Getter(AccessLevel.MODULE) + @Setter(AccessLevel.MODULE) + private String ignored12; + + @Getter(AccessLevel.MODULE) + private String ignored13; + + @Setter(AccessLevel.MODULE) + private String ignored14; +} diff --git a/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/lombok/LombokAccessLevelOverwriteDefaultProperties.java b/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/lombok/LombokAccessLevelOverwriteDefaultProperties.java new file mode 100644 index 00000000000..d98f29bf3af --- /dev/null +++ b/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/lombok/LombokAccessLevelOverwriteDefaultProperties.java @@ -0,0 +1,122 @@ +/* + * 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.lombok; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; + +import org.springframework.boot.configurationsample.ConfigurationProperties; + +/** + * Configuration properties using lombok @Getter and @Setter without explicitly defining + * AccessLevel on element level and overwriting behaviour at field level. + * + * @author Jonas Keßler + */ +@Getter +@Setter +@ConfigurationProperties(prefix = "accesslevel.overwrite.default") +public class LombokAccessLevelOverwriteDefaultProperties { + + private String name0; + + @Getter(AccessLevel.PUBLIC) + @Setter(AccessLevel.PUBLIC) + private String name1; + + @Getter(AccessLevel.PUBLIC) + private String name2; + + @Setter(AccessLevel.PUBLIC) + private String name3; + + @Getter + @Setter + private String name4; + + @Getter + private String name5; + + @Setter + private String name6; + + /* + * AccessLevel.NONE + */ + @Getter(AccessLevel.NONE) + @Setter(AccessLevel.NONE) + private String ignored0; + + @Getter(AccessLevel.NONE) + private String ignored1; + + @Setter(AccessLevel.NONE) + private String ignored2; + + /* + * AccessLevel.PRIVATE + */ + @Getter(AccessLevel.PRIVATE) + @Setter(AccessLevel.PRIVATE) + private String ignored3; + + @Getter(AccessLevel.PRIVATE) + private String ignored4; + + @Setter(AccessLevel.PRIVATE) + private String ignored5; + + /* + * AccessLevel.PACKAGE + */ + @Getter(AccessLevel.PACKAGE) + @Setter(AccessLevel.PACKAGE) + private String ignored6; + + @Getter(AccessLevel.PACKAGE) + private String ignored7; + + @Setter(AccessLevel.PACKAGE) + private String ignored8; + + /* + * AccessLevel.PROTECTED + */ + @Getter(AccessLevel.PROTECTED) + @Setter(AccessLevel.PROTECTED) + private String ignored9; + + @Getter(AccessLevel.PROTECTED) + private String ignored10; + + @Setter(AccessLevel.PROTECTED) + private String ignored11; + + /* + * AccessLevel.MODULE + */ + @Getter(AccessLevel.MODULE) + @Setter(AccessLevel.MODULE) + private String ignored12; + + @Getter(AccessLevel.MODULE) + private String ignored13; + + @Setter(AccessLevel.MODULE) + private String ignored14; +} diff --git a/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/lombok/LombokAccessLevelOverwriteExplicitProperties.java b/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/lombok/LombokAccessLevelOverwriteExplicitProperties.java new file mode 100644 index 00000000000..a0c22ddf3f0 --- /dev/null +++ b/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/lombok/LombokAccessLevelOverwriteExplicitProperties.java @@ -0,0 +1,122 @@ +/* + * 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.lombok; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; + +import org.springframework.boot.configurationsample.ConfigurationProperties; + +/** + * Configuration properties using lombok @Getter and @Setter with explicitly defining + * AccessLevel.PUBLIC on element level and overwriting behaviour at field level. + * + * @author Jonas Keßler + */ +@Getter(AccessLevel.PUBLIC) +@Setter(AccessLevel.PUBLIC) +@ConfigurationProperties(prefix = "accesslevel.overwrite.explicit") +public class LombokAccessLevelOverwriteExplicitProperties { + + private String name0; + + @Getter(AccessLevel.PUBLIC) + @Setter(AccessLevel.PUBLIC) + private String name1; + + @Getter(AccessLevel.PUBLIC) + private String name2; + + @Setter(AccessLevel.PUBLIC) + private String name3; + + @Getter + @Setter + private String name4; + + @Getter + private String name5; + + @Setter + private String name6; + + /* + * AccessLevel.NONE + */ + @Getter(AccessLevel.NONE) + @Setter(AccessLevel.NONE) + private String ignored0; + + @Getter(AccessLevel.NONE) + private String ignored1; + + @Setter(AccessLevel.NONE) + private String ignored2; + + /* + * AccessLevel.PRIVATE + */ + @Getter(AccessLevel.PRIVATE) + @Setter(AccessLevel.PRIVATE) + private String ignored3; + + @Getter(AccessLevel.PRIVATE) + private String ignored4; + + @Setter(AccessLevel.PRIVATE) + private String ignored5; + + /* + * AccessLevel.PACKAGE + */ + @Getter(AccessLevel.PACKAGE) + @Setter(AccessLevel.PACKAGE) + private String ignored6; + + @Getter(AccessLevel.PACKAGE) + private String ignored7; + + @Setter(AccessLevel.PACKAGE) + private String ignored8; + + /* + * AccessLevel.PROTECTED + */ + @Getter(AccessLevel.PROTECTED) + @Setter(AccessLevel.PROTECTED) + private String ignored9; + + @Getter(AccessLevel.PROTECTED) + private String ignored10; + + @Setter(AccessLevel.PROTECTED) + private String ignored11; + + /* + * AccessLevel.MODULE + */ + @Getter(AccessLevel.MODULE) + @Setter(AccessLevel.MODULE) + private String ignored12; + + @Getter(AccessLevel.MODULE) + private String ignored13; + + @Setter(AccessLevel.MODULE) + private String ignored14; +} diff --git a/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/lombok/LombokAccessLevelProperties.java b/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/lombok/LombokAccessLevelProperties.java new file mode 100644 index 00000000000..0fbf3174488 --- /dev/null +++ b/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/lombok/LombokAccessLevelProperties.java @@ -0,0 +1,128 @@ +/* + * 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.lombok; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; + +import org.springframework.boot.configurationsample.ConfigurationProperties; + +/** + * Configuration properties without lombok annotations at element level. + * + * @author Jonas Keßler + */ +@ConfigurationProperties(prefix = "accesslevel") +public class LombokAccessLevelProperties { + + @Getter(AccessLevel.PUBLIC) + @Setter(AccessLevel.PUBLIC) + private String name0; + + @Getter + @Setter + private String name1; + + /* + * AccessLevel.NONE + */ + @Getter(AccessLevel.NONE) + @Setter(AccessLevel.NONE) + private String ignored0; + + @Getter(AccessLevel.NONE) + private String ignored1; + + @Setter(AccessLevel.NONE) + private String ignored2; + + /* + * AccessLevel.PRIVATE + */ + @Getter(AccessLevel.PRIVATE) + @Setter(AccessLevel.PRIVATE) + private String ignored3; + + @Getter(AccessLevel.PRIVATE) + private String ignored4; + + @Setter(AccessLevel.PRIVATE) + private String ignored5; + + /* + * AccessLevel.PACKAGE + */ + @Getter(AccessLevel.PACKAGE) + @Setter(AccessLevel.PACKAGE) + private String ignored6; + + @Getter(AccessLevel.PACKAGE) + private String ignored7; + + @Setter(AccessLevel.PACKAGE) + private String ignored8; + + /* + * AccessLevel.PROTECTED + */ + @Getter(AccessLevel.PROTECTED) + @Setter(AccessLevel.PROTECTED) + private String ignored9; + + @Getter(AccessLevel.PROTECTED) + private String ignored10; + + @Setter(AccessLevel.PROTECTED) + private String ignored11; + + /* + * AccessLevel.MODULE + */ + @Getter(AccessLevel.MODULE) + @Setter(AccessLevel.MODULE) + private String ignored12; + + @Getter(AccessLevel.MODULE) + private String ignored13; + + @Setter(AccessLevel.MODULE) + private String ignored14; + + /* + * Nor getter neither setter defined + */ + private String ignored15; + + /* + * Either PUBLIC getter or setter explicitly defined + */ + @Getter(AccessLevel.PUBLIC) + private String ignored16; + + @Setter(AccessLevel.PUBLIC) + private String ignored17; + + /* + * Either PUBLIC getter or setter implicitly defined + */ + @Getter + private String ignored18; + + @Setter + private String ignored19; +}