Binds class instance fields using reflection using Binder.forField() Change-Id: I597f3832d112cfa69c73fb185f1564c482e4eb15tags/8.0.0.alpha6
@@ -25,6 +25,7 @@ import java.util.LinkedHashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
import com.vaadin.annotations.PropertyId; | |||
import com.vaadin.util.ReflectTools; | |||
import com.vaadin.v7.data.Item; | |||
import com.vaadin.v7.data.Property; |
@@ -4,8 +4,8 @@ import static org.junit.Assert.assertTrue; | |||
import org.junit.Test; | |||
import com.vaadin.annotations.PropertyId; | |||
import com.vaadin.v7.data.fieldgroup.FieldGroup; | |||
import com.vaadin.v7.data.fieldgroup.PropertyId; | |||
import com.vaadin.v7.ui.TextField; | |||
public class ReflectToolsGetSuperFieldTest { |
@@ -5,10 +5,10 @@ import static org.junit.Assert.assertEquals; | |||
import org.junit.Assert; | |||
import org.junit.Test; | |||
import com.vaadin.annotations.PropertyId; | |||
import com.vaadin.v7.data.Item; | |||
import com.vaadin.v7.data.fieldgroup.BeanFieldGroup; | |||
import com.vaadin.v7.data.fieldgroup.FieldGroup.CommitException; | |||
import com.vaadin.v7.data.fieldgroup.PropertyId; | |||
import com.vaadin.v7.data.util.BeanItem; | |||
import com.vaadin.v7.ui.Field; | |||
import com.vaadin.v7.ui.RichTextArea; |
@@ -4,9 +4,9 @@ import static org.junit.Assert.assertTrue; | |||
import org.junit.Test; | |||
import com.vaadin.annotations.PropertyId; | |||
import com.vaadin.ui.FormLayout; | |||
import com.vaadin.v7.data.fieldgroup.FieldGroup; | |||
import com.vaadin.v7.data.fieldgroup.PropertyId; | |||
import com.vaadin.v7.data.util.ObjectProperty; | |||
import com.vaadin.v7.data.util.PropertysetItem; | |||
import com.vaadin.v7.ui.TextField; |
@@ -80,6 +80,13 @@ | |||
<artifactId>jsoup</artifactId> | |||
</dependency> | |||
<!-- Small reflection library --> | |||
<dependency> | |||
<groupId>com.googlecode.gentyref</groupId> | |||
<artifactId>gentyref</artifactId> | |||
<version>1.2.0</version> | |||
</dependency> | |||
<!-- TESTING DEPENDENCIES --> | |||
<!-- Test dependencies --> |
@@ -13,25 +13,27 @@ | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.v7.data.fieldgroup; | |||
package com.vaadin.annotations; | |||
import java.lang.annotation.ElementType; | |||
import java.lang.annotation.Retention; | |||
import java.lang.annotation.RetentionPolicy; | |||
import java.lang.annotation.Target; | |||
import com.vaadin.v7.ui.Field; | |||
import com.vaadin.data.BeanBinder; | |||
import com.vaadin.data.Binder; | |||
import com.vaadin.data.HasValue; | |||
/** | |||
* Defines the custom property name to be bound to a {@link Field} using | |||
* {@link FieldGroup} or {@link BeanFieldGroup}. | |||
* {@link Binder} or {@link BeanBinder}. | |||
* <p> | |||
* The automatic data binding in FieldGroup and BeanFieldGroup relies on a | |||
* naming convention by default: properties of an item are bound to similarly | |||
* named field components in given a editor object. If you want to map a | |||
* property with a different name (ID) to a {@link com.vaadin.client.ui.Field}, | |||
* you can use this annotation for the member fields, with the name (ID) of the | |||
* desired property as the parameter. | |||
* The automatic data binding in Binder and BeanBinder relies on a naming | |||
* convention by default: properties of an item are bound to similarly named | |||
* field components in given a editor object. If you want to map a property with | |||
* a different name (ID) to a {@link HasValue}, you can use this annotation for | |||
* the member fields, with the name (ID) of the desired property as the | |||
* parameter. | |||
* <p> | |||
* In following usage example, the text field would be bound to property "foo" | |||
* in the Entity class. <code> | |||
@@ -46,8 +48,9 @@ import com.vaadin.v7.ui.Field; | |||
} | |||
{ | |||
Editor e = new Editor(); | |||
BeanFieldGroup.bindFieldsUnbuffered(new Entity(), e); | |||
Editor editor = new Editor(); | |||
BeanBinder<Entity> binder = new BeanBinder(Entity.class); | |||
binder.bindInstanceFields(editor); | |||
} | |||
</pre> | |||
* </code> | |||
@@ -57,7 +60,6 @@ import com.vaadin.v7.ui.Field; | |||
*/ | |||
@Target({ ElementType.FIELD }) | |||
@Retention(RetentionPolicy.RUNTIME) | |||
@Deprecated | |||
public @interface PropertyId { | |||
String value(); | |||
} |
@@ -18,10 +18,21 @@ package com.vaadin.data; | |||
import java.beans.IntrospectionException; | |||
import java.beans.PropertyDescriptor; | |||
import java.lang.reflect.Field; | |||
import java.lang.reflect.InvocationTargetException; | |||
import java.lang.reflect.Method; | |||
import java.lang.reflect.Type; | |||
import java.util.ArrayList; | |||
import java.util.HashSet; | |||
import java.util.List; | |||
import java.util.Locale; | |||
import java.util.Objects; | |||
import java.util.Optional; | |||
import java.util.Set; | |||
import java.util.function.BiConsumer; | |||
import com.googlecode.gentyref.GenericTypeReflector; | |||
import com.vaadin.annotations.PropertyId; | |||
import com.vaadin.data.util.BeanUtil; | |||
import com.vaadin.data.util.converter.Converter; | |||
import com.vaadin.data.validator.BeanValidator; | |||
@@ -184,6 +195,7 @@ public class BeanBinder<BEAN> extends Binder<BEAN> { | |||
getter = descriptor.getReadMethod(); | |||
setter = descriptor.getWriteMethod(); | |||
finalBinding.bind(this::getValue, this::setValue); | |||
getBinder().boundProperties.add(propertyName); | |||
} | |||
@Override | |||
@@ -253,6 +265,7 @@ public class BeanBinder<BEAN> extends Binder<BEAN> { | |||
} | |||
private final Class<? extends BEAN> beanType; | |||
private final Set<String> boundProperties; | |||
/** | |||
* Creates a new {@code BeanBinder} supporting beans of the given type. | |||
@@ -263,6 +276,7 @@ public class BeanBinder<BEAN> extends Binder<BEAN> { | |||
public BeanBinder(Class<? extends BEAN> beanType) { | |||
BeanUtil.checkBeanValidationAvailable(); | |||
this.beanType = beanType; | |||
boundProperties = new HashSet<>(); | |||
} | |||
@Override | |||
@@ -317,4 +331,223 @@ public class BeanBinder<BEAN> extends Binder<BEAN> { | |||
return new BeanBindingImpl<>(this, field, converter, handler); | |||
} | |||
/** | |||
* Binds member fields found in the given object. | |||
* <p> | |||
* This method processes all (Java) member fields whose type extends | |||
* {@link HasValue} and that can be mapped to a property id. Property id | |||
* mapping is done based on the field name or on a @{@link PropertyId} | |||
* annotation on the field. All non-null unbound fields for which a property | |||
* id can be determined are bound to the property id. | |||
* </p> | |||
* <p> | |||
* For example: | |||
* | |||
* <pre> | |||
* public class MyForm extends VerticalLayout { | |||
* private TextField firstName = new TextField("First name"); | |||
* @PropertyId("last") | |||
* private TextField lastName = new TextField("Last name"); | |||
* | |||
* MyForm myForm = new MyForm(); | |||
* ... | |||
* binder.bindMemberFields(myForm); | |||
* </pre> | |||
* | |||
* </p> | |||
* This binds the firstName TextField to a "firstName" property in the item, | |||
* lastName TextField to a "last" property. | |||
* <p> | |||
* It's not always possible to bind a field to a property because their | |||
* types are incompatible. E.g. custom converter is required to bind | |||
* {@code HasValue<String>} and {@code Integer} property (that would be a | |||
* case of "age" property). In such case {@link IllegalStateException} will | |||
* be thrown unless the field has been configured manually before calling | |||
* the {@link #bindInstanceFields(Object)} method. | |||
* <p> | |||
* It's always possible to do custom binding for any field: the | |||
* {@link #bindInstanceFields(Object)} method doesn't override existing | |||
* bindings. | |||
* | |||
* @param objectWithMemberFields | |||
* The object that contains (Java) member fields to bind | |||
* @throws IllegalStateException | |||
* if there are incompatible HasValue<T> and property types | |||
*/ | |||
public void bindInstanceFields(Object objectWithMemberFields) { | |||
Class<?> objectClass = objectWithMemberFields.getClass(); | |||
getFieldsInDeclareOrder(objectClass).stream() | |||
.filter(memberField -> HasValue.class | |||
.isAssignableFrom(memberField.getType())) | |||
.forEach(memberField -> handleProperty(memberField, | |||
(property, type) -> bindProperty(objectWithMemberFields, | |||
memberField, property, type))); | |||
} | |||
/** | |||
* Binds {@code property} with {@code propertyType} to the field in the | |||
* {@code objectWithMemberFields} instance using {@code memberField} as a | |||
* reference to a member. | |||
* | |||
* @param objectWithMemberFields | |||
* the object that contains (Java) member fields to build and | |||
* bind | |||
* @param memberField | |||
* reference to a member field to bind | |||
* @param property | |||
* property name to bind | |||
* @param propertyType | |||
* type of the property | |||
*/ | |||
protected void bindProperty(Object objectWithMemberFields, | |||
Field memberField, String property, Class<?> propertyType) { | |||
Type valueType = GenericTypeReflector.getTypeParameter( | |||
memberField.getGenericType(), | |||
HasValue.class.getTypeParameters()[0]); | |||
if (valueType == null) { | |||
throw new IllegalStateException(String.format( | |||
"Unable to detect value type for the member '%s' in the " | |||
+ "class '%s'.", | |||
memberField.getName(), | |||
objectWithMemberFields.getClass().getName())); | |||
} | |||
if (propertyType.equals(valueType)) { | |||
HasValue<?> field; | |||
// Get the field from the object | |||
try { | |||
field = (HasValue<?>) ReflectTools.getJavaFieldValue( | |||
objectWithMemberFields, memberField, HasValue.class); | |||
} catch (IllegalArgumentException | IllegalAccessException | |||
| InvocationTargetException e) { | |||
// If we cannot determine the value, just skip the field | |||
return; | |||
} | |||
if (field == null) { | |||
field = makeFieldInstance( | |||
(Class<? extends HasValue<?>>) memberField.getType()); | |||
initializeField(objectWithMemberFields, memberField, field); | |||
} | |||
forField(field).bind(property); | |||
} else { | |||
throw new IllegalStateException(String.format( | |||
"Property type '%s' doesn't " | |||
+ "match the field type '%s'. " | |||
+ "Binding should be configured manulaly using converter.", | |||
propertyType.getName(), valueType.getTypeName())); | |||
} | |||
} | |||
/** | |||
* Makes an instance of the field type {@code fieldClass}. | |||
* <p> | |||
* The resulting field instance is used to bind a property to it using the | |||
* {@link #bindInstanceFields(Object)} method. | |||
* <p> | |||
* The default implementation relies on the default constructor of the | |||
* class. If there is no suitable default constructor or you want to | |||
* configure the instantiated class then override this method and provide | |||
* your own implementation. | |||
* | |||
* @see #bindInstanceFields(Object) | |||
* @param fieldClass | |||
* type of the field | |||
* @return a {@code fieldClass} instance object | |||
*/ | |||
protected HasValue<?> makeFieldInstance( | |||
Class<? extends HasValue<?>> fieldClass) { | |||
try { | |||
return fieldClass.newInstance(); | |||
} catch (InstantiationException | IllegalAccessException e) { | |||
throw new IllegalStateException( | |||
String.format("Couldn't create an '%s' type instance", | |||
fieldClass.getName()), | |||
e); | |||
} | |||
} | |||
/** | |||
* Returns an array containing {@link Field} objects reflecting all the | |||
* fields of the class or interface represented by this Class object. The | |||
* elements in the array returned are sorted in declare order from sub class | |||
* to super class. | |||
* | |||
* @param searchClass | |||
* class to introspect | |||
* @return list of all fields in the class considering hierarchy | |||
*/ | |||
protected List<Field> getFieldsInDeclareOrder(Class<?> searchClass) { | |||
ArrayList<Field> memberFieldInOrder = new ArrayList<>(); | |||
while (searchClass != null) { | |||
for (Field memberField : searchClass.getDeclaredFields()) { | |||
memberFieldInOrder.add(memberField); | |||
} | |||
searchClass = searchClass.getSuperclass(); | |||
} | |||
return memberFieldInOrder; | |||
} | |||
private void initializeField(Object objectWithMemberFields, | |||
Field memberField, HasValue<?> value) { | |||
try { | |||
ReflectTools.setJavaFieldValue(objectWithMemberFields, memberField, | |||
value); | |||
} catch (IllegalArgumentException | IllegalAccessException | |||
| InvocationTargetException e) { | |||
throw new IllegalStateException( | |||
String.format("Could not assign value to field '%s'", | |||
memberField.getName()), | |||
e); | |||
} | |||
} | |||
private void handleProperty(Field field, | |||
BiConsumer<String, Class<?>> propertyHandler) { | |||
Optional<PropertyDescriptor> descriptor = getPropertyDescriptor(field); | |||
if (!descriptor.isPresent()) { | |||
return; | |||
} | |||
String propertyName = descriptor.get().getName(); | |||
if (boundProperties.contains(propertyName)) { | |||
return; | |||
} | |||
propertyHandler.accept(propertyName, | |||
descriptor.get().getPropertyType()); | |||
boundProperties.add(propertyName); | |||
} | |||
private Optional<PropertyDescriptor> getPropertyDescriptor(Field field) { | |||
PropertyId propertyIdAnnotation = field.getAnnotation(PropertyId.class); | |||
String propertyId; | |||
if (propertyIdAnnotation != null) { | |||
// @PropertyId(propertyId) always overrides property id | |||
propertyId = propertyIdAnnotation.value(); | |||
} else { | |||
propertyId = field.getName(); | |||
} | |||
List<PropertyDescriptor> descriptors; | |||
try { | |||
descriptors = BeanUtil.getBeanPropertyDescriptors(beanType); | |||
} catch (IntrospectionException e) { | |||
throw new IllegalArgumentException(String.format( | |||
"Could not resolve bean '%s' properties (see the cause):", | |||
beanType.getName()), e); | |||
} | |||
Optional<PropertyDescriptor> propertyDescitpor = descriptors.stream() | |||
.filter(descriptor -> minifyFieldName(descriptor.getName()) | |||
.equals(minifyFieldName(propertyId))) | |||
.findFirst(); | |||
return propertyDescitpor; | |||
} | |||
private String minifyFieldName(String fieldName) { | |||
return fieldName.toLowerCase(Locale.ENGLISH).replace("_", ""); | |||
} | |||
} |
@@ -1200,7 +1200,7 @@ public class Binder<BEAN> implements Serializable { | |||
* the handler to notify of status changes, not null | |||
* @return the new incomplete binding | |||
*/ | |||
protected <FIELDVALUE, TARGET> BindingImpl<BEAN, FIELDVALUE, TARGET> createBinding( | |||
protected <FIELDVALUE, TARGET> Binding<BEAN, FIELDVALUE, TARGET> createBinding( | |||
HasValue<FIELDVALUE> field, Converter<FIELDVALUE, TARGET> converter, | |||
ValidationStatusHandler handler) { | |||
return new BindingImpl<>(this, field, converter, handler); |
@@ -0,0 +1,387 @@ | |||
/* | |||
* Copyright 2000-2016 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 | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.data; | |||
import java.time.LocalDate; | |||
import org.junit.Assert; | |||
import org.junit.Test; | |||
import com.vaadin.annotations.PropertyId; | |||
import com.vaadin.data.util.converter.StringToIntegerConverter; | |||
import com.vaadin.data.validator.StringLengthValidator; | |||
import com.vaadin.tests.data.bean.Person; | |||
import com.vaadin.ui.AbstractField; | |||
import com.vaadin.ui.AbstractTextField; | |||
import com.vaadin.ui.DateField; | |||
import com.vaadin.ui.FormLayout; | |||
import com.vaadin.ui.TextField; | |||
/** | |||
* Unit tests for {@link BeanBinder#bindInstanceFields(Object)} method. | |||
* | |||
* @author Vaadin Ltd | |||
* | |||
*/ | |||
public class BeanBinderInstanceFieldTest { | |||
public static class BindAllFields extends FormLayout { | |||
private TextField firstName; | |||
private DateField birthDate; | |||
} | |||
public static class BindFieldsUsingAnnotation extends FormLayout { | |||
@PropertyId("firstName") | |||
private TextField nameField; | |||
@PropertyId("birthDate") | |||
private DateField birthDateField; | |||
} | |||
public static class BindOnlyOneField extends FormLayout { | |||
private TextField firstName; | |||
private TextField noFieldInPerson; | |||
} | |||
public static class BindNoHasValueField extends FormLayout { | |||
private String firstName; | |||
} | |||
public static class BindGenericField extends FormLayout { | |||
private CustomField<String> firstName; | |||
} | |||
public static class BindGenericWrongTypeParameterField extends FormLayout { | |||
private CustomField<Boolean> firstName; | |||
} | |||
public static class BindWrongTypeParameterField extends FormLayout { | |||
private IntegerTextField firstName; | |||
} | |||
public static class BindGeneric<T> extends FormLayout { | |||
private CustomField<T> firstName; | |||
} | |||
public static class BindRaw extends FormLayout { | |||
private CustomField firstName; | |||
} | |||
public static class BindAbstract extends FormLayout { | |||
private AbstractTextField firstName; | |||
} | |||
public static class BindNonInstantiatableType extends FormLayout { | |||
private NoDefaultCtor firstName; | |||
} | |||
public static class BindComplextHierarchyGenericType extends FormLayout { | |||
private ComplexHierarchy firstName; | |||
} | |||
public static class NoDefaultCtor extends TextField { | |||
public NoDefaultCtor(int arg) { | |||
} | |||
} | |||
public static class IntegerTextField extends CustomField<Integer> { | |||
} | |||
public static class ComplexHierarchy extends Generic<Long> { | |||
} | |||
public static class Generic<T> extends ComplexGeneric<Boolean, String, T> { | |||
} | |||
public static class ComplexGeneric<U, V, S> extends CustomField<V> { | |||
} | |||
public static class CustomField<T> extends AbstractField<T> { | |||
private T value; | |||
@Override | |||
public T getValue() { | |||
return value; | |||
} | |||
@Override | |||
protected void doSetValue(T value) { | |||
this.value = value; | |||
} | |||
} | |||
@Test | |||
public void bindInstanceFields_bindAllFields() { | |||
BindAllFields form = new BindAllFields(); | |||
BeanBinder<Person> binder = new BeanBinder<>(Person.class); | |||
binder.bindInstanceFields(form); | |||
Person person = new Person(); | |||
person.setFirstName("foo"); | |||
person.setBirthDate(LocalDate.now()); | |||
binder.bind(person); | |||
Assert.assertEquals(person.getFirstName(), form.firstName.getValue()); | |||
Assert.assertEquals(person.getBirthDate(), form.birthDate.getValue()); | |||
form.firstName.setValue("bar"); | |||
form.birthDate.setValue(person.getBirthDate().plusDays(345)); | |||
Assert.assertEquals(form.firstName.getValue(), person.getFirstName()); | |||
Assert.assertEquals(form.birthDate.getValue(), person.getBirthDate()); | |||
} | |||
@Test | |||
public void bindInstanceFields_bindOnlyOneFields() { | |||
BindOnlyOneField form = new BindOnlyOneField(); | |||
BeanBinder<Person> binder = new BeanBinder<>(Person.class); | |||
binder.bindInstanceFields(form); | |||
Person person = new Person(); | |||
person.setFirstName("foo"); | |||
binder.bind(person); | |||
Assert.assertEquals(person.getFirstName(), form.firstName.getValue()); | |||
Assert.assertNull(form.noFieldInPerson); | |||
form.firstName.setValue("bar"); | |||
Assert.assertEquals(form.firstName.getValue(), person.getFirstName()); | |||
} | |||
@Test | |||
public void bindInstanceFields_bindNotHasValueField_fieldIsNull() { | |||
BindNoHasValueField form = new BindNoHasValueField(); | |||
BeanBinder<Person> binder = new BeanBinder<>(Person.class); | |||
binder.bindInstanceFields(form); | |||
Person person = new Person(); | |||
person.setFirstName("foo"); | |||
binder.bind(person); | |||
Assert.assertNull(form.firstName); | |||
} | |||
@Test | |||
public void bindInstanceFields_genericField() { | |||
BindGenericField form = new BindGenericField(); | |||
BeanBinder<Person> binder = new BeanBinder<>(Person.class); | |||
binder.bindInstanceFields(form); | |||
Person person = new Person(); | |||
person.setFirstName("foo"); | |||
binder.bind(person); | |||
Assert.assertEquals(person.getFirstName(), form.firstName.getValue()); | |||
form.firstName.setValue("bar"); | |||
Assert.assertEquals(form.firstName.getValue(), person.getFirstName()); | |||
} | |||
@Test(expected = IllegalStateException.class) | |||
public void bindInstanceFields_genericFieldWithWrongTypeParameter() { | |||
BindGenericWrongTypeParameterField form = new BindGenericWrongTypeParameterField(); | |||
BeanBinder<Person> binder = new BeanBinder<>(Person.class); | |||
binder.bindInstanceFields(form); | |||
} | |||
@Test(expected = IllegalStateException.class) | |||
public void bindInstanceFields_generic() { | |||
BindGeneric<String> form = new BindGeneric<>(); | |||
BeanBinder<Person> binder = new BeanBinder<>(Person.class); | |||
binder.bindInstanceFields(form); | |||
} | |||
@Test(expected = IllegalStateException.class) | |||
public void bindInstanceFields_rawFieldType() { | |||
BindRaw form = new BindRaw(); | |||
BeanBinder<Person> binder = new BeanBinder<>(Person.class); | |||
binder.bindInstanceFields(form); | |||
} | |||
@Test(expected = IllegalStateException.class) | |||
public void bindInstanceFields_abstractFieldType() { | |||
BindAbstract form = new BindAbstract(); | |||
BeanBinder<Person> binder = new BeanBinder<>(Person.class); | |||
binder.bindInstanceFields(form); | |||
} | |||
@Test(expected = IllegalStateException.class) | |||
public void bindInstanceFields_noInstantiatableFieldType() { | |||
BindNonInstantiatableType form = new BindNonInstantiatableType(); | |||
BeanBinder<Person> binder = new BeanBinder<>(Person.class); | |||
binder.bindInstanceFields(form); | |||
} | |||
@Test(expected = IllegalStateException.class) | |||
public void bindInstanceFields_wrongFieldType() { | |||
BindWrongTypeParameterField form = new BindWrongTypeParameterField(); | |||
BeanBinder<Person> binder = new BeanBinder<>(Person.class); | |||
binder.bindInstanceFields(form); | |||
} | |||
@Test | |||
public void bindInstanceFields_complexGenericHierarchy() { | |||
BindComplextHierarchyGenericType form = new BindComplextHierarchyGenericType(); | |||
BeanBinder<Person> binder = new BeanBinder<>(Person.class); | |||
binder.bindInstanceFields(form); | |||
Person person = new Person(); | |||
person.setFirstName("foo"); | |||
binder.bind(person); | |||
Assert.assertEquals(person.getFirstName(), form.firstName.getValue()); | |||
form.firstName.setValue("bar"); | |||
Assert.assertEquals(form.firstName.getValue(), person.getFirstName()); | |||
} | |||
@Test | |||
public void bindInstanceFields_bindNotHasValueField_fieldIsNotReplaced() { | |||
BindNoHasValueField form = new BindNoHasValueField(); | |||
BeanBinder<Person> binder = new BeanBinder<>(Person.class); | |||
String name = "foo"; | |||
form.firstName = name; | |||
binder.bindInstanceFields(form); | |||
Person person = new Person(); | |||
person.setFirstName("foo"); | |||
binder.bind(person); | |||
Assert.assertEquals(name, form.firstName); | |||
} | |||
@Test | |||
public void bindInstanceFields_bindAllFieldsUsingAnnotations() { | |||
BindFieldsUsingAnnotation form = new BindFieldsUsingAnnotation(); | |||
BeanBinder<Person> binder = new BeanBinder<>(Person.class); | |||
binder.bindInstanceFields(form); | |||
Person person = new Person(); | |||
person.setFirstName("foo"); | |||
person.setBirthDate(LocalDate.now()); | |||
binder.bind(person); | |||
Assert.assertEquals(person.getFirstName(), form.nameField.getValue()); | |||
Assert.assertEquals(person.getBirthDate(), | |||
form.birthDateField.getValue()); | |||
form.nameField.setValue("bar"); | |||
form.birthDateField.setValue(person.getBirthDate().plusDays(345)); | |||
Assert.assertEquals(form.nameField.getValue(), person.getFirstName()); | |||
Assert.assertEquals(form.birthDateField.getValue(), | |||
person.getBirthDate()); | |||
} | |||
@Test | |||
public void bindInstanceFields_bindNotBoundFieldsOnly_customBindingIsNotReplaced() { | |||
BindAllFields form = new BindAllFields(); | |||
BeanBinder<Person> binder = new BeanBinder<>(Person.class); | |||
TextField name = new TextField(); | |||
form.firstName = name; | |||
binder.forField(form.firstName) | |||
.withValidator( | |||
new StringLengthValidator("Name is invalid", 3, 10)) | |||
.bind("firstName"); | |||
binder.bindInstanceFields(form); | |||
Person person = new Person(); | |||
String personName = "foo"; | |||
person.setFirstName(personName); | |||
person.setBirthDate(LocalDate.now()); | |||
binder.bind(person); | |||
Assert.assertEquals(person.getFirstName(), form.firstName.getValue()); | |||
Assert.assertEquals(person.getBirthDate(), form.birthDate.getValue()); | |||
// the instance is not overridden | |||
Assert.assertEquals(name, form.firstName); | |||
form.firstName.setValue("aa"); | |||
form.birthDate.setValue(person.getBirthDate().plusDays(345)); | |||
Assert.assertEquals(personName, person.getFirstName()); | |||
Assert.assertEquals(form.birthDate.getValue(), person.getBirthDate()); | |||
Assert.assertFalse(binder.validate().isOk()); | |||
} | |||
@Test | |||
public void bindInstanceFields_fieldsAreConfigured_customBindingIsNotReplaced() { | |||
BindOnlyOneField form = new BindOnlyOneField(); | |||
BeanBinder<Person> binder = new BeanBinder<>(Person.class); | |||
TextField name = new TextField(); | |||
form.firstName = name; | |||
binder.forField(form.firstName) | |||
.withValidator( | |||
new StringLengthValidator("Name is invalid", 3, 10)) | |||
.bind("firstName"); | |||
TextField ageField = new TextField(); | |||
form.noFieldInPerson = ageField; | |||
binder.forField(form.noFieldInPerson) | |||
.withConverter(new StringToIntegerConverter("")) | |||
.bind(Person::getAge, Person::setAge); | |||
binder.bindInstanceFields(form); | |||
Person person = new Person(); | |||
String personName = "foo"; | |||
int age = 11; | |||
person.setFirstName(personName); | |||
person.setAge(age); | |||
binder.bind(person); | |||
Assert.assertEquals(person.getFirstName(), form.firstName.getValue()); | |||
Assert.assertEquals(String.valueOf(person.getAge()), | |||
form.noFieldInPerson.getValue()); | |||
// the instances are not overridden | |||
Assert.assertEquals(name, form.firstName); | |||
Assert.assertEquals(ageField, form.noFieldInPerson); | |||
form.firstName.setValue("aa"); | |||
age = age + 56; | |||
form.noFieldInPerson.setValue(String.valueOf(age)); | |||
Assert.assertEquals(personName, person.getFirstName()); | |||
Assert.assertEquals(form.noFieldInPerson.getValue(), | |||
String.valueOf(person.getAge())); | |||
Assert.assertFalse(binder.validate().isOk()); | |||
} | |||
} |
@@ -1,7 +1,7 @@ | |||
package com.vaadin.tests.data.bean; | |||
import java.math.BigDecimal; | |||
import java.util.Date; | |||
import java.time.LocalDate; | |||
public class Person { | |||
private String firstName; | |||
@@ -11,7 +11,7 @@ public class Person { | |||
private Sex sex; | |||
private Address address; | |||
private boolean deceased; | |||
private Date birthDate; | |||
private LocalDate birthDate; | |||
private Integer salary; // null if unknown | |||
private Double salaryDouble; // null if unknown | |||
@@ -122,11 +122,11 @@ public class Person { | |||
this.salaryDouble = salaryDouble; | |||
} | |||
public Date getBirthDate() { | |||
public LocalDate getBirthDate() { | |||
return birthDate; | |||
} | |||
public void setBirthDate(Date birthDate) { | |||
public void setBirthDate(LocalDate birthDate) { | |||
this.birthDate = birthDate; | |||
} | |||
@@ -16,11 +16,11 @@ | |||
package com.vaadin.tests.declarative; | |||
import com.vaadin.annotations.DesignRoot; | |||
import com.vaadin.annotations.PropertyId; | |||
import com.vaadin.ui.Button; | |||
import com.vaadin.ui.DateField; | |||
import com.vaadin.ui.VerticalLayout; | |||
import com.vaadin.ui.declarative.Design; | |||
import com.vaadin.v7.data.fieldgroup.PropertyId; | |||
import com.vaadin.v7.ui.ComboBox; | |||
import com.vaadin.v7.ui.TextField; | |||
@@ -18,6 +18,7 @@ package com.vaadin.tests.fieldgroup; | |||
import java.util.Iterator; | |||
import java.util.Map; | |||
import com.vaadin.annotations.PropertyId; | |||
import com.vaadin.server.VaadinRequest; | |||
import com.vaadin.shared.ui.label.ContentMode; | |||
import com.vaadin.shared.util.SharedUtil; | |||
@@ -39,7 +40,6 @@ import com.vaadin.v7.data.Property.ValueChangeListener; | |||
import com.vaadin.v7.data.Validator.InvalidValueException; | |||
import com.vaadin.v7.data.fieldgroup.BeanFieldGroup; | |||
import com.vaadin.v7.data.fieldgroup.FieldGroup.CommitException; | |||
import com.vaadin.v7.data.fieldgroup.PropertyId; | |||
import com.vaadin.v7.data.util.BeanItem; | |||
import com.vaadin.v7.data.util.BeanItemContainer; | |||
import com.vaadin.v7.data.validator.IntegerRangeValidator; |
@@ -3,6 +3,7 @@ package com.vaadin.tests.fieldgroup; | |||
import java.util.Date; | |||
import java.util.Locale; | |||
import com.vaadin.annotations.PropertyId; | |||
import com.vaadin.server.VaadinRequest; | |||
import com.vaadin.tests.components.AbstractTestUIWithLog; | |||
import com.vaadin.tests.data.bean.Person; | |||
@@ -12,7 +13,6 @@ import com.vaadin.ui.Notification; | |||
import com.vaadin.v7.data.fieldgroup.BeanFieldGroup; | |||
import com.vaadin.v7.data.fieldgroup.FieldGroup; | |||
import com.vaadin.v7.data.fieldgroup.FieldGroup.CommitException; | |||
import com.vaadin.v7.data.fieldgroup.PropertyId; | |||
import com.vaadin.v7.data.util.BeanItem; | |||
import com.vaadin.v7.ui.DateField; | |||
import com.vaadin.v7.ui.InlineDateField; |
@@ -1,5 +1,6 @@ | |||
package com.vaadin.tests.fieldgroup; | |||
import com.vaadin.annotations.PropertyId; | |||
import com.vaadin.tests.components.TestBase; | |||
import com.vaadin.tests.data.bean.Address; | |||
import com.vaadin.tests.data.bean.Country; | |||
@@ -7,7 +8,6 @@ import com.vaadin.tests.data.bean.Person; | |||
import com.vaadin.tests.data.bean.Sex; | |||
import com.vaadin.v7.data.fieldgroup.BeanFieldGroup; | |||
import com.vaadin.v7.data.fieldgroup.FieldGroup; | |||
import com.vaadin.v7.data.fieldgroup.PropertyId; | |||
import com.vaadin.v7.data.util.BeanItem; | |||
import com.vaadin.v7.ui.TextField; | |||
@@ -1,5 +1,6 @@ | |||
package com.vaadin.tests.fieldgroup; | |||
import com.vaadin.annotations.PropertyId; | |||
import com.vaadin.tests.data.bean.Address; | |||
import com.vaadin.tests.data.bean.Country; | |||
import com.vaadin.tests.data.bean.Person; | |||
@@ -7,7 +8,6 @@ import com.vaadin.tests.data.bean.Sex; | |||
import com.vaadin.tests.util.Log; | |||
import com.vaadin.ui.CheckBox; | |||
import com.vaadin.v7.data.fieldgroup.BeanFieldGroup; | |||
import com.vaadin.v7.data.fieldgroup.PropertyId; | |||
import com.vaadin.v7.ui.NativeSelect; | |||
import com.vaadin.v7.ui.TextField; | |||
@@ -1,10 +1,10 @@ | |||
package com.vaadin.tests.minitutorials.v7a1; | |||
import com.vaadin.annotations.PropertyId; | |||
import com.vaadin.server.VaadinRequest; | |||
import com.vaadin.tests.components.AbstractReindeerTestUI; | |||
import com.vaadin.ui.GridLayout; | |||
import com.vaadin.v7.data.fieldgroup.FieldGroup; | |||
import com.vaadin.v7.data.fieldgroup.PropertyId; | |||
import com.vaadin.v7.data.util.BeanItem; | |||
import com.vaadin.v7.ui.TextArea; | |||
import com.vaadin.v7.ui.TextField; |