Combobox, Databinding and JavaBeans

I have been studying Vaadin for some months and now I have faced up with the problem I didn’t manage to resolve by myself.

I would like to implement an option of adding new values to a combobox. The combobox includes the names of the operating systems, for example.

Here is a fragment of my code.
I have a bean which presents an operating system:

public class OperatingSystem implements Serializable {
    
    private static final long serialVersionUID = 1L;
    private Integer osId;
    private String osName;

    public OperationSystem(Integer osId, String osName) {
        this.osId = osId;
        this.osName = osName;
    }

    public Integer getOsId() {
        return osId;
    }

    public void setOsId(Integer osId) {
        this.osId = osId;
    }

    public String getOsName() {
        return osName;
    }

    public void setOsName(String osName) {
        this.osName = osName;
    }
}

Also a class that generates test data:

[code]
public class TestData {

public static ArrayList<OperationSystem> getOperatingSystems() {
    ArrayList<OperationSystem> os = new ArrayList<>();
    
    os.add(new OperationSystem(1, "Linux"));
    os.add(new OperationSystem(2, "Windows"));
    os.add(new OperationSystem(3, "FreeBSD"));
    
    return os;
}

}
[/code]And a code that creates and handles the combobox:

BeanContainer<Integer, OperatingSystem> os = new BeanContainer<>(OperatingSystem.class);
os.setBeanIdProperty("osId");
ArrayList<OperatingSystem> osData = TestData.getOperatingSystems();
os.addAll(osData);

ComboBox osName = new ComboBox("Select OS Name:", os);
osName.setNullSelectionAllowed(true);
osName.setItemCaptionPropertyId("osName");
osName.setNewItemsAllowed(true);

osName.setNewItemHandler(newItemCaption -> {
            boolean newItem = osData.stream().noneMatch(data -> data.getOsName().equalsIgnoreCase(newItemCaption));
            if (newItem) {
                OperatingSystem newOs = new OperatingSystem(generateNextOsId(osData), newItemCaption);
                os.addBean(newOs);
                // osData.add(newOs); // if remove comment everything is fine
                osName.select(newOs.getOsId());
            }
});

Please also pay attention that a method generateNextOsId was created for generation of next id for an operating system.

private Integer generateNextOsId(final ArrayList<OperatingSystem> osData) {
        Integer currentMaxOsId = osData.stream().mapToInt(OperatingSystem::getOsId).max().getAsInt();
        return new Integer(currentMaxOsId.intValue() + 1);
    }

And it works, but don’t in a right way :slight_smile:
What do I mean?
Let suppose, that I want to add, says, UNIX operating system into created combobox. I will be succeed in this action. The generateNextOsId method will create new id with value equals 4 (four) and UNIX operating system will be succesfully added into the bean container (is represented by a variable named “os”) and will appear in the combobox and combobox drop-down list. OK.
Go further.
Next, I have an strong desire to add Xenix operating system (oh, Lord) into the combobox. That is impossible and I know why. It’s becouse of generateNextOsId method will create new id with value equals 4 (four) AGAIN!!! It means that changes from the bean container don’t propagated to the backing beans (is represented by a variable named “osData”) .
Probably that’s right behaviour.
If I remove comment imposed on line marked by “if remove comment everything is fine” - it works and I can add whole bunch of the new operating systems into combobox!

I have put through myself some information about containers but it seems it’s not enough becouse I’m stuck.

So final question is very simple: what is the right solution to get data propagated from the bean container to the backing beans? It doesn’t seem right for me to use add methods twice:

os.addBean(newOs);
osData.add(newOs);

I would appreciate you if you show me more elegant and (the main thing!) right solution from Vaadin’s point of view.

You need to decide which is the authoritative data source (osData or os), and keep it updated and use it in all operations. If osData is not needed elsewhere, implement all operations (such as the checking if an item is new and generateNextOsId()) in terms of os (the BeanContainer) and don’t use osData for anything after it has been used to create the container. See e.g. AbstractBeanContainer.getItemIds() for a simpler way to implement generateNextOsId().

Alternatively, you could use Vaadin 8 and the new DataProvider API, and a ListDataProvider with a modifiable collection that you can add to before refreshing the ComboBox. That way, you could actually keep using osData as the internal authoritative data source.