Vaadin: Bind Enum values to ComboBox (ConversionException)

I want to bind enum values from my Model to a ComboBox. What I tried:

Model:

public class Model{
public enum Status {
        Neu, Analyse, Pruefung, Freigabe, OnTheRoad, EOL, CANCELLED;
        public String getCaption() {
            return name();
        }

        public int getId() {
            return ordinal();
        }
    }

    private Status status;
}

Form:

public class MyForm extends CustomComponent{

private final BeanFieldGroup<Model> binder;

@PropertyId("status")
 private final ComboBox cStatus = new ComboBox("Status");

public MyForm (Model model) {
        final HorizontalLayout content = new HorizontalLayout();
        content.addComponent(buildContent());

        // Now use a binder to bind the members
        binder = new BeanFieldGroup<>(Model.class);

        // We need an item data source before we create the fields to be able to
        // find the properties, otherwise we have to specify them by hand
        final BeanItem bean = new BeanItem<>(model);
        binder.setItemDataSource(bean);
        binder.buildAndBindMemberFields(this);

        setCompositionRoot(content);
}

private buildContent(){
        final FormLayout basicContent = new FormLayout();

        final BeanContainer<Integer, Status> container = new BeanContainer<>(Status.class);
        container.setBeanIdProperty("id");
        container.addAll(EnumSet.allOf(Status.class));
        cStatus.setContainerDataSource(container);
        cStatus.setItemCaptionPropertyId("caption");
        basicContent.addComponent(cStatus);

        return basicContent;
}

public void commit() throws CommitException {
        binder.commit(); // This is what causes the error
}
}

When i call commit() I'm getting this error:

Caused by: com.vaadin.data.util.converter.Converter$ConversionException: Unable to convert value of type java.lang.Integer to model type class com.xx.test.entities.def.Model$Status. No converter is set and the types are not compatible.
    at com.vaadin.data.util.converter.ConverterUtil.convertToModel(ConverterUtil.java:181) ~[vaadin-server-7.5.6.jar:7.5.6]
    at com.vaadin.ui.AbstractField.convertToModel(AbstractField.java:751) ~[vaadin-server-7.5.6.jar:7.5.6]
    at com.vaadin.ui.AbstractField.convertToModel(AbstractField.java:731) ~[vaadin-server-7.5.6.jar:7.5.6]
    at com.vaadin.ui.AbstractField.getConvertedValue(AbstractField.java:817) ~[vaadin-server-7.5.6.jar:7.5.6]
    at com.vaadin.ui.AbstractField.commit(AbstractField.java:253) ~[vaadin-server-7.5.6.jar:7.5.6]
    at com.vaadin.data.fieldgroup.FieldGroup.commitFields(FieldGroup.java:509) ~[vaadin-server-7.5.6.jar:7.5.6]
    at com.vaadin.data.fieldgroup.FieldGroup.commit(FieldGroup.java:481) ~[vaadin-server-7.5.6.jar:7.5.6]
    ... 49 more

Is there any way to bind the Enum of my Model to the ComboBox ?

Thanks for any help!

UPDATE (André Schild answer):

I want to display the String representation of the Enum values, so I think I need to Use String instead of Integer? I figured out that there actually is a build in "StringToEnumConverter". However, I tried it with the StringToEnumConverter and my own "StringToStatusConverter":

cStatus.setConverter((Converter) new StringToEnumConverter());
cStatus.addItems(EnumSet.allOf(Status.class));`
// cStatus.addItem(Status.CANCELLED); // Did not work
// cStatus.setConvertedValue(Status.EOL); // Did not work

Now I'm getting:

java.lang.ClassCastException: xx.xxx.xxx.entities.modeldefdef.Model$Status cannot be cast to java.lang.String
    at com.vaadin.data.util.converter.StringToEnumConverter.convertToModel(StringToEnumConverter.java:32) ~[vaadin-server-7.5.6.jar:7.5.6]

... for both Converters.


Solution 1:

In your buildContent() method write:

// ...
final BeanItemContainer<Status> container = new BeanItemContainer<>(Status.class);
container.addAll(EnumSet.allOf(Status.class));
cStatus.setContainerDataSource(container);
cStatus.setItemCaptionPropertyId("caption");
basicContent.addComponent(cStatus);
// ...

Why? Your ComboBox is bound to a Property of type Status by your FieldGroup. But your current Container BeanContainer has the type Integer, that is the type of the container's IDs. To fix that the container's ID types have to match the property's type. This is done by using the BeanItemContainer, which uses the Bean type as object ID. Here the problem is explained in more detail.

Solution 2:

Vaadin has the concept of Converter which transform from a internal representation to a external representation.

You will need to implement the Converter interface, and then specify to use it.

public class IntegerToModelStatusConverter implements Converter<Integer, Model$Status>

and

final ComboBox statusField = new ComboBox("Status");
statusField .setConverter(new IntegerToModelStatusConverter());

The integer is the int value of the corresponding enum element.

There is a wiki entry describing this in more details.