Problem with Table in CustomField

I have the following problem:

In my data model, I have an object (a bean, let’s call it BigBean) that has a set of other beans (let’s call them SmallBeans) as its member field. I want to allow editing this set (adding, removing, changing SmallBeans) in a form that is binded to the BigBean instance. Adding or changing small beans should not be allowed inline in the form, but should be realized in popup window with another, small form. This is required to allow validation of SmallBeans before they are added to the set in the BigBean.
So, I thought I could use a CustomField with a Table. SmallBeans in the BigBean would be displayed in Table, backed by a BeanItemContainer. Editing or adding SmallBeans would be done by displaying popup window with a form for the SmallBean.
To test my solution concept I prepared something like code below. This code below does not use CustomField, just a plain table and some code around it to allow editing the SmallBeans and adding them to the Table.

package com.macquail.vaadin.table.test;

import com.vaadin.data.Item;
import com.vaadin.data.Property.ValueChangeEvent;
import com.vaadin.data.Property.ValueChangeListener;
import com.vaadin.data.util.BeanItem;
import com.vaadin.data.util.BeanItemContainer;
import com.vaadin.server.VaadinRequest;
import com.vaadin.ui.Button;
import com.vaadin.ui.Form;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Table;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Window;
import com.vaadin.ui.Button.ClickEvent;

public class TableTest1UI extends UI {

	private BeanItemContainer<SimpleBean> bic = new BeanItemContainer<>(
			SimpleBean.class);

	private Table myTable;
	private VerticalLayout rootLayout = new VerticalLayout();
	private HorizontalLayout buttonBar = new HorizontalLayout();
	private Button addEntryButton;
	private Button editSelectedButton;
	

	@Override
	protected void init(VaadinRequest request) {
		setupTable();
		setupAddButton();
		setupEditSelectedButton();
		setupButtonBar();
		setContent(rootLayout);
	}

	private void setupButtonBar() {
		
		buttonBar.addComponent(addEntryButton);
		buttonBar.addComponent(editSelectedButton);
		rootLayout.addComponent(buttonBar);
	}

	private void setupEditSelectedButton() {
		editSelectedButton = new Button("Edit selected", new Button.ClickListener() {	
			@Override
			public void buttonClick(ClickEvent event) {
				displayForm(bic.getItem(myTable.getValue()), false);
			}
		});
		editSelectedButton.setEnabled(false);
	}

	private void setupTable() {
		myTable = new Table();

		myTable.setContainerDataSource(bic);
		myTable.setSelectable(true);
		myTable.setMultiSelect(false);
		myTable.setImmediate(true);
		
		myTable.addValueChangeListener(new ValueChangeListener() {
			
			/**
			 * 
			 */
			private static final long serialVersionUID = -7843318082825401136L;

			@Override
			public void valueChange(ValueChangeEvent event) {
				if(myTable.getValue()!=null) {
					editSelectedButton.setEnabled(true);
				} else {
					editSelectedButton.setEnabled(false);
				}
			}
		});

		rootLayout.addComponent(myTable);
	}

	private void setupAddButton() {
		addEntryButton = new Button();
		addEntryButton.setCaption("Add entry");
		addEntryButton.addClickListener(new Button.ClickListener() {

			@Override
			public void buttonClick(ClickEvent event) {
				createNewBeanAndDisplayForm();
			}

		});
		rootLayout.addComponent(addEntryButton);
	}

	private void createNewBeanAndDisplayForm() {
		SimpleBean newBean = new SimpleBean();
		BeanItem<SimpleBean> newItem = new BeanItem<>(newBean);

		displayForm(newItem, true);

	}

	private void displayForm(final Item dataSource, boolean addingNew) {
		final Form f = new Form();
		f.setItemDataSource(dataSource);
		f.setBuffered(true);

		final Window w = new Window();
		w.setModal(true);
		VerticalLayout windowLayout = new VerticalLayout();

		windowLayout.addComponent(f);

		final Button ok = new Button("ok");
		Button cancel = new Button("cancel");

		HorizontalLayout buttons = new HorizontalLayout();
		buttons.addComponent(ok);
		buttons.addComponent(cancel);

		windowLayout.addComponent(buttons);
		w.setContent(windowLayout);
		
		
		final Button.ClickListener cancelListener = new Button.ClickListener() {

			@Override
			public void buttonClick(ClickEvent event) {
				f.discard();
				w.close();

			}
		};

		cancel.addClickListener(cancelListener);
		if (addingNew) {
			ok.addClickListener(new Button.ClickListener() {

				@Override
				public void buttonClick(ClickEvent event) {
					if (f.isValid()) {
						f.commit();
						BeanItem<SimpleBean> beanItem = (BeanItem<SimpleBean>) dataSource;
						SimpleBean bean = beanItem.getBean();
						bic.addBean(bean);
						w.close();
					}
				}
			});
		} else {
			ok.addClickListener(new Button.ClickListener() {
				
				@Override
				public void buttonClick(ClickEvent event) {
					f.commit();
					w.close();
				}
			});
		}
		
		UI.getCurrent().addWindow(w);
	}

