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;
+++ /dev/null
-/*
- * 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.v7.data.fieldgroup;
-
-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;
-
-/**
- * Defines the custom property name to be bound to a {@link Field} using
- * {@link FieldGroup} or {@link BeanFieldGroup}.
- * <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.
- * <p>
- * In following usage example, the text field would be bound to property "foo"
- * in the Entity class. <code>
- * <pre>
- * class Editor extends FormLayout {
- @PropertyId("foo")
- TextField myField = new TextField();
- }
-
- class Entity {
- String foo;
- }
-
- {
- Editor e = new Editor();
- BeanFieldGroup.bindFieldsUnbuffered(new Entity(), e);
- }
- </pre>
- * </code>
- *
- * @since 7.0
- * @author Vaadin Ltd
- */
-@Target({ ElementType.FIELD })
-@Retention(RetentionPolicy.RUNTIME)
-@Deprecated
-public @interface PropertyId {
- String value();
-}
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 {
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;
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;
<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 -->
--- /dev/null
+/*
+ * 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.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+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 Binder} or {@link BeanBinder}.
+ * <p>
+ * 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>
+ * <pre>
+ * class Editor extends FormLayout {
+ @PropertyId("foo")
+ TextField myField = new TextField();
+ }
+
+ class Entity {
+ String foo;
+ }
+
+ {
+ Editor editor = new Editor();
+ BeanBinder<Entity> binder = new BeanBinder(Entity.class);
+ binder.bindInstanceFields(editor);
+ }
+ </pre>
+ * </code>
+ *
+ * @since 7.0
+ * @author Vaadin Ltd
+ */
+@Target({ ElementType.FIELD })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface PropertyId {
+ String value();
+}
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;
getter = descriptor.getReadMethod();
setter = descriptor.getWriteMethod();
finalBinding.bind(this::getValue, this::setValue);
+ getBinder().boundProperties.add(propertyName);
}
@Override
}
private final Class<? extends BEAN> beanType;
+ private final Set<String> boundProperties;
/**
* Creates a new {@code BeanBinder} supporting beans of the given type.
public BeanBinder(Class<? extends BEAN> beanType) {
BeanUtil.checkBeanValidationAvailable();
this.beanType = beanType;
+ boundProperties = new HashSet<>();
}
@Override
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("_", "");
+ }
+
}
* 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);
--- /dev/null
+/*
+ * 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());
+ }
+}
package com.vaadin.tests.data.bean;
import java.math.BigDecimal;
-import java.util.Date;
+import java.time.LocalDate;
public class Person {
private String firstName;
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
this.salaryDouble = salaryDouble;
}
- public Date getBirthDate() {
+ public LocalDate getBirthDate() {
return birthDate;
}
- public void setBirthDate(Date birthDate) {
+ public void setBirthDate(LocalDate birthDate) {
this.birthDate = birthDate;
}
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;