package net.databinder.components.tree.hib; import java.io.Serializable; import java.util.Collection; import java.util.List; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeModel; import net.databinder.components.tree.data.DataTreeObject; import net.databinder.models.hib.CriteriaBuilder; import net.databinder.models.hib.HibernateListModel; import net.databinder.models.hib.HibernateObjectModel; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.markup.html.tree.BaseTree; import org.apache.wicket.model.IModel; import org.apache.wicket.model.Model; import org.hibernate.Criteria; import org.hibernate.criterion.Property; /** * An extension of {@link BaseTree} based on node objects being represented by * {@link HibernateObjectModel}s. Additionally, it offers some convenience * methods. * * @author Thomas Kappler * * @param <T> the IDataTreeNode implementation being represented by the tree nodes */ public abstract class DataTree<T extends DataTreeObject<T>> extends BaseTree { /** * Construct a tree with a root entity. * @param id Wicket id * @param rootModel must contain a root of type T */ @SuppressWarnings("unchecked") public DataTree(String id, HibernateObjectModel rootModel) { super(id); DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode(rootModel); populateTree(rootNode, ((T)rootModel.getObject()).getChildren()); setDefaultModel(new Model(new DefaultTreeModel(rootNode))); } /** * Construct a rootless tree based on a list of top level nodes. * @param id * @param topLevelModel must contain a List<T> of top level children */ @SuppressWarnings("unchecked") public DataTree(String id, HibernateListModel topLevelModel) { super(id); setRootLess(true); DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode(topLevelModel); populateTree(rootNode, (List<T>)topLevelModel.getObject()); setDefaultModel(new Model(new DefaultTreeModel(rootNode))); } /** * Convenience criteria builder for fetching top-level entities. */ public static class TopLevelCriteriaBuilder implements CriteriaBuilder { /** * build criteria for a null "parent" property */ public void build(Criteria criteria) { criteria.add(Property.forName("parent").isNull()); } } public DefaultMutableTreeNode clear(AjaxRequestTarget target) { T newObject = createNewObject(); DefaultMutableTreeNode newRootNode = new DefaultMutableTreeNode( new HibernateObjectModel<T>(newObject)); TreeModel treeModel = new DefaultTreeModel(newRootNode); setDefaultModel(new Model<Serializable>((Serializable) treeModel)); repaint(target); return newRootNode; } /** * Recursively build the tree nodes according to the structure given by the * beans. * * @param parent * a tree node serving as parent to the newly created nodes for * the elements in children * @param children * objects to be inserted into the tree below parent */ private void populateTree(DefaultMutableTreeNode parent, Collection<T> children) { for (T t : children) { HibernateObjectModel<T> m = new HibernateObjectModel<T>(t); DefaultMutableTreeNode node = new DefaultMutableTreeNode(m); parent.add(node); populateTree(node, t.getChildren()); } } /** * Get the IDataTreeNode instance behind this node, or null if the node is the * root of a tree with no root entity. * * @param node * a tree node * @return the object represented by node */ @SuppressWarnings("unchecked") public T getDataTreeNode(DefaultMutableTreeNode node) { Object nodeObject = ((IModel) node.getUserObject()).getObject(); return (nodeObject instanceof DataTreeObject<?>) ? (T) nodeObject : null; } /** * @return the root node of the tree */ public DefaultMutableTreeNode getRootNode() { DefaultTreeModel treeModel = (DefaultTreeModel) getDefaultModelObject(); if (treeModel.getRoot() == null) { return null; } return (DefaultMutableTreeNode) treeModel.getRoot(); } /** * Create a new user object using {@link #createNewObject()} and add it * to the tree as a child of parentNode. * * @param parentNode * to node serving as parent of the new object * @return the newly created tree node */ public DefaultMutableTreeNode addNewChildNode(DefaultMutableTreeNode parentNode) { T newObject = createNewObject(); T parent = getDataTreeNode(parentNode); if (parent != null) parent.addChild(newObject); DefaultMutableTreeNode newNode = new DefaultMutableTreeNode( new HibernateObjectModel<T>(newObject)); parentNode.add(newNode); return newNode; } /** * Repaint the tree when something has changed. It possibly does too much, * but you're safe that changes do show after you call it. * * @param target */ public void repaint(AjaxRequestTarget target) { invalidateAll(); updateTree(target); } /** * Create a new instance of T. Used to create the backing objects of new * tree nodes. * * @return a new instance of T */ protected abstract T createNewObject(); /** * Override to update components when another tree node is selected. Does * nothing by default. * * @param target * @param selectedNode * the currently selected node */ public void updateDependentComponents(AjaxRequestTarget target, DefaultMutableTreeNode selectedNode) { // Do nothing by default } @Override public void onDetach() { super.onDetach(); // in a root less tree it's not bound to any component ((IModel)getRootNode().getUserObject()).detach(); } }