BeanItem with enum property / ComboBox

Hi,

I want to use a BeanItem with an enum property on a Form with a ComboBox for that enum property. I use setItemDataSource(beanItem) for setting the bean for that form.
For building this Form from the beans properties I extended the DefaultFieldFactory.
My question is, how can bind the selected ComboBox-Value to the BeanItem-Property when the form is committed (form.commit()).

I allways get the error:

Exception

com.vaadin.data.Property$ConversionException: java.lang.NoSuchMethodException: de.saarland.lzd.mis.business.Informationsart.(java.lang.String)
	at com.vaadin.data.util.MethodProperty.setValue(MethodProperty.java:716)
	at com.vaadin.ui.AbstractField.commit(AbstractField.java:242)
	at com.vaadin.ui.Form.commit(Form.java:336)
	at de.saarland.lzd.mis.ui.informationsempfaenger.InformationsempfaengerForm$2.buttonClick(InformationsempfaengerForm.java:59)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at com.vaadin.event.ListenerMethod.receiveEvent(ListenerMethod.java:487)
	at com.vaadin.event.EventRouter.fireEvent(EventRouter.java:161)
	at com.vaadin.ui.AbstractComponent.fireEvent(AbstractComponent.java:1154)
	at com.vaadin.ui.Button.fireClick(Button.java:371)
	at com.vaadin.ui.Button.changeVariables(Button.java:193)
	at com.vaadin.terminal.gwt.server.AbstractCommunicationManager.handleVariables(AbstractCommunicationManager.java:1094)
	at com.vaadin.terminal.gwt.server.AbstractCommunicationManager.doHandleUidlRequest(AbstractCommunicationManager.java:590)
	at com.vaadin.terminal.gwt.server.CommunicationManager.handleUidlRequest(CommunicationManager.java:266)
	at com.vaadin.terminal.gwt.server.AbstractApplicationServlet.service(AbstractApplicationServlet.java:476)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298)
	at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:857)
	at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:588)
	at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
	at java.lang.Thread.run(Thread.java:619)
Caused by: java.lang.NoSuchMethodException: de.saarland.lzd.mis.business.Informationsart.(java.lang.String)
	at java.lang.Class.getConstructor0(Class.java:2706)
	at java.lang.Class.getConstructor(Class.java:1657)
	at com.vaadin.data.util.MethodProperty.setValue(MethodProperty.java:709)
	... 29 more

I would be glad if someone could help me. Thanks.

Sorry for my english,
Thomas

Here is the class from which I build the BeanItem:

@Entity
@Table(name = "informationsempfaenger")
public class Informationsempfaenger extends DomainObject {

	private static final long serialVersionUID = -5838470992191901480L;

	@Column(name = "bez", length = 128, nullable = false)
	private String bezeichnung;

	@Enumerated(EnumType.ORDINAL)
	@Column(length = 1)
	private Informationsart artDerInformation;

	@Column(name = "email", length = 128, nullable = false)
	private String email;

	@SuppressWarnings("unused")
	private Informationsempfaenger() {
		super();
	}

	public Informationsempfaenger(Long id, int version, Date erstellung,
			Date aktualisierung, String letzterBearbeiter, boolean geloescht,
			String bez, String email, Informationsart artDerInformation) {
		super(id, version, erstellung, aktualisierung, letzterBearbeiter,
				geloescht);
		assert bez != null : "bez darf nicht null sein";
		assert bez.length() != 0 : "laenge von bez darf nicht 0 sein";
		assert email != null : "email darf nicht null sein";
		assert email.length() != 0 : "laenge von email darf nicht 0 sein";
		assert artDerInformation != null : "artDerInformation darf nicht null sein";
		this.bezeichnung = bez;
		this.email = email;
		this.artDerInformation = artDerInformation;
	}

       ... setter and getter Methods

	@Override
	public String toString() {
		return getBezeichnung() + "(" + getEmail() + ")";
	}

	@Override
	public boolean equals(Object obj) {
		if (!super.equals(obj)) {
			return false;
		}
		final Informationsempfaenger other = (Informationsempfaenger) obj;
		if (!bezeichnung.endsWith(other.bezeichnung)) {
			return false;
		}
		if (!email.endsWith(other.email)) {
			return false;
		}
		if (!artDerInformation.equals(other.artDerInformation)) {
			return false;
		}
		return true;
	}

	@Override
	public int hashCode() {
		int hash = super.hashCode();
		hash = 97 * hash + bezeichnung.hashCode();
		hash = 97 * hash + email.hashCode();
		hash = 97 * hash + artDerInformation.hashCode();
		return hash;
	}
}

Here is my enum:

public enum Informationsart {

	IT_BESCHAFFUNG(1), 
	DMS_TEAM(2), 
	GEHEIMSCHUTZBEAUFTRAGTER_MDF(3), 
	PERSONALRAT(4);

	private int ART;

	Informationsart(int art) {
		this.ART = art;
	}
	
