diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java index 528eada8ca8..46cc606db6d 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java @@ -63,6 +63,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({ "*" }) @@ -94,6 +95,8 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor static final String LOMBOK_SETTER_ANNOTATION = "lombok.Setter"; + static final String LOMBOK_ACCESS_LEVEL_PUBLIC = "PUBLIC"; + private static final Set SUPPORTED_OPTIONS = Collections.unmodifiableSet( new HashSet<>(Arrays.asList(ADDITIONAL_METADATA_LOCATIONS_OPTION))); @@ -366,16 +369,44 @@ 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 hasLombokPublicAccessor(field, element, true); } 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)); + && hasLombokPublicAccessor(field, element, false); + } + + /** + * Determine if the specified {@link VariableElement field} defines a public accessor + * using lombok annotations. + * @param field the field to inspect + * @param element the parent element of the field (i.e. its holding class) + * @param getter {@code true} to look for the read accessor, {@code false} for the + * write accessor + * @return {@code true} if this field is a public accessor of the specified type + */ + private boolean hasLombokPublicAccessor(VariableElement field, TypeElement element, + boolean getter) { + String annotation = (getter ? LOMBOK_GETTER_ANNOTATION + : LOMBOK_SETTER_ANNOTATION); + AnnotationMirror lombokMethodAnnotationOnField = getAnnotation(field, annotation); + if (lombokMethodAnnotationOnField != null) { + return isAccessLevelPublic(lombokMethodAnnotationOnField); + } + AnnotationMirror lombokMethodAnnotationOnElement = getAnnotation(element, + annotation); + if (lombokMethodAnnotationOnElement != null) { + return isAccessLevelPublic(lombokMethodAnnotationOnElement); + } + return hasAnnotation(element, LOMBOK_DATA_ANNOTATION); + } + + + private boolean isAccessLevelPublic(AnnotationMirror lombokAnnotation) { + Map values = getAnnotationElementValues(lombokAnnotation); + Object value = values.get("value"); + return (value == null || value.toString().equals(LOMBOK_ACCESS_LEVEL_PUBLIC)); } private void processNestedType(String prefix, TypeElement element, diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessorTests.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessorTests.java index 10113e0daad..298de012af4 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessorTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessorTests.java @@ -47,6 +47,10 @@ import org.springframework.boot.configurationsample.endpoint.incremental.Increme 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; @@ -96,6 +100,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 { @@ -542,6 +547,41 @@ public class ConfigurationMetadataAnnotationProcessorTests { ConfigurationMetadata metadata = compile(LombokExplicitProperties.class); assertSimpleLombokProperties(metadata, LombokExplicitProperties.class, "explicit"); + assertThat(metadata.getItems()).hasSize(6); + } + + @Test + public void lombokAccessLevelProperties() { + ConfigurationMetadata metadata = compile(LombokAccessLevelProperties.class); + assertAccessLevelLombokProperties(metadata, LombokAccessLevelProperties.class, + "accesslevel", 2); + } + + @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 @@ -1000,6 +1040,21 @@ public class ConfigurationMetadataAnnotationProcessorTests { assertThat(metadata).doesNotHave(Metadata.withProperty(prefix + ".ignored")); } + private void assertAccessLevelOverwriteLombokProperties( + ConfigurationMetadata metadata, Class source, String prefix) { + assertAccessLevelLombokProperties(metadata, source, prefix, 7); + } + + private void assertAccessLevelLombokProperties(ConfigurationMetadata metadata, + Class source, String prefix, int countNameFields) { + 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)); + } + assertThat(metadata.getItems()).hasSize(1 + countNameFields); + } + private ConfigurationMetadata compile(Class... types) { TestConfigurationMetadataAnnotationProcessor processor = new TestConfigurationMetadataAnnotationProcessor( this.compiler.getOutputLocation()); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/lombok/LombokAccessLevelOverwriteDataProperties.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/lombok/LombokAccessLevelOverwriteDataProperties.java new file mode 100644 index 00000000000..b458be59f38 --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/lombok/LombokAccessLevelOverwriteDataProperties.java @@ -0,0 +1,123 @@ +/* + * 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 ignoredAccessLevelNone; + + @Getter(AccessLevel.NONE) + private String ignoredGetterAccessLevelNone; + + @Setter(AccessLevel.NONE) + private String ignoredSetterAccessLevelNone; + + /* + * AccessLevel.PRIVATE + */ + @Getter(AccessLevel.PRIVATE) + @Setter(AccessLevel.PRIVATE) + private String ignoredAccessLevelPrivate; + + @Getter(AccessLevel.PRIVATE) + private String ignoredGetterAccessLevelPrivate; + + @Setter(AccessLevel.PRIVATE) + private String ignoredSetterAccessLevelPrivate; + + /* + * AccessLevel.PACKAGE + */ + @Getter(AccessLevel.PACKAGE) + @Setter(AccessLevel.PACKAGE) + private String ignoredAccessLevelPackage; + + @Getter(AccessLevel.PACKAGE) + private String ignoredGetterAccessLevelPackage; + + @Setter(AccessLevel.PACKAGE) + private String ignoredSetterAccessLevelPackage; + + /* + * AccessLevel.PROTECTED + */ + @Getter(AccessLevel.PROTECTED) + @Setter(AccessLevel.PROTECTED) + private String ignoredAccessLevelProtected; + + @Getter(AccessLevel.PROTECTED) + private String ignoredGetterAccessLevelProtected; + + @Setter(AccessLevel.PROTECTED) + private String ignoredSetterAccessLevelProtected; + + /* + * AccessLevel.MODULE + */ + @Getter(AccessLevel.MODULE) + @Setter(AccessLevel.MODULE) + private String ignoredAccessLevelModule; + + @Getter(AccessLevel.MODULE) + private String ignoredGetterAccessLevelModule; + + @Setter(AccessLevel.MODULE) + private String ignoredSetterAccessLevelModule; + +} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/lombok/LombokAccessLevelOverwriteDefaultProperties.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/lombok/LombokAccessLevelOverwriteDefaultProperties.java new file mode 100644 index 00000000000..cd67459cf53 --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/lombok/LombokAccessLevelOverwriteDefaultProperties.java @@ -0,0 +1,93 @@ +/* + * 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.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 ignoredAccessLevelNone; + + /* + * AccessLevel.PRIVATE + */ + @Getter(AccessLevel.PRIVATE) + @Setter(AccessLevel.PRIVATE) + private String ignoredAccessLevelPrivate; + + /* + * AccessLevel.PACKAGE + */ + @Getter(AccessLevel.PACKAGE) + @Setter(AccessLevel.PACKAGE) + private String ignoredAccessLevelPackage; + + /* + * AccessLevel.PROTECTED + */ + @Getter(AccessLevel.PROTECTED) + @Setter(AccessLevel.PROTECTED) + private String ignoredAccessLevelProtected; + + /* + * AccessLevel.MODULE + */ + @Getter(AccessLevel.MODULE) + @Setter(AccessLevel.MODULE) + private String ignoredAccessLevelModule; + +} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/lombok/LombokAccessLevelOverwriteExplicitProperties.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/lombok/LombokAccessLevelOverwriteExplicitProperties.java new file mode 100644 index 00000000000..4a19f290f1b --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/lombok/LombokAccessLevelOverwriteExplicitProperties.java @@ -0,0 +1,93 @@ +/* + * 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.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 ignoredAccessLevelNone; + + /* + * AccessLevel.PRIVATE + */ + @Getter(AccessLevel.PRIVATE) + @Setter(AccessLevel.PRIVATE) + private String ignoredAccessLevelPrivate; + + /* + * AccessLevel.PACKAGE + */ + @Getter(AccessLevel.PACKAGE) + @Setter(AccessLevel.PACKAGE) + private String ignoredAccessLevelPackage; + + /* + * AccessLevel.PROTECTED + */ + @Getter(AccessLevel.PROTECTED) + @Setter(AccessLevel.PROTECTED) + private String ignoredAccessLevelProtected; + + /* + * AccessLevel.MODULE + */ + @Getter(AccessLevel.MODULE) + @Setter(AccessLevel.MODULE) + private String ignoredAccessLevelModule; + +} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/lombok/LombokAccessLevelProperties.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/lombok/LombokAccessLevelProperties.java new file mode 100644 index 00000000000..6063ce92edf --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/lombok/LombokAccessLevelProperties.java @@ -0,0 +1,85 @@ +/* + * 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.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 ignoredAccessLevelNone; + + /* + * AccessLevel.PRIVATE + */ + @Getter(AccessLevel.PRIVATE) + @Setter(AccessLevel.PRIVATE) + private String ignoredAccessLevelPrivate; + + /* + * AccessLevel.PACKAGE + */ + @Getter(AccessLevel.PACKAGE) + @Setter(AccessLevel.PACKAGE) + private String ignoredAccessLevelPackage; + + /* + * AccessLevel.PROTECTED + */ + @Getter(AccessLevel.PROTECTED) + @Setter(AccessLevel.PROTECTED) + private String ignoredAccessLevelProtected; + + /* + * AccessLevel.MODULE + */ + @Getter(AccessLevel.MODULE) + @Setter(AccessLevel.MODULE) + private String ignoredAccessLevelModule; + + /* + * Either PUBLIC getter or setter explicitly defined + */ + @Getter(AccessLevel.PUBLIC) + private String ignoredOnlyPublicGetter; + + @Setter(AccessLevel.PUBLIC) + private String ignoredOnlyPublicSetter; + +} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/lombok/LombokExplicitProperties.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/lombok/LombokExplicitProperties.java index d04a597f11b..12dd427f774 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/lombok/LombokExplicitProperties.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/lombok/LombokExplicitProperties.java @@ -62,4 +62,10 @@ public class LombokExplicitProperties { @SuppressWarnings("unused") private String ignored; + @Getter + private String ignoredOnlyGetter; + + @Setter + private String ignoredOnlySetter; + }