diff options
author | Mehdi Javan <32511762+mehdi-vaadin@users.noreply.github.com> | 2018-11-06 08:40:58 +0200 |
---|---|---|
committer | Sun Zhe <31067185+ZheSun88@users.noreply.github.com> | 2018-11-06 08:40:58 +0200 |
commit | 023ec7f48c5ca59d3a995d360500dda6ce9ef32f (patch) | |
tree | 839fca576fd15210480c887ad1df3f3775988902 /server | |
parent | 09207941a982b825dbd429b71c7e89712a3da8f2 (diff) | |
download | vaadin-framework-023ec7f48c5ca59d3a995d360500dda6ce9ef32f.tar.gz vaadin-framework-023ec7f48c5ca59d3a995d360500dda6ce9ef32f.zip |
Fixing NPE in Binder, ported from Flow (#11290)
Diffstat (limited to 'server')
-rw-r--r-- | server/src/main/java/com/vaadin/data/Binder.java | 24 | ||||
-rw-r--r-- | server/src/test/java/com/vaadin/data/UnbindTest.java | 54 |
2 files changed, 73 insertions, 5 deletions
diff --git a/server/src/main/java/com/vaadin/data/Binder.java b/server/src/main/java/com/vaadin/data/Binder.java index 88764a6a2f..d2aff412f6 100644 --- a/server/src/main/java/com/vaadin/data/Binder.java +++ b/server/src/main/java/com/vaadin/data/Binder.java @@ -1005,7 +1005,7 @@ public class Binder<BEAN> implements Serializable { private boolean readOnly; - private final Registration onValueChange; + private Registration onValueChange; private boolean valueInit = false; /** @@ -1071,7 +1071,8 @@ public class Binder<BEAN> implements Serializable { /** * Removes this binding from its binder and unregisters the - * {@code ValueChangeListener} from any bound {@code HasValue}. + * {@code ValueChangeListener} from any bound {@code HasValue}. It does + * nothing if it is called for an already unbound binding. * * @since 8.2 */ @@ -1079,9 +1080,14 @@ public class Binder<BEAN> implements Serializable { public void unbind() { if (onValueChange != null) { onValueChange.remove(); + onValueChange = null; } - binder.removeBindingInternal(this); - binder = null; + + if (binder != null) { + binder.removeBindingInternal(this); + binder = null; + } + field = null; } @@ -1687,7 +1693,15 @@ public class Binder<BEAN> implements Serializable { clearFields(); } else { changedBindings.clear(); - getBindings().forEach(binding -> binding.initFieldValue(bean)); + getBindings().forEach(binding -> { + // Some bindings may have been removed from binder + // during readBean. We should skip those bindings to + // avoid NPE inside initFieldValue. It happens e.g. when + // we unbind a binding in valueChangeListener of another + // field. + if (binding.getField() != null) + binding.initFieldValue(bean); + }); getValidationStatusHandler().statusChange( BinderValidationStatus.createUnresolvedStatus(this)); fireStatusChangeEvent(false); diff --git a/server/src/test/java/com/vaadin/data/UnbindTest.java b/server/src/test/java/com/vaadin/data/UnbindTest.java new file mode 100644 index 0000000000..ca26c34d4c --- /dev/null +++ b/server/src/test/java/com/vaadin/data/UnbindTest.java @@ -0,0 +1,54 @@ +package com.vaadin.data; + +import com.vaadin.tests.data.bean.BeanToValidate; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +public class UnbindTest + extends BinderTestBase<Binder<BeanToValidate>, BeanToValidate> { + @Before + public void setUp() { + binder = new BeanValidationBinder<>(BeanToValidate.class); + item = new BeanToValidate(); + item.setFirstname("Johannes"); + item.setAge(32); + } + + @Test + public void binding_unbind_shouldBeRemovedFromBindings() { + Binder.Binding<BeanToValidate, String> firstnameBinding = binder + .bind(nameField, "firstname"); + Assert.assertEquals(1, binder.getBindings().size()); + firstnameBinding.unbind(); + Assert.assertTrue(binder.getBindings().isEmpty()); + Assert.assertNull(firstnameBinding.getField()); + } + + @Test + public void binding_unbindDuringReadBean_shouldBeRemovedFromBindings() { + Binder.Binding<BeanToValidate, String> firstnameBinding = binder + .bind(nameField, "firstname"); + Binder.Binding<BeanToValidate, String> ageBinding = binder + .bind(ageField, "age"); + Assert.assertEquals(2, binder.getBindings().size()); + nameField.addValueChangeListener(event -> { + if (event.getValue().length() > 0) + ageBinding.unbind(); + }); + binder.readBean(item); + Assert.assertEquals(1, binder.getBindings().size()); + Assert.assertNull(ageBinding.getField()); + } + + @Test + public void binding_unbindTwice_shouldBeRemovedFromBindings() { + Binder.Binding<BeanToValidate, String> firstnameBinding = binder + .bind(nameField, "firstname"); + Assert.assertEquals(1, binder.getBindings().size()); + firstnameBinding.unbind(); + firstnameBinding.unbind(); + Assert.assertTrue(binder.getBindings().isEmpty()); + Assert.assertNull(firstnameBinding.getField()); + } +} |