diff options
author | Artur Signell <artur@vaadin.com> | 2016-08-10 17:41:16 +0300 |
---|---|---|
committer | Vaadin Code Review <review@vaadin.com> | 2016-08-11 10:15:27 +0000 |
commit | 9ea656ab97f19dfd99fad875e8fc589b512e0066 (patch) | |
tree | 77bd0a8ac625482395c3f2d1581d111c8c5953ff /server/src/main | |
parent | fd3617bba2d758ad78689116cfe444d6bfbc1664 (diff) | |
download | vaadin-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.java | 214 |
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 |