@@ -99,8 +99,8 @@ import com.vaadin.util.ReflectTools; | |||
*/ | |||
public class Binder<BEAN> implements Serializable { | |||
// TODO get default factory from session / ui like in V7 ? | |||
private BindingConverterFactory defaultConverter; | |||
private final DefaultConverterFactory defaultConverterFactory = new DefaultConverterFactory(); | |||
private ConverterFactory customConverterFactory; | |||
/** | |||
* Represents the binding between a field and a data property. | |||
@@ -2639,28 +2639,26 @@ public class Binder<BEAN> implements Serializable { | |||
memberField.getName(), | |||
objectWithMemberFields.getClass().getName())); | |||
} | |||
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 false; | |||
} | |||
if (field == null) { | |||
field = makeFieldInstance( | |||
(Class<? extends HasValue<?>>) memberField.getType()); | |||
initializeField(objectWithMemberFields, memberField, field); | |||
} | |||
BindingBuilder builder = forField(field); | |||
Class<?> fieldClass = GenericTypeReflector.erase(valueType); | |||
if (propertyType.equals(fieldClass) || (defaultConverter != null | |||
&& defaultConverter.isSupported(fieldClass, propertyType))) { | |||
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 false; | |||
} | |||
if (field == null) { | |||
field = makeFieldInstance( | |||
(Class<? extends HasValue<?>>) memberField.getType()); | |||
initializeField(objectWithMemberFields, memberField, field); | |||
} | |||
BindingBuilder builder = forField(field); | |||
if (defaultConverter != null) | |||
builder = defaultConverter.buildBindingConverter(builder, fieldClass, | |||
propertyType); | |||
if (propertyType.equals(fieldClass) | |||
|| applyConverterFactory(builder, propertyType, fieldClass)) { | |||
builder.bind(property); | |||
return true; | |||
} else { | |||
@@ -2672,27 +2670,13 @@ public class Binder<BEAN> implements Serializable { | |||
} | |||
} | |||
/** | |||
* @return the current default binding converter | |||
*/ | |||
public BindingConverterFactory getDefaultConverter() { | |||
return defaultConverter; | |||
} | |||
/** | |||
* Set at default binding converter. If this is set, it is used when | |||
* {@link #bindInstanceFields(Object)} is called. The default converter is | |||
* able to convert a set of input classes to a given range of output | |||
* classes. This is typically used for automatically binding Integer, | |||
* Double, Long or Float to a TextField (which use String as a return | |||
* value). It may also be used to convert {@link java.time.LocalDateTime} | |||
* (or LocalDate) to the old {@link java.util.Date} | |||
* | |||
* @param defaultConverter | |||
* an interface for converting values | |||
*/ | |||
public void setDefaultConverter(DefaultBindingConverterFactory defaultConverter) { | |||
this.defaultConverter = defaultConverter; | |||
private boolean applyConverterFactory(BindingBuilder bindingBuilder, | |||
Class<?> modelType, Class<?> presentationType) { | |||
return customConverterFactory != null | |||
&& customConverterFactory.applyConverter(bindingBuilder, | |||
presentationType, modelType) | |||
|| getDefaultConverterFactory().applyConverter(bindingBuilder, | |||
presentationType, modelType); | |||
} | |||
/** | |||
@@ -2923,6 +2907,37 @@ public class Binder<BEAN> implements Serializable { | |||
.ifPresent(Binding::unbind); | |||
} | |||
/** | |||
* TODO | |||
* | |||
* @return | |||
* @since | |||
*/ | |||
public ConverterFactory getCustomConverterFactory() { | |||
return customConverterFactory; | |||
} | |||
/** | |||
* TODO | |||
* | |||
* @param customConverterFactory | |||
* @since | |||
*/ | |||
public void setCustomConverterFactory( | |||
ConverterFactory customConverterFactory) { | |||
this.customConverterFactory = customConverterFactory; | |||
} | |||
/** | |||
* TODO | |||
* | |||
* @return | |||
* @since | |||
*/ | |||
public DefaultConverterFactory getDefaultConverterFactory() { | |||
return defaultConverterFactory; | |||
} | |||
private static final Logger getLogger() { | |||
return Logger.getLogger(Binder.class.getName()); | |||
} |
@@ -23,12 +23,20 @@ import java.io.Serializable; | |||
* Binder when creating bindings with {@link Binder#bindInstanceFields(Object)}. | |||
* <p> | |||
* The framework default implementation is | |||
* {@link DefaultBindingConverterFactory}. | |||
* {@link DefaultConverterFactory}. | |||
* | |||
* @author Vaadin Ltd. | |||
* @since | |||
*/ | |||
public interface BindingConverterFactory extends Serializable { | |||
public interface ConverterFactory extends Serializable { | |||
<PRESENTATION, MODEL> boolean applyConverter( | |||
Binder.BindingBuilder<MODEL, PRESENTATION> bindingBuilder, | |||
Class<PRESENTATION> presentationType, Class<MODEL> modelType); | |||
// <MODEL, PRESENTATION> Optional<Consumer<Binder.BindingBuilder<MODEL, | |||
// PRESENTATION>>> getConverterApplier( | |||
// Class<PRESENTATION> presentationType, Class<MODEL> modelType); | |||
/** | |||
* Builds converter to the given binding to convert between the presentation | |||
@@ -43,8 +51,9 @@ public interface BindingConverterFactory extends Serializable { | |||
* the bean property type | |||
* @return the binder builder with converter applied if possible | |||
*/ | |||
Binder.BindingBuilder buildBindingConverter(Binder.BindingBuilder builder, | |||
Class<?> presentationType, Class<?> modelType); | |||
// Binder.BindingBuilder buildBindingConverter(Binder.BindingBuilder | |||
// builder, | |||
// Class<?> presentationType, Class<?> modelType); | |||
/** | |||
* Returns whether this factory has a converter to convert between the | |||
@@ -57,6 +66,6 @@ public interface BindingConverterFactory extends Serializable { | |||
* class of the property type in the entity | |||
* @return {@code true} if conversion possible, {@code false} if not | |||
*/ | |||
boolean isSupported(Class<?> presentationType, Class<?> modelType); | |||
// boolean isSupported(Class<?> presentationType, Class<?> modelType); | |||
} |
@@ -45,7 +45,7 @@ import com.vaadin.server.SerializableFunction; | |||
/** | |||
* TODO | |||
*/ | |||
public class DefaultBindingConverterFactory implements BindingConverterFactory { | |||
public class DefaultConverterFactory implements ConverterFactory { | |||
private static final Map<Class<?>, Set<Class<?>>> SUPPORTED_PRESENTATION_TO_MODEL_CONVERTERS_MAP = new HashMap<>(); | |||
static { | |||
@@ -60,6 +60,8 @@ public class DefaultBindingConverterFactory implements BindingConverterFactory { | |||
new HashSet<>(Collections.singletonList(Date.class))); | |||
} | |||
// TODO: provide setters for customizing default behavior | |||
private static final SerializableFunction<Class, ErrorMessageProvider> TYPE_BASED_ERROR_MESSAGE_PROVIDER = clazz -> context -> String | |||
.format("Invalid value, expected ${0}", clazz.getSimpleName()); | |||
@@ -68,34 +70,36 @@ public class DefaultBindingConverterFactory implements BindingConverterFactory { | |||
} | |||
@Override | |||
public Binder.BindingBuilder buildBindingConverter( | |||
Binder.BindingBuilder builder, Class<?> presentationType, | |||
Class<?> modelType) { | |||
public <PRESENTATION, MODEL> boolean applyConverter( | |||
Binder.BindingBuilder<MODEL, PRESENTATION> builder, | |||
Class<PRESENTATION> presentationType, Class<MODEL> modelType) { | |||
if (isSupported(presentationType, modelType)) { | |||
Supplier<?> nullRepresentationProvider = getNullRepresentationProvider( | |||
Supplier<PRESENTATION> nullRepresentationProvider = getNullRepresentationProvider( | |||
presentationType, modelType); | |||
if (nullRepresentationProvider != null) { | |||
builder = builder.withNullRepresentation( | |||
builder.withNullRepresentation( | |||
nullRepresentationProvider.get()); | |||
} | |||
if (presentationType == String.class) { | |||
builder = builder | |||
((Binder.BindingBuilder<MODEL, String>)builder) | |||
.withConverter(createStringConverter(modelType)); | |||
} else if (presentationType == LocalDateTime.class) { | |||
if (modelType == Date.class) { | |||
builder = builder | |||
((Binder.BindingBuilder<Date, LocalDateTime>)builder) | |||
.withConverter(new LocalDateTimeToDateConverter( | |||
getZoneIdForDateConverters())); | |||
} | |||
} else if (presentationType == LocalDate.class) { | |||
if (modelType == Date.class) { | |||
builder = builder | |||
((Binder.BindingBuilder<Date, LocalDate>)builder) | |||
.withConverter(new LocalDateToDateConverter( | |||
getZoneIdForDateConverters())); | |||
} | |||
} | |||
return true; | |||
} else { | |||
return false; | |||
} | |||
return builder; | |||
} | |||
/** | |||
@@ -147,10 +151,10 @@ public class DefaultBindingConverterFactory implements BindingConverterFactory { | |||
* @return the null representation provider, or {@code null} to prevent | |||
* conversion of {@code null} model value | |||
*/ | |||
protected Supplier<?> getNullRepresentationProvider( | |||
Class<?> presentationType, Class<?> modelType) { | |||
protected <PRESENTATION> Supplier<PRESENTATION> getNullRepresentationProvider( | |||
Class<PRESENTATION> presentationType, Class<?> modelType) { | |||
if (presentationType.equals(String.class)) { | |||
return () -> ""; | |||
return () -> (PRESENTATION) ""; | |||
} | |||
return null; | |||
} | |||
@@ -172,8 +176,7 @@ public class DefaultBindingConverterFactory implements BindingConverterFactory { | |||
return ZoneId.systemDefault(); | |||
} | |||
@Override | |||
public boolean isSupported(Class<?> presentationType, Class<?> modelType) { | |||
private boolean isSupported(Class<?> presentationType, Class<?> modelType) { | |||
Set<Class<?>> supported = SUPPORTED_PRESENTATION_TO_MODEL_CONVERTERS_MAP | |||
.get(presentationType); | |||
return supported != null && supported.size() > 0 |