From 8476a79dd263c048c8ec16ebe49cc3be0907dbe2 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Fri, 15 Aug 2014 10:46:06 -0700 Subject: [PATCH] Add support for RelaxedDataBinder aliases Update RelaxedDataBinder to support property aliases and change DataSourceBuilder to use them. Fixes gh-1384 --- .../autoconfigure/jdbc/DataSourceBuilder.java | 12 +---- .../boot/bind/RelaxedDataBinder.java | 54 ++++++++++++++++--- .../boot/bind/RelaxedDataBinderTests.java | 23 ++++++++ 3 files changed, 72 insertions(+), 17 deletions(-) diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceBuilder.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceBuilder.java index 3a8aba07a7c..3459539d4c0 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceBuilder.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceBuilder.java @@ -23,7 +23,6 @@ import javax.sql.DataSource; import org.springframework.beans.BeanUtils; import org.springframework.beans.MutablePropertyValues; -import org.springframework.beans.PropertyValues; import org.springframework.boot.bind.RelaxedDataBinder; import org.springframework.util.ClassUtils; @@ -84,15 +83,8 @@ public class DataSourceBuilder { } private void bind(DataSource result) { - new RelaxedDataBinder(result).bind(getPropertyValues()); - } - - private PropertyValues getPropertyValues() { - if (getType().getName().contains("Hikari") && this.properties.containsKey("url")) { - this.properties.put("jdbcUrl", this.properties.get("url")); - this.properties.remove("url"); - } - return new MutablePropertyValues(this.properties); + MutablePropertyValues properties = new MutablePropertyValues(this.properties); + new RelaxedDataBinder(result).withAlias("url", "jdbcUrl").bind(properties); } public DataSourceBuilder type(Class type) { diff --git a/spring-boot/src/main/java/org/springframework/boot/bind/RelaxedDataBinder.java b/spring-boot/src/main/java/org/springframework/boot/bind/RelaxedDataBinder.java index 7c2bba853db..9194bf23e86 100644 --- a/spring-boot/src/main/java/org/springframework/boot/bind/RelaxedDataBinder.java +++ b/spring-boot/src/main/java/org/springframework/boot/bind/RelaxedDataBinder.java @@ -19,6 +19,7 @@ package org.springframework.boot.bind; import java.net.InetAddress; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -29,6 +30,8 @@ import org.springframework.beans.InvalidPropertyException; import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.PropertyValue; import org.springframework.core.convert.TypeDescriptor; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; import org.springframework.validation.DataBinder; @@ -47,6 +50,8 @@ public class RelaxedDataBinder extends DataBinder { private boolean ignoreNestedProperties; + private MultiValueMap nameAliases = new LinkedMultiValueMap(); + /** * Create a new {@link RelaxedDataBinder} instance. * @param target the target into which properties are bound @@ -76,6 +81,27 @@ public class RelaxedDataBinder extends DataBinder { this.ignoreNestedProperties = ignoreNestedProperties; } + /** + * Set name aliases. + * @param aliases a map of property name to aliases + */ + public void setNameAliases(Map> aliases) { + this.nameAliases = new LinkedMultiValueMap(aliases); + } + + /** + * Add aliases to the {@link DataBinder}. + * @param name the property name to alias + * @param alias aliases for the property names + * @return this instance + */ + public RelaxedDataBinder withAlias(String name, String... alias) { + for (String value : alias) { + this.nameAliases.add(name, value); + } + return this; + } + @Override public void initBeanPropertyAccess() { super.initBeanPropertyAccess(); @@ -262,19 +288,33 @@ public class RelaxedDataBinder extends DataBinder { private String getActualPropertyName(BeanWrapper target, String prefix, String name) { prefix = StringUtils.hasText(prefix) ? prefix + "." : ""; - for (String candidate : new RelaxedNames(name)) { - try { - if (target.getPropertyType(prefix + candidate) != null) { - return candidate; + Iterable names = getNameAndAliases(name); + for (String nameOrAlias : names) { + for (String candidate : new RelaxedNames(nameOrAlias)) { + try { + if (target.getPropertyType(prefix + candidate) != null) { + return candidate; + } + } + catch (InvalidPropertyException ex) { + // swallow and continue } - } - catch (InvalidPropertyException ex) { - // swallow and continue } } return name; } + private Iterable getNameAndAliases(String name) { + List aliases = this.nameAliases.get(name); + if (aliases == null) { + return Collections.singleton(name); + } + List nameAndAliases = new ArrayList(aliases.size() + 1); + nameAndAliases.add(name); + nameAndAliases.addAll(aliases); + return nameAndAliases; + } + private static Object wrapTarget(Object target) { if (target instanceof Map) { @SuppressWarnings("unchecked") diff --git a/spring-boot/src/test/java/org/springframework/boot/bind/RelaxedDataBinderTests.java b/spring-boot/src/test/java/org/springframework/boot/bind/RelaxedDataBinderTests.java index b918e6e9784..c0f67e3ef53 100644 --- a/spring-boot/src/test/java/org/springframework/boot/bind/RelaxedDataBinderTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/bind/RelaxedDataBinderTests.java @@ -53,6 +53,7 @@ import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; import static java.lang.annotation.RetentionPolicy.RUNTIME; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; @@ -444,6 +445,28 @@ public class RelaxedDataBinderTests { doTestBindCaseInsensitiveEnums(target); } + @Test + public void testBindWithoutAlais() throws Exception { + VanillaTarget target = new VanillaTarget(); + MutablePropertyValues properties = new MutablePropertyValues(); + properties.add("flub", "a"); + properties.add("foo", "b"); + new RelaxedDataBinder(target).bind(properties); + assertThat(target.getFooBaz(), nullValue()); + assertThat(target.getFoo(), equalTo("b")); + } + + @Test + public void testBindWithAlias() throws Exception { + VanillaTarget target = new VanillaTarget(); + MutablePropertyValues properties = new MutablePropertyValues(); + properties.add("flub", "a"); + properties.add("foo", "b"); + new RelaxedDataBinder(target).withAlias("flub", "fooBaz").bind(properties); + assertThat(target.getFooBaz(), equalTo("a")); + assertThat(target.getFoo(), equalTo("b")); + } + private void doTestBindCaseInsensitiveEnums(VanillaTarget target) throws Exception { BindingResult result = bind(target, "bingo: THIS"); assertThat(result.getErrorCount(), equalTo(0));