	public static class SmallBean {

	/**
	 * @param action
	 * @param ownersName
	 * @param ownersEmail
	 * @param secondaryOwnersName
	 * @param secondaryOwnersEmail
	 * @param dateDue
	 * @param dateCompleted
	 */

	private String action;
	private String ownersName;
	private String ownersEmail;
	private String secondaryOwnersName;
	private String secondaryOwnersEmail;
	private Date dateDue;
	private Date dateCompleted;

	@Override
	public String toString() {
		return "A simple bean: ${action}, ${ownersName}, ${ownersEmail}, ${secondaryOwnersName}, ${secondaryOwnersEmail}, ${dateDue.toString()}, ${dateCompleted.toString()}";
	}

	public SimpleBean(String action, String ownersName, String ownersEmail,
			String secondaryOwnersName, String secondaryOwnersEmail,
			Date dateDue, Date dateCompleted) {
		this.action = action;
		this.ownersName = ownersName;
		this.ownersEmail = ownersEmail;
		this.secondaryOwnersName = secondaryOwnersName;
		this.secondaryOwnersEmail = secondaryOwnersEmail;
		this.dateDue = dateDue;
		this.dateCompleted = dateCompleted;
	}

	public SimpleBean() {

	}

	/**
	 * @return the action
	 */
	public String getAction() {
		return action;
	}

	/**
	 * @param action
	 *            the action to set
	 */
	public void setAction(String action) {
		this.action = action;
	}

	/**
	 * @return the ownersName
	 */
	public String getOwnersName() {
		return ownersName;
	}

	/**
	 * @param ownersName
	 *            the ownersName to set
	 */
	public void setOwnersName(String ownersName) {
		this.ownersName = ownersName;
	}

	/**
	 * @return the ownersEmail
	 */
	public String getOwnersEmail() {
		return ownersEmail;
	}

	/**
	 * @param ownersEmail
	 *            the ownersEmail to set
	 */
	public void setOwnersEmail(String ownersEmail) {
		this.ownersEmail = ownersEmail;
	}

	/**
	 * @return the secondaryOwnersName
	 */
	public String getSecondaryOwnersName() {
		return secondaryOwnersName;
	}

	/**
	 * @param secondaryOwnersName
	 *            the secondaryOwnersName to set
	 */
	public void setSecondaryOwnersName(String secondaryOwnersName) {
		this.secondaryOwnersName = secondaryOwnersName;
	}

	/**
	 * @return the secondaryOwnersEmail
	 */
	public String getSecondaryOwnersEmail() {
		return secondaryOwnersEmail;
	}

	/**
	 * @param secondaryOwnersEmail
	 *            the secondaryOwnersEmail to set
	 */
	public void setSecondaryOwnersEmail(String secondaryOwnersEmail) {
		this.secondaryOwnersEmail = secondaryOwnersEmail;
	}

	/**
	 * @return the dateDue
	 */
	public Date getDateDue() {
		return dateDue;
	}

	/**
	 * @param dateDue
	 *            the dateDue to set
	 */
	public void setDateDue(Date dateDue) {
		this.dateDue = dateDue;
	}

	/**
	 * @return the dateCompleted
	 */
	public Date getDateCompleted() {
		return dateCompleted;
	}

	/**
	 * @param dateCompleted
	 *            the dateCompleted to set
	 */
	public void setDateCompleted(Date dateCompleted) {
		this.dateCompleted = dateCompleted;
	}
}
}

This was very promising, so I went on to implement my own CustomField:

package com.macquail.vaadin.table.test;

import java.util.Arrays;
import java.util.Date;
import java.util.LinkedHashSet;
import java.util.Set;

import com.vaadin.annotations.PreserveOnRefresh;
import com.vaadin.data.Item;
import com.vaadin.data.Property;
import com.vaadin.data.fieldgroup.FieldGroup;
import com.vaadin.data.fieldgroup.FieldGroup.CommitException;
import com.vaadin.data.util.BeanItem;
import com.vaadin.data.util.BeanItemContainer;
import com.vaadin.data.util.ObjectProperty;
import com.vaadin.data.util.PropertysetItem;
import com.vaadin.server.VaadinRequest;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Component;
import com.vaadin.ui.CustomField;
import com.vaadin.ui.Form;
import com.vaadin.ui.Table;
import com.vaadin.ui.Table.ColumnGenerator;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Window;

