From b0979cbab6578c1f5f5a6757c9e80731faa7022d Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Fri, 27 Jun 2014 11:28:09 +0200 Subject: [PATCH] autoGrow support in DataBinder for field access This commit harmonizes the autoGrow feature for both regular bean property and direct field access. Issue: SPR-8692 --- .../validation/DataBinder.java | 4 +- .../validation/DirectFieldBindingResult.java | 14 +++++++ .../tests/sample/beans/FieldAccessBean.java | 4 -- .../DataBinderFieldAccessTests.java | 40 +++++++++++++++---- 4 files changed, 49 insertions(+), 13 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/validation/DataBinder.java b/spring-context/src/main/java/org/springframework/validation/DataBinder.java index 8d5b1af700..1b2db14377 100644 --- a/spring-context/src/main/java/org/springframework/validation/DataBinder.java +++ b/spring-context/src/main/java/org/springframework/validation/DataBinder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 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. @@ -246,7 +246,7 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter { public void initDirectFieldAccess() { Assert.state(this.bindingResult == null, "DataBinder is already initialized - call initDirectFieldAccess before other configuration methods"); - this.bindingResult = new DirectFieldBindingResult(getTarget(), getObjectName()); + this.bindingResult = new DirectFieldBindingResult(getTarget(), getObjectName(), isAutoGrowNestedPaths()); if (this.conversionService != null) { this.bindingResult.initConversion(this.conversionService); } diff --git a/spring-context/src/main/java/org/springframework/validation/DirectFieldBindingResult.java b/spring-context/src/main/java/org/springframework/validation/DirectFieldBindingResult.java index caa986d446..8904240cec 100644 --- a/spring-context/src/main/java/org/springframework/validation/DirectFieldBindingResult.java +++ b/spring-context/src/main/java/org/springframework/validation/DirectFieldBindingResult.java @@ -38,6 +38,8 @@ public class DirectFieldBindingResult extends AbstractPropertyBindingResult { private final Object target; + private final boolean autoGrowNestedPaths; + private transient ConfigurablePropertyAccessor directFieldAccessor; @@ -47,8 +49,19 @@ public class DirectFieldBindingResult extends AbstractPropertyBindingResult { * @param objectName the name of the target object */ public DirectFieldBindingResult(Object target, String objectName) { + this(target, objectName, true); + } + + /** + * Create a new DirectFieldBindingResult instance. + * @param target the target object to bind onto + * @param objectName the name of the target object + * @param autoGrowNestedPaths whether to "auto-grow" a nested path that contains a null value + */ + public DirectFieldBindingResult(Object target, String objectName, boolean autoGrowNestedPaths) { super(objectName); this.target = target; + this.autoGrowNestedPaths = autoGrowNestedPaths; } @@ -67,6 +80,7 @@ public class DirectFieldBindingResult extends AbstractPropertyBindingResult { if (this.directFieldAccessor == null) { this.directFieldAccessor = createDirectFieldAccessor(); this.directFieldAccessor.setExtractOldValueForEditor(true); + this.directFieldAccessor.setAutoGrowNestedPaths(this.autoGrowNestedPaths); } return this.directFieldAccessor; } diff --git a/spring-context/src/test/java/org/springframework/tests/sample/beans/FieldAccessBean.java b/spring-context/src/test/java/org/springframework/tests/sample/beans/FieldAccessBean.java index b387eaf395..700b0af700 100644 --- a/spring-context/src/test/java/org/springframework/tests/sample/beans/FieldAccessBean.java +++ b/spring-context/src/test/java/org/springframework/tests/sample/beans/FieldAccessBean.java @@ -28,10 +28,6 @@ public class FieldAccessBean { private TestBean spouse; - public FieldAccessBean() { - this.spouse = new TestBean(); - } - public String getName() { return name; } diff --git a/spring-context/src/test/java/org/springframework/validation/DataBinderFieldAccessTests.java b/spring-context/src/test/java/org/springframework/validation/DataBinderFieldAccessTests.java index f04b5d2731..9deebe1ac6 100644 --- a/spring-context/src/test/java/org/springframework/validation/DataBinderFieldAccessTests.java +++ b/spring-context/src/test/java/org/springframework/validation/DataBinderFieldAccessTests.java @@ -16,11 +16,16 @@ package org.springframework.validation; +import static org.junit.Assert.*; + import java.beans.PropertyEditorSupport; import java.util.Map; -import junit.framework.TestCase; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.springframework.beans.NullValueInNestedPathException; import org.springframework.tests.sample.beans.FieldAccessBean; import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.NotWritablePropertyException; @@ -32,9 +37,13 @@ import org.springframework.tests.sample.beans.TestBean; * @author Stephane Nicoll * @since 07.03.2006 */ -public class DataBinderFieldAccessTests extends TestCase { +public class DataBinderFieldAccessTests { + + @Rule + public final ExpectedException thrown = ExpectedException.none(); - public void testBindingNoErrors() throws Exception { + @Test + public void bindingNoErrors() throws Exception { FieldAccessBean rod = new FieldAccessBean(); DataBinder binder = new DataBinder(rod, "person"); assertTrue(binder.isIgnoreUnknownFields()); @@ -56,7 +65,8 @@ public class DataBinderFieldAccessTests extends TestCase { assertTrue("Same object", tb.equals(rod)); } - public void testBindingNoErrorsNotIgnoreUnknown() throws Exception { + @Test + public void bindingNoErrorsNotIgnoreUnknown() throws Exception { FieldAccessBean rod = new FieldAccessBean(); DataBinder binder = new DataBinder(rod, "person"); binder.initDirectFieldAccess(); @@ -75,7 +85,8 @@ public class DataBinderFieldAccessTests extends TestCase { } } - public void testBindingWithErrors() throws Exception { + @Test + public void bindingWithErrors() throws Exception { FieldAccessBean rod = new FieldAccessBean(); DataBinder binder = new DataBinder(rod, "person"); binder.initDirectFieldAccess(); @@ -110,7 +121,8 @@ public class DataBinderFieldAccessTests extends TestCase { } } - public void testedNestedBindingWithDefaultConversionNoErrors() throws Exception { + @Test + public void nestedBindingWithDefaultConversionNoErrors() throws Exception { FieldAccessBean rod = new FieldAccessBean(); DataBinder binder = new DataBinder(rod, "person"); assertTrue(binder.isIgnoreUnknownFields()); @@ -126,7 +138,21 @@ public class DataBinderFieldAccessTests extends TestCase { assertTrue((rod.getSpouse()).isJedi()); } - public void testBindingWithErrorsAndCustomEditors() throws Exception { + @Test + public void nestedBindingWithDisabledAutoGrow() throws Exception { + FieldAccessBean rod = new FieldAccessBean(); + DataBinder binder = new DataBinder(rod, "person"); + binder.setAutoGrowNestedPaths(false); + binder.initDirectFieldAccess(); + MutablePropertyValues pvs = new MutablePropertyValues(); + pvs.addPropertyValue(new PropertyValue("spouse.name", "Kerry")); + + thrown.expect(NullValueInNestedPathException.class); + binder.bind(pvs); + } + + @Test + public void bindingWithErrorsAndCustomEditors() throws Exception { FieldAccessBean rod = new FieldAccessBean(); DataBinder binder = new DataBinder(rod, "person"); binder.initDirectFieldAccess();