	@Override
	public String toString() {
		if (ART == 1) {
			return "IT-Beschaffung";
		}
		if (ART == 2) {
			return "DMS-Team";
		}
		if (ART == 3) {
			return "Geheimschutzbeauftragter des MdF";
		}
		if (ART == 4) {
			return "Personalrat";
		}
		return "";
	}

}

If I remember correctly, if the actual and wanted value types are not compatible, fields perform a standard “conversion” by calling (via reflection) a constructor of the desired type that takes as a parameter the toString() result of the given value. With enums, as they cannot be created from outside the enum itself, this always causes a problem unless the item ID in the select component is the actual enum value.

The first option (if practical) would be to make the item IDs in the combo box be the enum values themselves e.g. by using a BeanItemContainer for the combo box. This way, no conversions should be required.

Alternatively, to perform your own conversions, take a look at FieldWrapper in the
CustomField
add-on.

Hi Henri,

Thank you for your help.

I can’t change the enum. It’s given to me. And display a number into the combo box is ugly and is thus not in question. Or have I misunderstood you?

The mentioned alternative sounds interesting. Perhaps you have an example?

Greetings,
Thomas

If you cannot touch the enum:

The first option would be to use a BeanItemContainer in the ComboBox - I think toString() will be used by default if you don’t specify an item caption property, and you could also specify explicit captions for each item in the combo box if you want.

If you need any conversions (the FieldWrapper/PropertyConverter option), I don’t have an example with enums but it should be similar to
this one
.

ok, thanks again. I’ll look at this.

Hi,

have you found a solution for this?

I have the same problem here. Using the PropertyConverter didnt do the trick, and the ComboBox is not even displayed.

How did you get your Enumerated Values into the Combobox?

Regards,

Mitko

I get the same behavior with the component disappearing.

Is there a good reason why enums aren’t considered being a natural source of options for the select component?

Ok, I solved it by wrapping my enum-property in my own PropertyWrapper that handles conversion from and to String. Works quite nice in the end but I still wonder why enums as options for a select/combobox isn’t provided through the standard APIs?

Hi Fredrick,

I’ve got the same issue. Could you possible post your propertywrapper code? I’m looking for a concrete example.

Best regards,

mike

Hi all!

For this problem. Ive created a simple helper that builds a container with a HashMap of ItemIds(can be a enum), and String descriptions to captions. Whith this, I can even use a Internationalization classes to build the Captions, not using the enum names itself.

No conversion is needed. Setting and getting values is transparent.

I use a code like that:


public class ContainerUtils {

    public static String CAPTION_PROPERTY_NAME = "caption";
    
    public static Container createContainerFromMap(Map<?, String> hashMap) {
        IndexedContainer container = new IndexedContainer();
        container.addContainerProperty(CAPTION_PROPERTY_NAME, String.class, "");
        
        Iterator<?> iter = hashMap.keySet().iterator();
        while(iter.hasNext()) {
            Object itemId = iter.next();
            container.addItem(itemId);
            container.getItem(itemId).getItemProperty(CAPTION_PROPERTY_NAME).setValue(hashMap.get(itemId));
        }
        
        return container;
    }
       
}

To use:


        LinkedHashMap<EnumType, String> map = new LinkedHashMap<EnumType, String>();
        map.put(EnumType.ENUM1, "Enum1 Caption");
        map.put(EnumType.ENUM2, "Enum2 Caption");
        ComboBox box = new ComboBox("Box", ContainerUtils.createContainerFromMap(map));
        box.setItemCaptionMode(ComboBox.ITEM_CAPTION_MODE_PROPERTY);
        box.setItemCaptionPropertyId(ContainerUtils.CAPTION_PROPERTY_NAME);

You can use static methods to builds the container, initializes the combo, etc etc…

I hope that it can helps.

Hi, Eduardo! Your helper is handy! Let me suggest you another method:

public static Container createContainerFromEnumClass(Class<? extends Enum<?>> enumClass) {
	LinkedHashMap<Enum<?>, String> enumMap = new LinkedHashMap<Enum<?>, String>();
	for (Object enumConstant : enumClass.getEnumConstants()) {
		enumMap.put((Enum<?>) enumConstant, enumConstant.toString());
	}

	return createContainerFromMap(enumMap);
}

// use it this way: createContainerFromEnumClass(MyEnum.class)

It can be useful when your enum overrides toString.

Hi all,

can someone post a complete example, because I couldn’t manage to solve this. I tried everything posted here for the past 2 days but without success.

In my table, there is a 1 char column with values Y and N (Yes and No). I would like to have a combo box in my form which will be populated with YesNoEnum so I can select some value rather than typing Y or N in the TextField.

I did some debugging and noticed that the problem is with comparing String value from combo box with enum from my Entity (“Yes” is comapred with YesNoEnum.Yes)

Thank you

Here is my enum:

public enum YesNoEnum {

