) Binding.super.withConverter(
toModel, toPresentation, errorMessage);
}
/**
* Completes this binding by connecting the field to the property with
* the given name. The getter and setter methods of the property are
* looked up with bean introspection and used to read and write the
* property value.
*
* If a JSR-303 bean validation implementation is present on the
* classpath, adds a {@link BeanValidator} to this binding.
*
* The property must have an accessible getter method. It need not have
* an accessible setter; in that case the property value is never
* updated and the binding is said to be read-only.
*
* @param propertyName
* the name of the property to bind, not null
*
* @throws IllegalArgumentException
* if the property name is invalid
* @throws IllegalArgumentException
* if the property has no accessible getter
*
* @see Binding#bind(Function, java.util.function.BiConsumer)
*/
public void bind(String propertyName);
}
/**
* An internal implementation of {@link BeanBinding}.
*
* @param
* the bean type
* @param
* the field value type
* @param
* the target property type
*/
protected static class BeanBindingImpl
extends BindingImpl
implements BeanBinding {
private Method getter;
private Method setter;
/**
* Creates a new bean binding.
*
* @param binder
* the binder this instance is connected to, not null
* @param field
* the field to use, not null
* @param converter
* the initial converter to use, not null
* @param statusHandler
* the handler to notify of status changes, not null
*/
protected BeanBindingImpl(BeanBinder binder,
HasValue field,
Converter converter,
ValidationStatusHandler statusHandler) {
super(binder, field, converter, statusHandler);
}
@Override
public BeanBinding withValidator(
Validator super TARGET> validator) {
return (BeanBinding) super.withValidator(
validator);
}
@Override
public BeanBinding withConverter(
Converter converter) {
return (BeanBinding) super.withConverter(
converter);
}
@Override
public void bind(String propertyName) {
checkUnbound();
Binding finalBinding;
finalBinding = withConverter(createConverter());
if (BeanValidator.checkBeanValidationAvailable()) {
finalBinding = finalBinding.withValidator(new BeanValidator(
getBinder().beanType, propertyName, findLocale()));
}
PropertyDescriptor descriptor = getDescriptor(propertyName);
getter = descriptor.getReadMethod();
setter = descriptor.getWriteMethod();
finalBinding.bind(this::getValue, this::setValue);
}
@Override
protected BeanBinder getBinder() {
return (BeanBinder) super.getBinder();
}
private void setValue(BEAN bean, Object value) {
try {
if (setter != null) {
setter.invoke(bean, value);
}
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
private Object getValue(BEAN bean) {
try {
return getter.invoke(bean);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
private PropertyDescriptor getDescriptor(String propertyName) {
final Class> beanType = getBinder().beanType;
PropertyDescriptor descriptor = null;
try {
descriptor = BeanUtil.getPropertyDescriptor(beanType,
propertyName);
} catch (IntrospectionException ie) {
throw new IllegalArgumentException(
"Could not resolve bean property name (see the cause): "
+ beanType.getName() + "." + propertyName,
ie);
}
if (descriptor == null) {
throw new IllegalArgumentException(
"Could not resolve bean property name (please check spelling and getter visibility): "
+ beanType.getName() + "." + propertyName);
}
if (descriptor.getReadMethod() == null) {
throw new IllegalArgumentException(
"Bean property has no accessible getter: "
+ beanType.getName() + "." + propertyName);
}
return descriptor;
}
@SuppressWarnings("unchecked")
private Converter createConverter() {
return Converter.from(
fieldValue -> getter.getReturnType().cast(fieldValue),
propertyValue -> (TARGET) propertyValue, exception -> {
throw new RuntimeException(exception);
});
}
}
private final Class extends BEAN> beanType;
/**
* Creates a new {@code BeanBinder} supporting beans of the given type.
*
* @param beanType
* the bean {@code Class} instance, not null
*/
public BeanBinder(Class extends BEAN> beanType) {
BeanValidator.checkBeanValidationAvailable();
this.beanType = beanType;
}
@Override
public BeanBinding forField(
HasValue field) {
return createBinding(field, Converter.identity(),
this::handleValidationStatus);
}
@Override
public BeanBinding forSelect(
AbstractSingleSelect select) {
return (BeanBinding) super.forSelect(
select);
}
@Override
public BeanBinding, Set> forSelect(
AbstractMultiSelect select) {
return (BeanBinding, Set>) super.forSelect(
select);
}
/**
* Binds the given field to the property with the given name. The getter and
* setter methods of the property are looked up with bean introspection and
* used to read and write the property value.
*
* Use the {@link #forField(HasValue)} overload instead if you want to
* further configure the new binding.
*
* The property must have an accessible getter method. It need not have an
* accessible setter; in that case the property value is never updated and
* the binding is said to be read-only.
*
* @param
* the value type of the field to bind
* @param field
* the field to bind, not null
* @param propertyName
* the name of the property to bind, not null
*
* @throws IllegalArgumentException
* if the property name is invalid
* @throws IllegalArgumentException
* if the property has no accessible getter
*
* @see #bind(HasValue, java.util.function.Function,
* java.util.function.BiConsumer)
*/
public void bind(HasValue field,
String propertyName) {
forField(field).bind(propertyName);
}
@Override
public BeanBinder withValidator(Validator super BEAN> validator) {
return (BeanBinder) super.withValidator(validator);
}
@Override
protected BeanBindingImpl createBinding(
HasValue field, Converter converter,
ValidationStatusHandler handler) {
Objects.requireNonNull(field, "field cannot be null");
Objects.requireNonNull(converter, "converter cannot be null");
return new BeanBindingImpl<>(this, field, converter, handler);
}
}