Bladeren bron

Expose a public Binding interface, add unit tests to Binder

Change-Id: I50651f62ef2defcafb44e5995a113619cc39b17b
feature/vaadin8-book-vol2
Johannes Dahlström 8 jaren geleden
bovenliggende
commit
3937470d86

+ 46
- 36
server/src/main/java/com/vaadin/tokka/data/Binder.java Bestand weergeven

@@ -18,40 +18,42 @@ package com.vaadin.tokka.data;
import java.io.Serializable;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Function;

import com.vaadin.tokka.server.communication.data.SelectionModel;
import com.vaadin.tokka.server.communication.data.SelectionModel.Multi;
import com.vaadin.tokka.server.communication.data.SelectionModel.Single;
import com.vaadin.tokka.ui.components.HasValue;
import com.vaadin.tokka.ui.components.Listing;

// TODO: Should this class listen to changes in DataSource?
public class Binder<T> implements Serializable {

private Set<FieldBinding<T, ?>> bindings = new LinkedHashSet<>();
private T bean;

/**
* Internal class for tracking field bindings with getters and setters.
* Represents the binding between a single field and a property.
*
* @param <T>
* bean type
* the item type
* @param <V>
* value type
* the field value type
*/
public interface Binding<T, V> extends Serializable {
public void bind(Function<T, V> getter, BiConsumer<T, V> setter);
}

/**
* Internal implementation of {@code Binding}.
*/
private static class FieldBinding<T, V> implements Serializable {
private class BindingImpl<V> implements Binding<T, V> {
private HasValue<V> field;
private Function<T, V> getter;
private BiConsumer<T, V> setter;

public FieldBinding(HasValue<V> field, Function<T, V> getter,
BiConsumer<T, V> setter) {
public BindingImpl(HasValue<V> field) {
Objects.requireNonNull(field, "field cannot be null");
this.field = field;
this.getter = getter;
this.setter = setter;
}

void setFieldValue(T bean) {
@@ -63,15 +65,34 @@ public class Binder<T> implements Serializable {
setter.accept(bean, field.getValue());
}
}

@Override
public void bind(Function<T, V> getter, BiConsumer<T, V> setter) {
Objects.requireNonNull(getter, "getter cannot be null");
this.getter = getter;
this.setter = setter;
bindings.add(this);
if (bean != null) {
setFieldValue(bean);
}
}
}

private Set<BindingImpl<?>> bindings = new LinkedHashSet<>();
private T bean;

/**
* Gets the bean that has been bound with {@link #bind}.
* 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.
* @return the currently bound bean if any
*/
public T getBean() {
return bean;
public Optional<T> getBean() {
return Optional.ofNullable(bean);
}

public <V> Binding<T, V> addField(HasValue<V> field) {
return new BindingImpl<>(field);
}

/**
@@ -87,26 +108,17 @@ public class Binder<T> implements Serializable {
* problems.
*
* @param field
* editor field
* editor field, not null
* @param getter
* getter method to fetch data from the bean
* getter method to fetch data from the bean, not null
* @param setter
* setter method to store data back to the bean
* setter method to store data back to the bean or null if the
* field should be read-only
*/
public <V> void addField(HasValue<V> field, Function<T, V> getter,
BiConsumer<T, V> setter) {
if (getter == null || field == null) {
throw new IllegalArgumentException();
}

FieldBinding<T, V> binding = new FieldBinding<>(field, getter, setter);
bindings.add(binding);

if (bean != null) {
binding.setFieldValue(bean);
}

field.addValueChangeListener(e -> handleChangeEvent(field));
BindingImpl<V> binding = new BindingImpl<>(field);
binding.bind(getter, setter);
}

/**
@@ -123,7 +135,7 @@ public class Binder<T> implements Serializable {
return;
}

for (FieldBinding<T, ?> binding : bindings) {
for (BindingImpl<?> binding : bindings) {
binding.setFieldValue(bean);
}
}
@@ -164,7 +176,7 @@ public class Binder<T> implements Serializable {
return;
}

for (FieldBinding<T, ?> binding : bindings) {
for (BindingImpl<?> binding : bindings) {
binding.storeFieldValue(bean);
}

@@ -180,8 +192,6 @@ public class Binder<T> implements Serializable {
bind(bean);
}

/* PROTECTED SCOPE */

// Exists for the sake of making something before / after field update is
// processed
/**

+ 81
- 0
server/src/test/java/com/vaadin/tokka/data/BinderTest.java Bestand weergeven

@@ -0,0 +1,81 @@
package com.vaadin.tokka.data;

import static org.junit.Assert.assertEquals;

import org.junit.Before;
import org.junit.Test;

import com.vaadin.tests.data.bean.Person;
import com.vaadin.tokka.data.Binder;
import com.vaadin.tokka.ui.components.fields.TextField;

public class BinderTest {

Binder<Person> binder;

TextField nameField;
TextField ageField;

Person p = new Person();

@Before
public void setUp() {
binder = new Binder<>();
p.setFirstName("Johannes");
p.setAge(32);
nameField = new TextField();
ageField = new TextField();
}

@Test
public void testAddFieldAndBind() {
binder.addField(nameField).bind(Person::getFirstName,
Person::setFirstName);
binder.bind(p);

assertEquals("Johannes", nameField.getValue());
}

@Test
public void testAddFieldShortcut() {
binder.addField(nameField, Person::getFirstName, Person::setFirstName);
binder.bind(p);

assertEquals("Johannes", nameField.getValue());
}

@Test
public void testValueChangeOnSave() {
binder.addField(nameField, Person::getFirstName, Person::setFirstName);
binder.bind(p);

nameField.setValue("Teemu");

assertEquals("Johannes", p.getFirstName());

binder.save();

assertEquals("Teemu", p.getFirstName());
}

@Test
public void testAddFieldAfterBinding() {
binder.bind(p);
binder.addField(nameField, Person::getFirstName, Person::setFirstName);

assertEquals("Johannes", nameField.getValue());
}

@Test
public void testNoValueChangesAfterUnbind() {
binder.addField(nameField, Person::getFirstName, Person::setFirstName);
binder.bind(p);

binder.bind(null);

nameField.setValue("Teemu");
binder.save();

assertEquals("Johannes", p.getFirstName());
}
}

Laden…
Annuleren
Opslaan