aboutsummaryrefslogtreecommitdiffstats
path: root/server/src/main
diff options
context:
space:
mode:
authorArtur Signell <artur@vaadin.com>2016-08-10 17:41:16 +0300
committerVaadin Code Review <review@vaadin.com>2016-08-11 10:15:27 +0000
commit9ea656ab97f19dfd99fad875e8fc589b512e0066 (patch)
tree77bd0a8ac625482395c3f2d1581d111c8c5953ff /server/src/main
parentfd3617bba2d758ad78689116cfe444d6bfbc1664 (diff)
downloadvaadin-framework-9ea656ab97f19dfd99fad875e8fc589b512e0066.tar.gz
vaadin-framework-9ea656ab97f19dfd99fad875e8fc589b512e0066.zip
Refactor Binder and Binding/BindingImpl to be easier to understand
Change-Id: I675dabf9f15b673b04495db9efd315d8742afd7e
Diffstat (limited to 'server/src/main')
-rw-r--r--server/src/main/java/com/vaadin/data/Binder.java214
1 files changed, 121 insertions, 93 deletions
diff --git a/server/src/main/java/com/vaadin/data/Binder.java b/server/src/main/java/com/vaadin/data/Binder.java
index a34db33e51..0fbacce994 100644
--- a/server/src/main/java/com/vaadin/data/Binder.java
+++ b/server/src/main/java/com/vaadin/data/Binder.java
@@ -1,12 +1,12 @@
/*
* Copyright 2000-2014 Vaadin Ltd.
- *
+ *
* 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
@@ -46,30 +46,33 @@ import com.vaadin.ui.AbstractComponent;
* view, where a select component is used to pick the bean to edit.
* <p>
* Unless otherwise specified, {@code Binder} method arguments cannot be null.
- *
+ *
* @author Vaadin Ltd.
*
- * @param <T>
+ * @param <BEAN>
* the bean type
- *
+ *
* @see Binding
* @see HasValue
- *
+ *
* @since
*/
-public class Binder<T> implements Serializable {
+public class Binder<BEAN> implements Serializable {
/**
- * Represents the binding between a single field and a property.
+ * Represents the binding between a field and a data property.
+ *
+ * @param <BEAN>
+ * the bean type
+ * @param <FIELDVALUE>
+ * the value type of the field
+ * @param <TARGET>
+ * the target data type of the binding, matches the field type
+ * until a converter has been set
*
- * @param <T>
- * the item type
- * @param <V>
- * the field value type
- *
* @see Binder#forField(HasValue)
*/
- public interface Binding<T, V> extends Serializable {
+ public interface Binding<BEAN, FIELDVALUE, TARGET> extends Serializable {
/**
* Completes this binding using the given getter and setter functions
@@ -77,8 +80,8 @@ public class Binder<T> implements Serializable {
* update the field value from the property and to store the field value
* to the property, respectively.
* <p>
- * When a bean is bound with {@link Binder#bind(T)}, the field value is
- * set to the return value of the given getter. The property value is
+ * When a bean is bound with {@link Binder#bind(BEAN)}, the field value
+ * is set to the return value of the given getter. The property value is
* then updated via the given setter whenever the field value changes.
* The setter may be null; in that case the property value is never
* updated and the binding is said to be <i>read-only</i>.
@@ -90,17 +93,17 @@ public class Binder<T> implements Serializable {
* implementing user-defined conversion or validation. However, in the
* most basic use case you can simply pass a pair of method references
* to this method as follows:
- *
+ *
* <pre>
* class Person {
* public String getName() { ... }
* public void setName(String name) { ... }
* }
- *
+ *
* TextField nameField = new TextField();
* binder.forField(nameField).bind(Person::getName, Person::setName);
* </pre>
- *
+ *
* @param getter
* the function to get the value of the property to the
* field, not null
@@ -110,21 +113,23 @@ public class Binder<T> implements Serializable {
* @throws IllegalStateException
* if {@code bind} has already been called on this binding
*/
- public void bind(Function<T, V> getter, BiConsumer<T, V> setter);
+ public void bind(Function<BEAN, TARGET> getter,
+ BiConsumer<BEAN, TARGET> setter);
/**
* Adds a validator to this binding. Validators are applied, in
* registration order, when the field value is saved to the backing
* property. If any validator returns a failure, the property value is
* not updated.
- *
+ *
* @param validator
* the validator to add, not null
* @return this binding, for chaining
* @throws IllegalStateException
* if {@code bind} has already been called
*/
- public Binding<T, V> withValidator(Validator<? super V> validator);
+ public Binding<BEAN, FIELDVALUE, TARGET> withValidator(
+ Validator<? super TARGET> validator);
/**
* A convenience method to add a validator to this binding using the
@@ -133,10 +138,10 @@ public class Binder<T> implements Serializable {
* Validators are applied, in registration order, when the field value
* is saved to the backing property. If any validator returns a failure,
* the property value is not updated.
- *
+ *
* @see #withValidator(Validator)
* @see Validator#from(Predicate, String)
- *
+ *
* @param predicate
* the predicate performing validation, not null
* @param message
@@ -145,51 +150,70 @@ public class Binder<T> implements Serializable {
* @throws IllegalStateException
* if {@code bind} has already been called
*/
- public Binding<T, V> withValidator(Predicate<? super V> predicate,
- String message);
+ public Binding<BEAN, FIELDVALUE, TARGET> withValidator(
+ Predicate<? super TARGET> predicate, String message);
+
+ /**
+ * Gets the field the binding uses.
+ *
+ * @return the field for the binding
+ */
+ public HasValue<FIELDVALUE> getField();
+
}
/**
* An internal implementation of {@code Binding}.
- *
- * @param <V>
- * the value type
+ *
+ * @param <BEAN>
+ * the bean type, must match the Binder bean type
+ * @param <FIELDVALUE>
+ * the value type of the field
+ * @param <TARGET>
+ * the target data type of the binding, matches the field type
+ * until a converter has been set
*/
- protected class BindingImpl<V> implements Binding<T, V> {
+ protected static class BindingImpl<BEAN, FIELDVALUE, TARGET>
+ implements Binding<BEAN, FIELDVALUE, TARGET> {
+
+ private Binder<BEAN> binder;
- private HasValue<V> field;
+ private HasValue<FIELDVALUE> field;
private Registration onValueChange;
- private Function<T, V> getter;
- private BiConsumer<T, V> setter;
+ private Function<BEAN, TARGET> getter;
+ private BiConsumer<BEAN, TARGET> setter;
- private List<Validator<? super V>> validators = new ArrayList<>();
+ private List<Validator<? super TARGET>> validators = new ArrayList<>();
/**
* Creates a new binding associated with the given field.
- *
+ *
+ * @param binder
+ * the binder this instance is connected to
* @param field
* the field to bind
*/
- protected BindingImpl(HasValue<V> field) {
+ protected BindingImpl(Binder<BEAN> binder, HasValue<FIELDVALUE> field) {
+ this.binder = binder;
this.field = field;
}
@Override
- public void bind(Function<T, V> getter, BiConsumer<T, V> setter) {
+ public void bind(Function<BEAN, TARGET> getter,
+ BiConsumer<BEAN, TARGET> setter) {
checkUnbound();
Objects.requireNonNull(getter, "getter cannot be null");
this.getter = getter;
this.setter = setter;
- bindings.add(this);
- if (bean != null) {
- bind(bean);
- }
+ binder.bindings.add(this);
+ binder.getBean().ifPresent(this::bind);
}
@Override
- public Binding<T, V> withValidator(Validator<? super V> validator) {
+ public Binding<BEAN, FIELDVALUE, TARGET> withValidator(
+ Validator<? super TARGET> validator) {
checkUnbound();
Objects.requireNonNull(validator, "validator cannot be null");
validators.add(validator);
@@ -197,20 +221,21 @@ public class Binder<T> implements Serializable {
}
@Override
- public Binding<T, V> withValidator(Predicate<? super V> predicate,
- String message) {
+ public Binding<BEAN, FIELDVALUE, TARGET> withValidator(
+ Predicate<? super TARGET> predicate, String message) {
return withValidator(Validator.from(predicate, message));
}
- private void bind(T bean) {
+ private void bind(BEAN bean) {
setFieldValue(bean);
onValueChange = field
.addValueChangeListener(e -> storeFieldValue(bean));
}
- private List<ValidationError<V>> validate() {
+ private List<ValidationError<FIELDVALUE>> validate() {
return validators.stream()
- .map(validator -> validator.apply(field.getValue()))
+ .map(validator -> validator
+ .apply((TARGET) field.getValue()))
.filter(Result::isError)
.map(result -> new ValidationError<>(field,
result.getMessage().orElse(null)))
@@ -224,26 +249,26 @@ public class Binder<T> implements Serializable {
/**
* Sets the field value by invoking the getter function on the given
* bean.
- *
+ *
* @param bean
* the bean to fetch the property value from
*/
- private void setFieldValue(T bean) {
+ private void setFieldValue(BEAN bean) {
assert bean != null;
- field.setValue(getter.apply(bean));
+ field.setValue((FIELDVALUE) getter.apply(bean));
}
/**
* Saves the field value by invoking the setter function on the given
* bean, if the value passes all registered validators.
- *
+ *
* @param bean
* the bean to set the property value to
*/
- private void storeFieldValue(T bean) {
+ private void storeFieldValue(BEAN bean) {
assert bean != null;
if (setter != null) {
- setter.accept(bean, field.getValue());
+ setter.accept(bean, (TARGET) field.getValue());
}
}
@@ -254,19 +279,23 @@ public class Binder<T> implements Serializable {
}
}
+ @Override
+ public HasValue<FIELDVALUE> getField() {
+ return field;
+ }
}
- private T bean;
+ private BEAN bean;
- private Set<BindingImpl<?>> bindings = new LinkedHashSet<>();
+ private Set<BindingImpl<BEAN, ?, ?>> bindings = new LinkedHashSet<>();
/**
* Returns an {@code Optional} of the bean that has been bound with
* {@link #bind}, or an empty optional if a bean is not currently bound.
- *
+ *
* @return the currently bound bean if any
*/
- public Optional<T> getBean() {
+ public Optional<BEAN> getBean() {
return Optional.ofNullable(bean);
}
@@ -276,14 +305,15 @@ public class Binder<T> implements Serializable {
* {@link Binding#bind(Function, BiConsumer) Binding.bind} which completes
* the binding. Until {@code Binding.bind} is called, the binding has no
* effect.
- *
- * @param <V>
+ *
+ * @param <FIELDVALUE>
* the value type of the field
* @param field
* the field to be bound, not null
* @return the new binding
*/
- public <V> Binding<T, V> forField(HasValue<V> field) {
+ public <FIELDVALUE> Binding<BEAN, FIELDVALUE, FIELDVALUE> forField(
+ HasValue<FIELDVALUE> field) {
return createBinding(field);
}
@@ -295,8 +325,8 @@ public class Binder<T> implements Serializable {
* Use the {@link #forField(HasValue)} overload instead if you want to
* further configure the new binding.
* <p>
- * When a bean is bound with {@link Binder#bind(T)}, the field value is set
- * to the return value of the given getter. The property value is then
+ * When a bean is bound with {@link Binder#bind(BEAN)}, the field value is
+ * set to the return value of the given getter. The property value is then
* updated via the given setter whenever the field value changes. The setter
* may be null; in that case the property value is never updated and the
* binding is said to be <i>read-only</i>.
@@ -308,18 +338,18 @@ public class Binder<T> implements Serializable {
* implementing user-defined conversion or validation. However, in the most
* basic use case you can simply pass a pair of method references to this
* method as follows:
- *
+ *
* <pre>
* class Person {
* public String getName() { ... }
* public void setName(String name) { ... }
* }
- *
+ *
* TextField nameField = new TextField();
* binder.bind(nameField, Person::getName, Person::setName);
* </pre>
- *
- * @param <V>
+ *
+ * @param <FIELDVALUE>
* the value type of the field
* @param field
* the field to bind, not null
@@ -330,8 +360,9 @@ public class Binder<T> implements Serializable {
* the function to save the field value to the property or null
* if read-only
*/
- public <V> void bind(HasValue<V> field, Function<T, V> getter,
- BiConsumer<T, V> setter) {
+ public <FIELDVALUE> void bind(HasValue<FIELDVALUE> field,
+ Function<BEAN, FIELDVALUE> getter,
+ BiConsumer<BEAN, FIELDVALUE> setter) {
forField(field).bind(getter, setter);
}
@@ -343,11 +374,11 @@ public class Binder<T> implements Serializable {
* corresponding getter functions. Any changes to field values are reflected
* back to their corresponding property values of the bean as long as the
* bean is bound.
- *
+ *
* @param bean
* the bean to edit, not null
*/
- public void bind(T bean) {
+ public void bind(BEAN bean) {
Objects.requireNonNull(bean, "bean cannot be null");
unbind();
this.bean = bean;
@@ -359,17 +390,17 @@ public class Binder<T> implements Serializable {
* validation as a set of validation errors.
* <p>
* Validation is successful if the resulting set is empty.
- *
+ *
* @return the validation result.
*/
public List<ValidationError<?>> validate() {
List<ValidationError<?>> resultErrors = new ArrayList<>();
- for (BindingImpl<?> binding : bindings) {
- clearError(binding.field);
+ for (BindingImpl<BEAN, ?, ?> binding : bindings) {
+ clearError(binding.getField());
List<? extends ValidationError<?>> errors = binding.validate();
resultErrors.addAll(errors);
if (!errors.isEmpty()) {
- handleError(binding.field, errors.get(0).getMessage());
+ handleError(binding.getField(), errors.get(0).getMessage());
}
}
return resultErrors;
@@ -390,16 +421,14 @@ public class Binder<T> implements Serializable {
* Reads the bound property values from the given bean to the corresponding
* fields. The bean is not otherwise associated with this binder; in
* particular its property values are not bound to the field value changes.
- * To achieve that, use {@link #bind(T)}.
- *
+ * To achieve that, use {@link #bind(BEAN)}.
+ *
* @param bean
* the bean whose property values to read, not null
*/
- public void load(T bean) {
+ public void load(BEAN bean) {
Objects.requireNonNull(bean, "bean cannot be null");
- bindings.forEach(
-
- binding -> binding.setFieldValue(bean));
+ bindings.forEach(binding -> binding.setFieldValue(bean));
}
@@ -413,26 +442,25 @@ public class Binder<T> implements Serializable {
* @throws BindingException
* if some of the bound field values fail to validate
*/
- public void save(T bean) {
+ public void save(BEAN bean) {
Objects.requireNonNull(bean, "bean cannot be null");
- bindings.forEach(
-
- binding -> binding.storeFieldValue(bean));
-
+ bindings.forEach(binding -> binding.storeFieldValue(bean));
}
/**
* Creates a new binding with the given field.
- *
- * @param <V>
- * the field value type
+ *
+ * @param <FIELDVALUE>
+ * the value type of the field
* @param field
* the field to bind
* @return the new incomplete binding
*/
- protected <V> BindingImpl<V> createBinding(HasValue<V> field) {
+ protected <FIELDVALUE> Binding<BEAN, FIELDVALUE, FIELDVALUE> createBinding(
+ HasValue<FIELDVALUE> field) {
Objects.requireNonNull(field, "field cannot be null");
- BindingImpl<V> b = new BindingImpl<>(field);
+ BindingImpl<BEAN, FIELDVALUE, FIELDVALUE> b = new BindingImpl<>(this,
+ field);
return b;
}
@@ -441,7 +469,7 @@ public class Binder<T> implements Serializable {
* implementation clears the
* {@link AbstractComponent#setComponentError(ErrorMessage) component error}
* of the field if it is a Component, otherwise does nothing.
- *
+ *
* @param field
* the field with an invalid value
*/
@@ -456,7 +484,7 @@ public class Binder<T> implements Serializable {
* given field. The default implementation sets the
* {@link AbstractComponent#setComponentError(ErrorMessage) component error}
* of the field if it is a Component, otherwise does nothing.
- *
+ *
* @param field
* the field with the invalid value
* @param error