	Y("Yes", "Y"), //
	N("No", "N");

	private String caption;
	private String code;

	YesNoEnum(String caption, String code) {
		this.caption = caption;
		this.code = code;
	}

	public String getCaption() {
		return caption;
	}

	public String getCode() {
		return code;
	}

	public String toString() {
		return caption;
	}
}

Entity:

privileged aspect Model_Roo_DbManaged {
    
    //@Enumerated(EnumType.STRING)
    @Column(name = "ACTIVE", length = 1)
    @NotNull
    private YesNoEnum Model.active;
	
	    
    public YesNoEnum Model.getActive() {
        return this.Model;
    }
    
    public void Valuta.setActive(YesNoEnum Model) {
        this.Model = Model;
    }

Form:

public FormFieldFactory getFormFieldFactory() {
		return new DefaultFieldFactory() {
			@Override
			public Field createField(Item item, Object propertyId,
					Component uiContext) {
				Field field = null;
				if (getIdProperty().equals(propertyId)
						|| getVersionProperty().equals(propertyId)) {
					return null;
				} else if ("active".equals(propertyId)) {
					cboActive = new ComboBox("Active", createContainerFromEnumClass(YesNoEnum.class));
					cboActive.setNullSelectionAllowed(false);
					cboActive.setRequired(true);
					field = new BeanFieldWrapper<YesNoEnum>(cboActive, YesNoEnum.class, createContainerFromEnumClass(YesNoEnum.class), "code");
					field.setCaption(createCaptionByPropertyId(propertyId));
					return field;
				}
			}
		}
}
				
	public static Container createContainerFromEnumClass(
			Class<? extends Enum<?>> enumClass) {
		LinkedHashMap<Enum<?>, String> enumMap = new LinkedHashMap<Enum<?>, String>();
		for (Object enumConstant : enumClass.getEnumConstants()) {
			enumMap.put((Enum<?>) enumConstant, enumConstant.toString());
		}

		return createContainerFromMap(enumMap);
	}

	public static String CAPTION_PROPERTY_NAME = "caption";

	public static Container createContainerFromMap(Map<?, String> hashMap) {
		IndexedContainer container = new IndexedContainer();
		container.addContainerProperty(CAPTION_PROPERTY_NAME, String.class, "");

		Iterator<?> iter = hashMap.keySet().iterator();
		while (iter.hasNext()) {
			Object itemId = iter.next();
			container.addItem(itemId);
			container.getItem(itemId).getItemProperty(CAPTION_PROPERTY_NAME)
					.setValue(hashMap.get(itemId));
		}

		return container;
	}

Thanks Eduardo and André Gomes, your ideas were great. Solved my problem with this issue.
Thanks,
Anthony

I use BeanContainer to do in simple way:
demo code:
public class EnumItem {
private Integer codeId = null;
private String nameValue = null;

    public Integer getCodeId() {
        return codeId;
    }

    public void setCodeId(Integer codeId) {
        this.codeId = codeId;
    }

    public String getNameValue() {
        return nameValue;
    }

    public void setNameValue(String nameValue) {
        this.nameValue = nameValue;
    }

}

BeanContainer<Integer, EnumItem> enumContainer = new BeanContainer<Integer, EnumItem>(EnumItem.class);
enumContainer.setBeanIdProperty("codeId");
enumContainer.addAll(xxxx);
ComboBox typeField = new ComboBox();
typeField.setContainerDataSource(enumContainer);
typeField.setItemCaptionPropertyId("nameValue");

There is a more elegant way:


public enum Planet {
    MERCURY,
    VENUS,
    EARTH,
    MARS,
    JUPITER,
    SATURN,
    URANUS,
    NEPTUNE,
    PLUTO;
    public String getCaption(){
        return name();
    }
    public int getId(){
        return ordinal();
    }
}
BeanContainer<Integer, Planet> container =  new BeanContainer<>(Planet.class);
container.setBeanIdProperty("id");
container.addAll(EnumSet.allOf(Planet.class));

ComboBox planet = new ComboBox("Planet", container);
planet.setItemCaptionPropertyId("caption");

Andris Lapinsh:
There is a more elegant way:

public enum Planet {
    MERCURY,
    VENUS,
    EARTH,
    MARS,
    JUPITER,
    SATURN,
    URANUS,
    NEPTUNE,
    PLUTO;
    public String getCaption(){
        return name();
    }
    public int getId(){
        return ordinal();
    }
}
BeanContainer<Integer, Planet> container =  new BeanContainer<>(Planet.class);
container.setBeanIdProperty("id");
container.addAll(EnumSet.allOf(Planet.class));

ComboBox planet = new ComboBox("Planet", container);
planet.setItemCaptionPropertyId("caption");

Could you please explain what is BeanContainer? which library do you use?

@Ali This is an old thread, it’s probably about Vaadin 6 or 7. There is no such thing as a BeanContainer anymore in Vaadin Flow.