Here is a solution:
The POJO must implement the TreeItem interface.
items are identified with an integer
items have a parent-id
items have an int array of children-ids
public interface TreeItem<T> {
public T[] getChildren();
public int[] getChildrenIds();
public int getParentId();
public int getId();
}
Then, I extended BeanContainer to implement HierarchicalContainer.
WARNING: Only use addBean(BEANTYPE bean)!
This method adds the entire tree recursively!
Adding items with a custom ID that does not match the Bean’s ID may lead to unexpected results, and I did not shield those methods!
import java.util.ArrayList;
import com.vaadin.data.Container;
import com.vaadin.data.util.BeanContainer;
import com.vaadin.data.util.BeanItem;
public class HierarchicalBeanContainer<BT extends TreeItem<BT>> extends BeanContainer<Integer,BT>
implements Container.Hierarchical {
private static final long serialVersionUID = -7483903937657002180L;
private ArrayList<Integer> rootitems = new ArrayList<Integer>();
public HierarchicalBeanContainer(Class<BT> type)
{ super(type);
rootitems.add(0);
}
public HierarchicalBeanContainer(Class<BT> type, int rootid)
{ super(type);
super.setBeanIdResolver(new BeanIdResolver<Integer,BT>() {
private static final long serialVersionUID = 14343948239048L;
@Override
public Integer getIdForBean(BT bean) {
return bean.getId();
}
});
rootitems.add(rootid);
}
public BeanItem<BT> addBean(BT bean)
{ BeanItem<BT> item = super.addBean(bean);
BT[] children = bean.getChildren();
for(int i = 0; i<children.length; i++)
addBean(children[i]
);
return item;
}
@Override
public ArrayList<Integer> getChildren(Object itemId) {
int[] ids = getItem(itemId).getBean().getChildrenIds();
ArrayList<Integer> list = new ArrayList<Integer>(ids.length);
for(int i = 0;i<ids.length;i++)
list.add(ids[i]
);
return list;
}
@Override
public Integer getParent(Object itemId) {
return getItem(itemId).getBean().getParentId();
}
@Override
public ArrayList<Integer> rootItemIds() {
return rootitems;
}
public void addRoot(int id)
{ if(!rootitems.contains(id))
rootitems.add(id);
}
public void removeRoot(int id)
{ if(rootitems.contains(id))
rootitems.remove(id);
}
@Override
public boolean setParent(Object itemId, Object newParentId)
throws UnsupportedOperationException {
throw new UnsupportedOperationException();
}
@Override
public boolean areChildrenAllowed(Object itemId) {
return hasChildren(itemId);
}
@Override
public boolean setChildrenAllowed(Object itemId, boolean areChildrenAllowed)
throws UnsupportedOperationException {
throw new UnsupportedOperationException();
}
@Override
public boolean isRoot(Object itemId) {
return rootitems.contains(getItem(itemId).getBean().getId());
}
@Override
public boolean hasChildren(Object itemId) {
int[] children = getItem(itemId).getBean().getChildrenIds();
return (children != null) && (children.length > 0);
}
}
You can also wrap the BeanContainer in ContainerHierarchicalWrapper to achieve this.
EDIT: it is actually better to use HierarchicalContainer, since it can deal with sorting and filtering (it’s not an easy feat since filtering can remove parent items; HierarchicalContainer makes sure that those parent items are still present in the tree even though they do not match the filter).