@PreserveOnRefresh
public class TableTest2UI extends UI {

	private static final String SIMPLE_BEANS = "simpleBeans";
	/**
	 * 
	 */
	private static final long serialVersionUID = 8018504267356739195L;

	@Override
	protected void init(VaadinRequest request) {
		VerticalLayout mainLayout = new VerticalLayout();

		PropertysetItem psi = new PropertysetItem();

		psi.addItemProperty(
				SIMPLE_BEANS,
				(Property<Set<SimpleBean>>) new ObjectProperty<Set<SimpleBean>>(
						new LinkedHashSet<SimpleBean>()));

		final FieldGroup fg = new FieldGroup();
		CustomField<Set<SimpleBean>> customField = new TestCustomField();
		mainLayout.addComponent(customField);
		setContent(mainLayout);

		fg.bind(customField, SIMPLE_BEANS);
		fg.setItemDataSource(psi);
		Button commit = new Button("Commit", new Button.ClickListener() {

			@Override
			public void buttonClick(ClickEvent event) {
				try {
					fg.commit();
				} catch (CommitException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println(fg.getItemDataSource()
						.getItemProperty(SIMPLE_BEANS).getValue());
			}
		});
		mainLayout.addComponent(commit);
	}

	public static class TestCustomField extends CustomField<Set<SimpleBean>> {

		private VerticalLayout myLayout;
		private Table myTable;
		private BeanItemContainer<SimpleBean> bic;
		private Button addButton;

		@Override
		protected Component initContent() {
			myLayout = new VerticalLayout();
			myTable = new Table();
			myTable.setImmediate(true);
			addButton = new Button("Add new");
			setupButton();
			bic = new BeanItemContainer<>(SimpleBean.class);
			myTable.setContainerDataSource(bic);
			setupTable();

			myLayout.addComponent(myTable);
			myLayout.addComponent(addButton);
			return myLayout;
		}

		private void setupButton() {
			addButton.addClickListener(new Button.ClickListener() {

				/**
				 * 
				 */
				private static final long serialVersionUID = 2709768780663048518L;

				@Override
				public void buttonClick(ClickEvent event) {
					Item temporary = new BeanItem<SimpleBean>(new SimpleBean());
					showEditWindowForItem(temporary, true);
				}

			});
		}

		@SuppressWarnings("unchecked")
		@Override
		public Class<? extends Set<SimpleBean>> getType() {
			return (Class<? extends Set<SimpleBean>>) Set.class;
		}

		private void setupTable() {
			myTable.setImmediate(true);

			final BeanItemContainer<SimpleBean> bic = new BeanItemContainer<SimpleBean>(
					SimpleBean.class);

			myTable.setContainerDataSource(bic, Arrays.asList("action",
					"ownersName", "ownersEmail", "secondaryOwnersName",
					"secondaryOwnersEmail", "dateDue", "dateCompleted"));

			myTable.addGeneratedColumn(" ", new ColumnGenerator() {
				/**
				 * 
				 */
				private static final long serialVersionUID = 6414229393221335106L;

				public Object generateCell(Table source, final Object itemId,
						Object columnId) {
					Button b = new Button("Edit");

					b.addClickListener(new Button.ClickListener() {

						/**
						 * 
						 */
						private static final long serialVersionUID = -2785917510375545381L;

						@Override
						public void buttonClick(ClickEvent event) {
							BeanItem<SimpleBean> temporaryItem = bic
									.getItem(itemId);
							showEditWindowForItem(temporaryItem, false);
						}
					});

					return b;
				}
			});
			myTable.addGeneratedColumn("", new ColumnGenerator() {
				/**
				 * 
				 */
				private static final long serialVersionUID = -8484119343196404732L;

				public Object generateCell(Table source, final Object itemId,
						Object columnId) {

					Button b = new Button("Remove");
					b.addClickListener(new Button.ClickListener() {
						/**
						 * 
						 */
						private static final long serialVersionUID = -5412127597801223641L;

						@Override
						public void buttonClick(ClickEvent event) {
							bic.removeItem(itemId);
						}
					});
					return b;
				}
			});
		}

		@Override
		protected Set<SimpleBean> getInternalValue() {
			return new LinkedHashSet<SimpleBean>(bic.getItemIds());
		}

		@Override
		protected void setInternalValue(Set<SimpleBean> newValue) {
			bic.removeAllItems();
			bic.addAll(newValue);
		}

		private void showEditWindowForItem(final Item temporaryItem, boolean addingNew) {
			VerticalLayout vl = new VerticalLayout();
			final Window w = new Window();
			final Form f = new Form();

			f.setItemDataSource(temporaryItem);
			f.setBuffered(true);
			f.setImmediate(true);

			vl.addComponent(f);
			Button okBtn = new Button("Ok");
			vl.addComponent(okBtn);

			w.setContent(vl);
			w.setModal(true);

			if (addingNew) {
				okBtn.addClickListener(new Button.ClickListener() {

					@Override
					public void buttonClick(ClickEvent event) {
						f.commit();
						BeanItem<SimpleBean> bi = (BeanItem<SimpleBean>) temporaryItem;
						SimpleBean sb = bi.getBean();
						bic.addBean(sb);
						w.close();
					}
				});
			} else {
				okBtn.addClickListener(new Button.ClickListener() {

					@Override
					public void buttonClick(ClickEvent event) {
						f.commit();
						w.close();
					}
				});

			}
			UI.getCurrent().addWindow(w);
		}
	}
	
	public static class SimpleBean {

		/**
		 * @param action
		 * @param ownersName
		 * @param ownersEmail
		 * @param secondaryOwnersName
		 * @param secondaryOwnersEmail
		 * @param dateDue
		 * @param dateCompleted
		 */

		private String action;
		private String ownersName;
		private String ownersEmail;
		private String secondaryOwnersName;
		private String secondaryOwnersEmail;
		private Date dateDue;
		private Date dateCompleted;

		@Override
		public String toString() {
			return "A simple bean: ${action}, ${ownersName}, ${ownersEmail}, ${secondaryOwnersName}, ${secondaryOwnersEmail}, ${dateDue.toString()}, ${dateCompleted.toString()}";
		}

		public SimpleBean(String action, String ownersName, String ownersEmail,
				String secondaryOwnersName, String secondaryOwnersEmail,
				Date dateDue, Date dateCompleted) {
			this.action = action;
			this.ownersName = ownersName;
			this.ownersEmail = ownersEmail;
			this.secondaryOwnersName = secondaryOwnersName;
			this.secondaryOwnersEmail = secondaryOwnersEmail;
			this.dateDue = dateDue;
			this.dateCompleted = dateCompleted;
		}
		
		public SimpleBean() {
			
		}

		/**
		 * @return the action
		 */
		public String getAction() {
			return action;
		}

		/**
		 * @param action
		 *            the action to set
		 */
		public void setAction(String action) {
			this.action = action;
		}

		/**
		 * @return the ownersName
		 */
		public String getOwnersName() {
			return ownersName;
		}

		/**
		 * @param ownersName
		 *            the ownersName to set
		 */
		public void setOwnersName(String ownersName) {
			this.ownersName = ownersName;
		}

		/**
		 * @return the ownersEmail
		 */
		public String getOwnersEmail() {
			return ownersEmail;
		}

		/**
		 * @param ownersEmail
		 *            the ownersEmail to set
		 */
		public void setOwnersEmail(String ownersEmail) {
			this.ownersEmail = ownersEmail;
		}

		/**
		 * @return the secondaryOwnersName
		 */
		public String getSecondaryOwnersName() {
			return secondaryOwnersName;
		}

		/**
		 * @param secondaryOwnersName
		 *            the secondaryOwnersName to set
		 */
		public void setSecondaryOwnersName(String secondaryOwnersName) {
			this.secondaryOwnersName = secondaryOwnersName;
		}

		/**
		 * @return the secondaryOwnersEmail
		 */
		public String getSecondaryOwnersEmail() {
			return secondaryOwnersEmail;
		}

		/**
		 * @param secondaryOwnersEmail
		 *            the secondaryOwnersEmail to set
		 */
		public void setSecondaryOwnersEmail(String secondaryOwnersEmail) {
			this.secondaryOwnersEmail = secondaryOwnersEmail;
		}

		/**
		 * @return the dateDue
		 */
		public Date getDateDue() {
			return dateDue;
		}

		/**
		 * @param dateDue
		 *            the dateDue to set
		 */
		public void setDateDue(Date dateDue) {
			this.dateDue = dateDue;
		}

		/**
		 * @return the dateCompleted
		 */
		public Date getDateCompleted() {
			return dateCompleted;
		}

		/**
		 * @param dateCompleted
		 *            the dateCompleted to set
		 */
		public void setDateCompleted(Date dateCompleted) {
			this.dateCompleted = dateCompleted;
		}
	}

}

However, that did not work the way I expected. Editing SmallBean in the small form made it disappear from the Table. Debugging has shown that SmallBeans are still in the container, however the Table did not show them. I would like to know what’s wrong with this approach and how to do it right. Please help…