From d969ebad0794015133d37c5bb32fe9168c7e3c3b Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Mon, 8 May 2017 09:57:54 -0700 Subject: [PATCH] Polish ConfigurationPropertySource support Improve ConfigurationPropertySource support by reworking some of the stream calls based on advice offered by Tagir Valeev from JetBrains. Also improved ConfigurationPropertySource.containsDescendantOf so that it returns an enum rather than an Optional (again based on feedback from Tagir). See gh-9000 --- .../boot/context/properties/bind/Binder.java | 9 +-- .../properties/bind/JavaBeanBinder.java | 6 +- .../AliasedConfigurationPropertySource.java | 19 +++-- .../source/ConfigurationPropertySource.java | 25 ++----- .../source/ConfigurationPropertyState.java | 70 +++++++++++++++++++ ...FilteredConfigurationPropertiesSource.java | 12 ++-- ...IterableConfigurationPropertiesSource.java | 5 +- .../IterableConfigurationPropertySource.java | 5 +- .../SpringConfigurationPropertySource.java | 15 ++-- ...ngIterableConfigurationPropertySource.java | 5 +- ...iasedConfigurationPropertySourceTests.java | 45 +++++++----- .../ConfigurationPropertyStateTests.java | 69 ++++++++++++++++++ ...redConfigurationPropertiesSourceTests.java | 19 +++-- ...bleConfigurationPropertiesSourceTests.java | 4 +- .../MockConfigurationPropertySource.java | 6 +- ...pringConfigurationPropertySourceTests.java | 2 +- ...rableConfigurationPropertySourceTests.java | 6 +- 17 files changed, 233 insertions(+), 89 deletions(-) create mode 100644 spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertyState.java create mode 100644 spring-boot/src/test/java/org/springframework/boot/context/properties/source/ConfigurationPropertyStateTests.java diff --git a/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/Binder.java b/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/Binder.java index 4bd44e660c..d4d61f5c8a 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/Binder.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/Binder.java @@ -36,6 +36,7 @@ import org.springframework.boot.context.properties.source.ConfigurationProperty; import org.springframework.boot.context.properties.source.ConfigurationPropertyName; import org.springframework.boot.context.properties.source.ConfigurationPropertySource; import org.springframework.boot.context.properties.source.ConfigurationPropertySources; +import org.springframework.boot.context.properties.source.ConfigurationPropertyState; import org.springframework.core.convert.ConversionService; import org.springframework.core.env.Environment; import org.springframework.format.support.DefaultFormattingConversionService; @@ -309,8 +310,8 @@ public class Binder { private boolean isUnbindableBean(ConfigurationPropertyName name, Bindable target, Context context) { - if (context.streamSources().map((s) -> s.containsDescendantOf(name).orElse(false)) - .anyMatch(Boolean.TRUE::equals)) { + if (context.streamSources().anyMatch((s) -> s + .containsDescendantOf(name) == ConfigurationPropertyState.PRESENT)) { // We know there are properties to bind so we can't bypass anything return false; } @@ -324,8 +325,8 @@ public class Binder { private boolean containsNoDescendantOf(Stream sources, ConfigurationPropertyName name) { - return sources.map((s) -> s.containsDescendantOf(name).orElse(true)) - .allMatch(Boolean.FALSE::equals); + return sources.allMatch( + (s) -> s.containsDescendantOf(name) == ConfigurationPropertyState.ABSENT); } /** diff --git a/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/JavaBeanBinder.java b/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/JavaBeanBinder.java index cebf897949..0704cacdb4 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/JavaBeanBinder.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/JavaBeanBinder.java @@ -27,6 +27,7 @@ import java.util.function.Supplier; import org.springframework.beans.BeanUtils; import org.springframework.boot.context.properties.source.ConfigurationPropertyName; +import org.springframework.boot.context.properties.source.ConfigurationPropertyState; import org.springframework.core.ResolvableType; /** @@ -40,9 +41,8 @@ class JavaBeanBinder implements BeanBinder { @Override public T bind(ConfigurationPropertyName name, Bindable target, BindContext context, BeanPropertyBinder propertyBinder) { - boolean hasKnownBindableProperties = context.streamSources() - .map((s) -> s.containsDescendantOf(name).orElse(false)) - .anyMatch(Boolean.TRUE::equals); + boolean hasKnownBindableProperties = context.streamSources().anyMatch(( + s) -> s.containsDescendantOf(name) == ConfigurationPropertyState.PRESENT); Bean bean = Bean.get(target, hasKnownBindableProperties); if (bean == null) { return null; diff --git a/spring-boot/src/main/java/org/springframework/boot/context/properties/source/AliasedConfigurationPropertySource.java b/spring-boot/src/main/java/org/springframework/boot/context/properties/source/AliasedConfigurationPropertySource.java index 2154204ee3..135ecbc37a 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/properties/source/AliasedConfigurationPropertySource.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/properties/source/AliasedConfigurationPropertySource.java @@ -16,8 +16,6 @@ package org.springframework.boot.context.properties.source; -import java.util.Optional; - import org.springframework.util.Assert; /** @@ -53,14 +51,21 @@ class AliasedConfigurationPropertySource implements ConfigurationPropertySource } @Override - public Optional containsDescendantOf(ConfigurationPropertyName name) { + public ConfigurationPropertyState containsDescendantOf( + ConfigurationPropertyName name) { Assert.notNull(name, "Name must not be null"); - Optional result = this.source.containsDescendantOf(name); + ConfigurationPropertyState result = this.source.containsDescendantOf(name); + if (result != ConfigurationPropertyState.ABSENT) { + return result; + } for (ConfigurationPropertyName alias : getAliases().getAliases(name)) { - Optional aliasResult = this.source.containsDescendantOf(alias); - result = result.flatMap((r) -> aliasResult.flatMap(a -> Optional.of(r || a))); + ConfigurationPropertyState aliasResult = this.source + .containsDescendantOf(alias); + if (aliasResult != ConfigurationPropertyState.ABSENT) { + return aliasResult; + } } - return result; + return ConfigurationPropertyState.ABSENT; } protected ConfigurationPropertySource getSource() { diff --git a/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertySource.java b/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertySource.java index d6cd35b3c7..a3c33ebda4 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertySource.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertySource.java @@ -16,7 +16,6 @@ package org.springframework.boot.context.properties.source; -import java.util.Optional; import java.util.function.Predicate; import org.springframework.boot.origin.OriginTrackedValue; @@ -42,25 +41,15 @@ public interface ConfigurationPropertySource { ConfigurationProperty getConfigurationProperty(ConfigurationPropertyName name); /** - * Optionally returns if the source contains any descendants of the specified name. - *
    - *
  • A result of {@code true} means that there is at least on property in the source - * with a name that's an - * {@link ConfigurationPropertyName#isAncestorOf(ConfigurationPropertyName) ancestor} - * of {@code name}.
  • - *
  • A result of {@code false} means that that there are no properties in the source - * with a name that's an - * {@link ConfigurationPropertyName#isAncestorOf(ConfigurationPropertyName) ancestor} - * of {@code name}.
  • - *
  • A result of {@code empty} means it is not possible to determine up determine if - * there's a property in the source with a name that's an - * {@link ConfigurationPropertyName#isAncestorOf(ConfigurationPropertyName) ancestor} - * of {@code name}. - *
+ * Returns if the source contains any descendants of the specified name. May return + * {@link ConfigurationPropertyState#PRESENT} or + * {@link ConfigurationPropertyState#ABSENT} if an answer can be determined or + * {@link ConfigurationPropertyState#UNKNOWN} if it's not possible to determine a + * definitive answer. * @param name the name to check - * @return an optional boolean determining if a descendant is contained in the source + * @return if the source contains any descendants */ - Optional containsDescendantOf(ConfigurationPropertyName name); + ConfigurationPropertyState containsDescendantOf(ConfigurationPropertyName name); /** * Return a filtered variant of this source, containing only names that match the diff --git a/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertyState.java b/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertyState.java new file mode 100644 index 0000000000..b84c39cb1c --- /dev/null +++ b/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertyState.java @@ -0,0 +1,70 @@ +/* + * 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.context.properties.source; + +import java.util.function.Predicate; + +import org.springframework.util.Assert; + +/** + * The state of content from a {@link ConfigurationPropertySource}. + * + * @author Phillip Webb + * @since 2.0.0 + */ +public enum ConfigurationPropertyState { + + /** + * The {@link ConfigurationPropertySource} has at least one matching + * {@link ConfigurationProperty}. + */ + PRESENT, + + /** + * The {@link ConfigurationPropertySource} has no matching + * {@link ConfigurationProperty ConfigurationProperties}. + */ + ABSENT, + + /** + * It's not possible to determine if {@link ConfigurationPropertySource} has matching + * {@link ConfigurationProperty ConfigurationProperties} or not. + */ + UNKNOWN; + + /** + * Search the given iterable using a predicate to determine if content is + * {@link #PRESENT} or {@link #ABSENT}. + * @param the data type + * @param source the source iterable to search + * @param predicate the predicate used to test for presence + * @return {@link #PRESENT} if the iterable contains a matching item, otherwise + * {@link #ABSENT}. + */ + static ConfigurationPropertyState search(Iterable source, + Predicate predicate) { + Assert.notNull(source, "Source must not be null"); + Assert.notNull(predicate, "Predicate must not be null"); + for (T item : source) { + if (predicate.test(item)) { + return PRESENT; + } + } + return ABSENT; + } + +} diff --git a/spring-boot/src/main/java/org/springframework/boot/context/properties/source/FilteredConfigurationPropertiesSource.java b/spring-boot/src/main/java/org/springframework/boot/context/properties/source/FilteredConfigurationPropertiesSource.java index 1c201eb850..eb274be338 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/properties/source/FilteredConfigurationPropertiesSource.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/properties/source/FilteredConfigurationPropertiesSource.java @@ -16,7 +16,6 @@ package org.springframework.boot.context.properties.source; -import java.util.Optional; import java.util.function.Predicate; import org.springframework.util.Assert; @@ -49,10 +48,13 @@ class FilteredConfigurationPropertiesSource implements ConfigurationPropertySour } @Override - public Optional containsDescendantOf(ConfigurationPropertyName name) { - // We can't be sure a contained descendant won't be filtered - return this.source.containsDescendantOf(name) - .flatMap((result) -> result ? Optional.empty() : Optional.of(result)); + public ConfigurationPropertyState containsDescendantOf(ConfigurationPropertyName name) { + ConfigurationPropertyState result = this.source.containsDescendantOf(name); + if (result == ConfigurationPropertyState.PRESENT) { + // We can't be sure a contained descendant won't be filtered + return ConfigurationPropertyState.UNKNOWN; + } + return result; } protected ConfigurationPropertySource getSource() { diff --git a/spring-boot/src/main/java/org/springframework/boot/context/properties/source/FilteredIterableConfigurationPropertiesSource.java b/spring-boot/src/main/java/org/springframework/boot/context/properties/source/FilteredIterableConfigurationPropertiesSource.java index f7d5f0b37c..d2e645930f 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/properties/source/FilteredIterableConfigurationPropertiesSource.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/properties/source/FilteredIterableConfigurationPropertiesSource.java @@ -16,7 +16,6 @@ package org.springframework.boot.context.properties.source; -import java.util.Optional; import java.util.function.Predicate; import java.util.stream.Stream; import java.util.stream.StreamSupport; @@ -48,8 +47,8 @@ class FilteredIterableConfigurationPropertiesSource } @Override - public Optional containsDescendantOf(ConfigurationPropertyName name) { - return Optional.of(stream().anyMatch(name::isAncestorOf)); + public ConfigurationPropertyState containsDescendantOf(ConfigurationPropertyName name) { + return ConfigurationPropertyState.search(this, name::isAncestorOf); } } diff --git a/spring-boot/src/main/java/org/springframework/boot/context/properties/source/IterableConfigurationPropertySource.java b/spring-boot/src/main/java/org/springframework/boot/context/properties/source/IterableConfigurationPropertySource.java index c82511376c..abae5e3ab3 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/properties/source/IterableConfigurationPropertySource.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/properties/source/IterableConfigurationPropertySource.java @@ -17,7 +17,6 @@ package org.springframework.boot.context.properties.source; import java.util.Iterator; -import java.util.Optional; import java.util.function.Predicate; import java.util.stream.Stream; @@ -62,8 +61,8 @@ public interface IterableConfigurationPropertySource Stream stream(); @Override - default Optional containsDescendantOf(ConfigurationPropertyName name) { - return Optional.of(stream().anyMatch(name::isAncestorOf)); + default ConfigurationPropertyState containsDescendantOf(ConfigurationPropertyName name) { + return ConfigurationPropertyState.search(this, name::isAncestorOf); } @Override diff --git a/spring-boot/src/main/java/org/springframework/boot/context/properties/source/SpringConfigurationPropertySource.java b/spring-boot/src/main/java/org/springframework/boot/context/properties/source/SpringConfigurationPropertySource.java index b3d37d7ea9..3f1ccb6ffc 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/properties/source/SpringConfigurationPropertySource.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/properties/source/SpringConfigurationPropertySource.java @@ -20,7 +20,6 @@ import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Optional; import java.util.function.Function; import org.springframework.boot.env.RandomValuePropertySource; @@ -62,7 +61,7 @@ class SpringConfigurationPropertySource implements ConfigurationPropertySource { private final PropertyMapper mapper; - private final Function> containsDescendantOfMethod; + private final Function containsDescendantOfMethod; /** * Create a new {@link SpringConfigurationPropertySource} implementation. @@ -73,13 +72,13 @@ class SpringConfigurationPropertySource implements ConfigurationPropertySource { */ SpringConfigurationPropertySource(PropertySource propertySource, PropertyMapper mapper, - Function> containsDescendantOfMethod) { + Function containsDescendantOfMethod) { Assert.notNull(propertySource, "PropertySource must not be null"); Assert.notNull(mapper, "Mapper must not be null"); this.propertySource = propertySource; this.mapper = new ExceptionSwallowingPropertyMapper(mapper); this.containsDescendantOfMethod = (containsDescendantOfMethod != null - ? containsDescendantOfMethod : (n) -> Optional.empty()); + ? containsDescendantOfMethod : (n) -> ConfigurationPropertyState.UNKNOWN); } @Override @@ -90,7 +89,7 @@ class SpringConfigurationPropertySource implements ConfigurationPropertySource { } @Override - public Optional containsDescendantOf(ConfigurationPropertyName name) { + public ConfigurationPropertyState containsDescendantOf(ConfigurationPropertyName name) { return this.containsDescendantOfMethod.apply(name); } @@ -173,11 +172,11 @@ class SpringConfigurationPropertySource implements ConfigurationPropertySource { return source; } - private static Function> getContainsDescendantOfMethod( + private static Function getContainsDescendantOfMethod( PropertySource source) { if (source instanceof RandomValuePropertySource) { - return (name) -> Optional - .of(name.isAncestorOf(RANDOM) || name.equals(RANDOM)); + return (name) -> (name.isAncestorOf(RANDOM) || name.equals(RANDOM) + ? ConfigurationPropertyState.PRESENT : ConfigurationPropertyState.ABSENT); } return null; } diff --git a/spring-boot/src/main/java/org/springframework/boot/context/properties/source/SpringIterableConfigurationPropertySource.java b/spring-boot/src/main/java/org/springframework/boot/context/properties/source/SpringIterableConfigurationPropertySource.java index 0a5c2303e1..abebcd303d 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/properties/source/SpringIterableConfigurationPropertySource.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/properties/source/SpringIterableConfigurationPropertySource.java @@ -20,7 +20,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; -import java.util.Optional; import java.util.stream.Stream; import org.springframework.core.env.EnumerablePropertySource; @@ -88,8 +87,8 @@ class SpringIterableConfigurationPropertySource extends SpringConfigurationPrope } @Override - public Optional containsDescendantOf(ConfigurationPropertyName name) { - return Optional.of(stream().anyMatch(name::isAncestorOf)); + public ConfigurationPropertyState containsDescendantOf(ConfigurationPropertyName name) { + return ConfigurationPropertyState.search(this, name::isAncestorOf); } private List getConfigurationPropertyNames() { diff --git a/spring-boot/src/test/java/org/springframework/boot/context/properties/source/AliasedConfigurationPropertySourceTests.java b/spring-boot/src/test/java/org/springframework/boot/context/properties/source/AliasedConfigurationPropertySourceTests.java index a437c66f83..5f2782a6d6 100644 --- a/spring-boot/src/test/java/org/springframework/boot/context/properties/source/AliasedConfigurationPropertySourceTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/context/properties/source/AliasedConfigurationPropertySourceTests.java @@ -16,8 +16,6 @@ package org.springframework.boot.context.properties.source; -import java.util.Optional; - import org.junit.Test; import org.mockito.Answers; @@ -57,56 +55,65 @@ public class AliasedConfigurationPropertySourceTests { } @Test - public void containsDescendantOfWhenSourceReturnsEmptyShouldReturnEmpty() + public void containsDescendantOfWhenSourceReturnsUnknownShouldReturnUnknown() throws Exception { ConfigurationPropertyName name = ConfigurationPropertyName.of("foo"); ConfigurationPropertySource source = mock(ConfigurationPropertySource.class, withSettings().defaultAnswer(Answers.CALLS_REAL_METHODS)); - given(source.containsDescendantOf(name)).willReturn(Optional.empty()); + given(source.containsDescendantOf(name)) + .willReturn(ConfigurationPropertyState.UNKNOWN); ConfigurationPropertySource aliased = source .withAliases(new ConfigurationPropertyNameAliases("foo.bar", "foo.bar1")); - assertThat(aliased.containsDescendantOf(name)).isEmpty(); + assertThat(aliased.containsDescendantOf(name)) + .isEqualTo(ConfigurationPropertyState.UNKNOWN); } @Test - public void containsDescendantOfWhenAliasReturnsEmptyShouldReturnEmpty() + public void containsDescendantOfWhenSourceReturnsPresentShouldReturnPresent() throws Exception { ConfigurationPropertyName name = ConfigurationPropertyName.of("foo"); ConfigurationPropertySource source = mock(ConfigurationPropertySource.class, withSettings().defaultAnswer(Answers.CALLS_REAL_METHODS)); - given(source.containsDescendantOf(name)).willReturn(Optional.of(true)); + given(source.containsDescendantOf(name)) + .willReturn(ConfigurationPropertyState.PRESENT); given(source.containsDescendantOf(ConfigurationPropertyName.of("bar"))) - .willReturn(Optional.empty()); + .willReturn(ConfigurationPropertyState.UNKNOWN); ConfigurationPropertySource aliased = source - .withAliases(new ConfigurationPropertyNameAliases("foo", "bar")); - assertThat(aliased.containsDescendantOf(name)).isEmpty(); + .withAliases(new ConfigurationPropertyNameAliases("foo.bar", "foo.bar1")); + assertThat(aliased.containsDescendantOf(name)) + .isEqualTo(ConfigurationPropertyState.PRESENT); } @Test - public void containsDescendantOfWhenAllAreFalseShouldReturnFalse() throws Exception { + public void containsDescendantOfWhenAllAreAbsentShouldReturnAbsent() + throws Exception { ConfigurationPropertyName name = ConfigurationPropertyName.of("foo"); ConfigurationPropertySource source = mock(ConfigurationPropertySource.class, withSettings().defaultAnswer(Answers.CALLS_REAL_METHODS)); - given(source.containsDescendantOf(name)).willReturn(Optional.of(false)); + given(source.containsDescendantOf(name)) + .willReturn(ConfigurationPropertyState.ABSENT); given(source.containsDescendantOf(ConfigurationPropertyName.of("bar"))) - .willReturn(Optional.of(false)); + .willReturn(ConfigurationPropertyState.ABSENT); ConfigurationPropertySource aliased = source .withAliases(new ConfigurationPropertyNameAliases("foo", "bar")); - assertThat(aliased.containsDescendantOf(name)).contains(false); - + assertThat(aliased.containsDescendantOf(name)) + .isEqualTo(ConfigurationPropertyState.ABSENT); } @Test - public void containsDescendantOfWhenAnyIsTrueShouldReturnTrue() throws Exception { + public void containsDescendantOfWhenAnyIsPresentShouldReturnPresent() + throws Exception { ConfigurationPropertyName name = ConfigurationPropertyName.of("foo"); ConfigurationPropertySource source = mock(ConfigurationPropertySource.class, withSettings().defaultAnswer(Answers.CALLS_REAL_METHODS)); - given(source.containsDescendantOf(name)).willReturn(Optional.of(false)); + given(source.containsDescendantOf(name)) + .willReturn(ConfigurationPropertyState.ABSENT); given(source.containsDescendantOf(ConfigurationPropertyName.of("bar"))) - .willReturn(Optional.of(true)); + .willReturn(ConfigurationPropertyState.PRESENT); ConfigurationPropertySource aliased = source .withAliases(new ConfigurationPropertyNameAliases("foo", "bar")); - assertThat(aliased.containsDescendantOf(name)).contains(true); + assertThat(aliased.containsDescendantOf(name)) + .isEqualTo(ConfigurationPropertyState.PRESENT); } private Object getValue(ConfigurationPropertySource source, String name) { diff --git a/spring-boot/src/test/java/org/springframework/boot/context/properties/source/ConfigurationPropertyStateTests.java b/spring-boot/src/test/java/org/springframework/boot/context/properties/source/ConfigurationPropertyStateTests.java new file mode 100644 index 0000000000..20eb5f9305 --- /dev/null +++ b/spring-boot/src/test/java/org/springframework/boot/context/properties/source/ConfigurationPropertyStateTests.java @@ -0,0 +1,69 @@ +/* + * 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.context.properties.source; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link ConfigurationPropertyState}. + * + * @author Phillip Webb + */ +public class ConfigurationPropertyStateTests { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void searchWhenIterableIsNullShouldThrowException() throws Exception { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("Source must not be null"); + ConfigurationPropertyState.search(null, (e) -> true); + } + + @Test + public void searchWhenPredicateIsNullShouldThrowException() throws Exception { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("Predicate must not be null"); + ConfigurationPropertyState.search(Collections.emptyList(), null); + } + + @Test + public void searchWhenContainsItemShouldReturnPresent() { + List source = Arrays.asList("a", "b", "c"); + ConfigurationPropertyState result = ConfigurationPropertyState.search(source, + "b"::equals); + assertThat(result).isEqualTo(ConfigurationPropertyState.PRESENT); + } + + @Test + public void searchWhenContainsNoItemShouldReturnAbsent() { + List source = Arrays.asList("a", "x", "c"); + ConfigurationPropertyState result = ConfigurationPropertyState.search(source, + "b"::equals); + assertThat(result).isEqualTo(ConfigurationPropertyState.ABSENT); + } + +} diff --git a/spring-boot/src/test/java/org/springframework/boot/context/properties/source/FilteredConfigurationPropertiesSourceTests.java b/spring-boot/src/test/java/org/springframework/boot/context/properties/source/FilteredConfigurationPropertiesSourceTests.java index 621b100677..7d3b4e2eab 100644 --- a/spring-boot/src/test/java/org/springframework/boot/context/properties/source/FilteredConfigurationPropertiesSourceTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/context/properties/source/FilteredConfigurationPropertiesSourceTests.java @@ -17,7 +17,6 @@ package org.springframework.boot.context.properties.source; import java.util.Objects; -import java.util.Optional; import org.junit.Rule; import org.junit.Test; @@ -74,9 +73,11 @@ public class FilteredConfigurationPropertiesSourceTests { ConfigurationPropertyName name = ConfigurationPropertyName.of("foo"); ConfigurationPropertySource source = mock(ConfigurationPropertySource.class, withSettings().defaultAnswer(Answers.CALLS_REAL_METHODS)); - given(source.containsDescendantOf(name)).willReturn(Optional.empty()); + given(source.containsDescendantOf(name)) + .willReturn(ConfigurationPropertyState.UNKNOWN); ConfigurationPropertySource filtered = source.filter((n) -> true); - assertThat(filtered.containsDescendantOf(name)).isEmpty(); + assertThat(filtered.containsDescendantOf(name)) + .isEqualTo(ConfigurationPropertyState.UNKNOWN); } @Test @@ -85,9 +86,11 @@ public class FilteredConfigurationPropertiesSourceTests { ConfigurationPropertyName name = ConfigurationPropertyName.of("foo"); ConfigurationPropertySource source = mock(ConfigurationPropertySource.class, withSettings().defaultAnswer(Answers.CALLS_REAL_METHODS)); - given(source.containsDescendantOf(name)).willReturn(Optional.of(false)); + given(source.containsDescendantOf(name)) + .willReturn(ConfigurationPropertyState.ABSENT); ConfigurationPropertySource filtered = source.filter((n) -> true); - assertThat(filtered.containsDescendantOf(name)).contains(false); + assertThat(filtered.containsDescendantOf(name)) + .isEqualTo(ConfigurationPropertyState.ABSENT); } @Test @@ -96,9 +99,11 @@ public class FilteredConfigurationPropertiesSourceTests { ConfigurationPropertyName name = ConfigurationPropertyName.of("foo"); ConfigurationPropertySource source = mock(ConfigurationPropertySource.class, withSettings().defaultAnswer(Answers.CALLS_REAL_METHODS)); - given(source.containsDescendantOf(name)).willReturn(Optional.of(true)); + given(source.containsDescendantOf(name)) + .willReturn(ConfigurationPropertyState.PRESENT); ConfigurationPropertySource filtered = source.filter((n) -> true); - assertThat(filtered.containsDescendantOf(name)).isEmpty(); + assertThat(filtered.containsDescendantOf(name)) + .isEqualTo(ConfigurationPropertyState.UNKNOWN); } protected final ConfigurationPropertySource createTestSource() { diff --git a/spring-boot/src/test/java/org/springframework/boot/context/properties/source/FilteredIterableConfigurationPropertiesSourceTests.java b/spring-boot/src/test/java/org/springframework/boot/context/properties/source/FilteredIterableConfigurationPropertiesSourceTests.java index b6d4a7030a..f9ad7e7f81 100644 --- a/spring-boot/src/test/java/org/springframework/boot/context/properties/source/FilteredIterableConfigurationPropertiesSourceTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/context/properties/source/FilteredIterableConfigurationPropertiesSourceTests.java @@ -51,9 +51,9 @@ public class FilteredIterableConfigurationPropertiesSourceTests source.put("faf.bar[0]", "1"); IterableConfigurationPropertySource filtered = source.filter(this::noBrackets); assertThat(filtered.containsDescendantOf(ConfigurationPropertyName.of("foo"))) - .contains(true); + .isEqualTo(ConfigurationPropertyState.PRESENT); assertThat(filtered.containsDescendantOf(ConfigurationPropertyName.of("faf"))) - .contains(false); + .isEqualTo(ConfigurationPropertyState.ABSENT); } private boolean noBrackets(ConfigurationPropertyName name) { diff --git a/spring-boot/src/test/java/org/springframework/boot/context/properties/source/MockConfigurationPropertySource.java b/spring-boot/src/test/java/org/springframework/boot/context/properties/source/MockConfigurationPropertySource.java index e01681a47c..b99596bd66 100644 --- a/spring-boot/src/test/java/org/springframework/boot/context/properties/source/MockConfigurationPropertySource.java +++ b/spring-boot/src/test/java/org/springframework/boot/context/properties/source/MockConfigurationPropertySource.java @@ -19,7 +19,6 @@ package org.springframework.boot.context.properties.source; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; -import java.util.Optional; import java.util.stream.Stream; import org.springframework.boot.origin.MockOrigin; @@ -99,8 +98,9 @@ public class MockConfigurationPropertySource } @Override - public Optional containsDescendantOf(ConfigurationPropertyName name) { - return Optional.empty(); + public ConfigurationPropertyState containsDescendantOf( + ConfigurationPropertyName name) { + return ConfigurationPropertyState.UNKNOWN; } } diff --git a/spring-boot/src/test/java/org/springframework/boot/context/properties/source/SpringConfigurationPropertySourceTests.java b/spring-boot/src/test/java/org/springframework/boot/context/properties/source/SpringConfigurationPropertySourceTests.java index e0f02f2973..3911288b57 100644 --- a/spring-boot/src/test/java/org/springframework/boot/context/properties/source/SpringConfigurationPropertySourceTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/context/properties/source/SpringConfigurationPropertySourceTests.java @@ -122,7 +122,7 @@ public class SpringConfigurationPropertySourceTests { SpringConfigurationPropertySource adapter = new SpringConfigurationPropertySource( propertySource, DefaultPropertyMapper.INSTANCE, null); assertThat(adapter.containsDescendantOf(ConfigurationPropertyName.of("foo"))) - .isEmpty(); + .isEqualTo(ConfigurationPropertyState.UNKNOWN); } @Test diff --git a/spring-boot/src/test/java/org/springframework/boot/context/properties/source/SpringIterableConfigurationPropertySourceTests.java b/spring-boot/src/test/java/org/springframework/boot/context/properties/source/SpringIterableConfigurationPropertySourceTests.java index 238c200a9c..b8a57dd8f1 100644 --- a/spring-boot/src/test/java/org/springframework/boot/context/properties/source/SpringIterableConfigurationPropertySourceTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/context/properties/source/SpringIterableConfigurationPropertySourceTests.java @@ -165,11 +165,11 @@ public class SpringIterableConfigurationPropertySourceTests { SpringIterableConfigurationPropertySource adapter = new SpringIterableConfigurationPropertySource( propertySource, DefaultPropertyMapper.INSTANCE); assertThat(adapter.containsDescendantOf(ConfigurationPropertyName.of("foo"))) - .contains(true); + .isEqualTo(ConfigurationPropertyState.PRESENT); assertThat(adapter.containsDescendantOf(ConfigurationPropertyName.of("faf"))) - .contains(false); + .isEqualTo(ConfigurationPropertyState.ABSENT); assertThat(adapter.containsDescendantOf(ConfigurationPropertyName.of("fof"))) - .contains(false); + .isEqualTo(ConfigurationPropertyState.ABSENT); } /**