From 49a72b007bacd8b11c0bf75a1953f21ad02483c4 Mon Sep 17 00:00:00 2001 From: Madhura Bhave Date: Thu, 1 Feb 2018 13:17:57 -0800 Subject: [PATCH] Bind to map with numeric key without needing [] Closes gh-10751 --- .../source/ConfigurationPropertyName.java | 4 +- ...onPropertiesBindingPostProcessorTests.java | 39 +++++++++++++++++++ .../ConfigurationPropertyNameTests.java | 22 ++++++----- 3 files changed, 53 insertions(+), 12 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertyName.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertyName.java index b0334a1059b..d6a833d8a66 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertyName.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/ConfigurationPropertyName.java @@ -29,7 +29,7 @@ import org.springframework.util.ObjectUtils; /** * A configuration property name composed of elements separated by dots. User created * names may contain the characters "{@code a-z}" "{@code 0-9}") and "{@code -}", they - * must be lower-case and must start with a letter. The "{@code -}" is used purely for + * must be lower-case and must start with an alpha-numeric character. The "{@code -}" is used purely for * formatting, i.e. "{@code foo-bar}" and "{@code foobar}" are considered equivalent. *

* The "{@code [}" and "{@code ]}" characters may be used to indicate an associative @@ -675,7 +675,7 @@ public final class ConfigurationPropertyName } public static boolean isValidChar(char ch, int index) { - return isAlpha(ch) || (index != 0 && (isNumeric(ch) || ch == '-')); + return isAlpha(ch) || isNumeric(ch) || (index != 0 && ch == '-'); } private static boolean isAlpha(char ch) { diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessorTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessorTests.java index a2293d5749c..6f0ba6e31d7 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessorTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessorTests.java @@ -273,6 +273,20 @@ public class ConfigurationPropertiesBindingPostProcessorTests { this.context.refresh(); } + @Test + public void bindToMapWithNumericKey() { + this.context = new AnnotationConfigApplicationContext(); + MutablePropertySources sources = this.context.getEnvironment() + .getPropertySources(); + Map source = new LinkedHashMap<>(); + source.put("sample.foos.1.name", "One"); + sources.addFirst(new MapPropertySource("test-source", source)); + this.context.register(NumericKeyConfiguration.class); + this.context.refresh(); + NumericKeyConfiguration foo = this.context.getBean(NumericKeyConfiguration.class); + assertThat(foo.getFoos().get("1")).isNotNull(); + } + private void prepareConverterContext(Class... config) { this.context = new AnnotationConfigApplicationContext(); MutablePropertySources sources = this.context.getEnvironment() @@ -604,4 +618,29 @@ public class ConfigurationPropertiesBindingPostProcessorTests { } + @Configuration + @EnableConfigurationProperties + @ConfigurationProperties(prefix = "sample") + static class NumericKeyConfiguration { + + private Map foos = new LinkedHashMap<>(); + + public Map getFoos() { + return this.foos; + } + + static class Foo { + + private String name; + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + } + } + } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/source/ConfigurationPropertyNameTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/source/ConfigurationPropertyNameTests.java index aa89e8ab53e..e6ea34d0508 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/source/ConfigurationPropertyNameTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/source/ConfigurationPropertyNameTests.java @@ -49,13 +49,6 @@ public class ConfigurationPropertyNameTests { ConfigurationPropertyName.of(null); } - @Test - public void ofNameShouldNotStartWithNumber() { - this.thrown.expect(InvalidConfigurationPropertyNameException.class); - this.thrown.expectMessage("is not valid"); - ConfigurationPropertyName.of("1foo"); - } - @Test public void ofNameShouldNotStartWithDash() { this.thrown.expect(InvalidConfigurationPropertyNameException.class); @@ -107,6 +100,15 @@ public class ConfigurationPropertyNameTests { assertThat(name.isIndexed(0)).isFalse(); } + @Test + public void ofNameWhenStartsWithNumber() { + ConfigurationPropertyName name = ConfigurationPropertyName.of("1foo"); + assertThat(name.toString()).isEqualTo("1foo"); + assertThat(name.getNumberOfElements()).isEqualTo(1); + assertThat(name.getElement(0, Form.ORIGINAL)).isEqualTo("1foo"); + assertThat(name.isIndexed(0)).isFalse(); + } + @Test public void ofNameWhenRunOnAssociative() { ConfigurationPropertyName name = ConfigurationPropertyName.of("foo[bar]"); @@ -420,8 +422,8 @@ public class ConfigurationPropertyNameTests { @Test public void appendWhenElementNameIsNotValidShouldThrowException() { this.thrown.expect(InvalidConfigurationPropertyNameException.class); - this.thrown.expectMessage("Configuration property name '1bar' is not valid"); - ConfigurationPropertyName.of("foo").append("1bar"); + this.thrown.expectMessage("Configuration property name '-bar' is not valid"); + ConfigurationPropertyName.of("foo").append("-bar"); } @Test @@ -589,7 +591,7 @@ public class ConfigurationPropertyNameTests { @Test public void isValidWhenNotValidShouldReturnFalse() { assertThat(ConfigurationPropertyName.isValid(null)).isFalse(); - assertThat(ConfigurationPropertyName.isValid("1foo")).isFalse(); + assertThat(ConfigurationPropertyName.isValid("-foo")).isFalse(); assertThat(ConfigurationPropertyName.isValid("FooBar")).isFalse(); assertThat(ConfigurationPropertyName.isValid("foo!bar")).isFalse(); }