zond
zond

Reputation: 1480

vaadin8 DateField: Cannot cast java.time.LocalDate to java.util.Date

I have simple entity (for example)

 import java.util.Date;
 class People implements Serializable{
   ...
   private Date birthdate; //(getters, setters)
   ...}

UI code:

 final Binder<People> binder = new Binder<People>(People.class);  ...
 People bean=new  People();
 binder.setBean(bean);
 DateField birthdate = new DateField("date of birth");
 binder.bind(birthdate, "birthdate");

When I select date from calendar in UI I get:

 Caused by: java.lang.ClassCastException: Cannot cast java.time.LocalDate to java.util.Date
at java.lang.Class.cast(Class.java:3369)
at com.vaadin.data.Binder$BindingBuilderImpl.lambda$createConverter$f6099586$1(Binder.java:800)
at com.vaadin.data.Converter.lambda$null$fdd4de71$1(Converter.java:105)
at com.vaadin.data.Result.of(Result.java:91)

I tried to use

 DateField birthdate = new DateField("birthdate");
 binder.bind(birthdate, "birthdate");
 binder.forField(birthdate).withConverter(new LocalDateToDateConverter());

but have same result. How to bind Date to DateField properly?

Upvotes: 3

Views: 1666

Answers (2)

Andr&#225;s
Andr&#225;s

Reputation: 26

If you use the conversion frequently you can extend BeanValidationBinder and "add" a custom Converter to DefaultConverterFactory, and use the extended MyBeanValidationBinder instead of the BeanValidationBinder (the example uses java.time.Instant instead of java.util.Date):

import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneOffset;
import java.util.Optional;

import com.vaadin.flow.data.binder.BeanValidationBinder;
import com.vaadin.flow.data.binder.Result;
import com.vaadin.flow.data.binder.ValueContext;
import com.vaadin.flow.data.converter.Converter;
import com.vaadin.flow.data.converter.ConverterFactory;
import com.vaadin.flow.data.converter.DefaultConverterFactory;

public class MyBeanValidationBinder<BEAN> extends BeanValidationBinder<BEAN> {
    private static final long serialVersionUID = 1L;

    public static class MyConverterFactory implements ConverterFactory {
        private static final long serialVersionUID = 1L;

        @Override
        public <P, M> Optional<Converter<P, M>> newInstance(Class<P> presentationType, Class<M> modelType) {
            Optional<Converter<P, M>> defaultConverter = DefaultConverterFactory.INSTANCE.newInstance(presentationType, modelType);
            if (defaultConverter.isEmpty() && presentationType.isAssignableFrom(LocalDate.class)  && modelType.isAssignableFrom(Instant.class) ) {
                return Optional.of(new Converter<P, M>() {
                    private static final long serialVersionUID = 1L;

                    @SuppressWarnings("unchecked")
                    @Override
                    public Result<M> convertToModel(P value, ValueContext context) {
                        if (value == null) {
                            return Result.ok(null);
                        }
                        LocalDate valueLocalDate = (LocalDate) value;
                        return Result.ok((M) valueLocalDate.atStartOfDay().toInstant(ZoneOffset.ofHours(0)));
                    }

                    @SuppressWarnings("unchecked")
                    @Override
                    public P convertToPresentation(M value, ValueContext context) {
                        if (value == null) {
                            return null;
                        }
                        Instant valueLocalInstant = (Instant) value;
                        return (P) LocalDate.ofInstant(valueLocalInstant, ZoneOffset.ofHours(0));
                    }});
            }
            return defaultConverter;
        }
        
    }
    
    MyConverterFactory myConverterFactory;
    
    public MyBeanValidationBinder(Class<BEAN> beanType) {
        super(beanType);
        myConverterFactory = new MyConverterFactory();
    }

    @Override
    protected ConverterFactory getConverterFactory() {
        return this.myConverterFactory;
    }
}

Creating the Binder:

final Binder<People> binder = new MyBeanValidationBinder<People>(People.class);

Upvotes: 0

Steffen Harbich
Steffen Harbich

Reputation: 2749

The problem is how you make use of the binder. Instead try

DateField birthdate = new DateField("birthdate");
binder.forField(birthdate).withConverter(new LocalDateToDateConverter()).bind("birthdate");

The forField method returns an object following the builder design pattern. That means you call some (chained) methods on that object and finish it by a call to bind.

Upvotes: 3

Related Questions