001 package net.databinder.components.tree.hib;
002
003 import java.io.Serializable;
004 import java.util.Collection;
005 import java.util.List;
006
007 import javax.swing.tree.DefaultMutableTreeNode;
008 import javax.swing.tree.DefaultTreeModel;
009 import javax.swing.tree.TreeModel;
010
011 import net.databinder.components.tree.data.DataTreeObject;
012 import net.databinder.models.hib.CriteriaBuilder;
013 import net.databinder.models.hib.HibernateListModel;
014 import net.databinder.models.hib.HibernateObjectModel;
015
016 import org.apache.wicket.ajax.AjaxRequestTarget;
017 import org.apache.wicket.markup.html.tree.BaseTree;
018 import org.apache.wicket.model.IModel;
019 import org.apache.wicket.model.Model;
020 import org.hibernate.Criteria;
021 import org.hibernate.criterion.Property;
022
023
024 /**
025 * An extension of {@link BaseTree} based on node objects being represented by
026 * {@link HibernateObjectModel}s. Additionally, it offers some convenience
027 * methods.
028 *
029 * @author Thomas Kappler
030 *
031 * @param <T> the IDataTreeNode implementation being represented by the tree nodes
032 */
033 public abstract class DataTree<T extends DataTreeObject<T>> extends BaseTree {
034 /**
035 * Construct a tree with a root entity.
036 * @param id Wicket id
037 * @param rootModel must contain a root of type T
038 */
039 @SuppressWarnings("unchecked")
040 public DataTree(String id, HibernateObjectModel rootModel) {
041 super(id);
042 DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode(rootModel);
043 populateTree(rootNode, ((T)rootModel.getObject()).getChildren());
044 setModel(new Model(new DefaultTreeModel(rootNode)));
045 }
046
047 /**
048 * Construct a rootless tree based on a list of top level nodes.
049 * @param id
050 * @param topLevelModel must contain a List<T> of top level children
051 */
052 @SuppressWarnings("unchecked")
053 public DataTree(String id, HibernateListModel topLevelModel) {
054 super(id);
055 setRootLess(true);
056 DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode(topLevelModel);
057 populateTree(rootNode, (List<T>)topLevelModel.getObject());
058 setModel(new Model(new DefaultTreeModel(rootNode)));
059 }
060
061 /**
062 * Convenience criteria builder for fetching top-level entities.
063 */
064 public static class TopLevelCriteriaBuilder implements CriteriaBuilder {
065 /**
066 * build criteria for a null "parent" property
067 */
068 public void build(Criteria criteria) {
069 criteria.add(Property.forName("parent").isNull());
070 }
071 }
072
073 public DefaultMutableTreeNode clear(AjaxRequestTarget target) {
074 T newObject = createNewObject();
075 DefaultMutableTreeNode newRootNode = new DefaultMutableTreeNode(
076 new HibernateObjectModel(newObject));
077 TreeModel treeModel = new DefaultTreeModel(newRootNode);
078 setModel(new Model((Serializable) treeModel));
079 repaint(target);
080 return newRootNode;
081 }
082
083 /**
084 * Recursively build the tree nodes according to the structure given by the
085 * beans.
086 *
087 * @param parent
088 * a tree node serving as parent to the newly created nodes for
089 * the elements in children
090 * @param children
091 * objects to be inserted into the tree below parent
092 */
093 private void populateTree(DefaultMutableTreeNode parent,
094 Collection<T> children) {
095 for (T t : children) {
096 HibernateObjectModel m = new HibernateObjectModel(t);
097 DefaultMutableTreeNode node = new DefaultMutableTreeNode(m);
098 parent.add(node);
099 populateTree(node, t.getChildren());
100 }
101 }
102
103 /**
104 * Get the IDataTreeNode instance behind this node, or null if the node is the
105 * root of a tree with no root entity.
106 *
107 * @param node
108 * a tree node
109 * @return the object represented by node
110 */
111 @SuppressWarnings("unchecked")
112 public T getDataTreeNode(DefaultMutableTreeNode node) {
113 Object nodeObject = ((IModel) node.getUserObject()).getObject();
114 return (nodeObject instanceof DataTreeObject<?>) ?
115 (T) nodeObject : null;
116 }
117
118 /**
119 * @return the root node of the tree
120 */
121 public DefaultMutableTreeNode getRootNode() {
122 DefaultTreeModel treeModel = (DefaultTreeModel) getModelObject();
123 if (treeModel.getRoot() == null) {
124 return null;
125 }
126 return (DefaultMutableTreeNode) treeModel.getRoot();
127 }
128
129 /**
130 * Create a new user object using {@link #createNewObject()} and add it
131 * to the tree as a child of parentNode.
132 *
133 * @param parentNode
134 * to node serving as parent of the new object
135 * @return the newly created tree node
136 */
137 public DefaultMutableTreeNode addNewChildNode(DefaultMutableTreeNode parentNode) {
138 T newObject = createNewObject();
139
140 T parent = getDataTreeNode(parentNode);
141 if (parent != null)
142 parent.addChild(newObject);
143
144 DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(
145 new HibernateObjectModel(newObject));
146 parentNode.add(newNode);
147 return newNode;
148 }
149
150 /**
151 * Repaint the tree when something has changed. It possibly does too much,
152 * but you're safe that changes do show after you call it.
153 *
154 * @param target
155 */
156 public void repaint(AjaxRequestTarget target) {
157 invalidateAll();
158 updateTree(target);
159 }
160
161 /**
162 * Create a new instance of T. Used to create the backing objects of new
163 * tree nodes.
164 *
165 * @return a new instance of T
166 */
167 protected abstract T createNewObject();
168
169 /**
170 * Override to update components when another tree node is selected. Does
171 * nothing by default.
172 *
173 * @param target
174 * @param selectedNode
175 * the currently selected node
176 */
177 public void updateDependentComponents(AjaxRequestTarget target, DefaultMutableTreeNode selectedNode) {
178 // Do nothing by default
179 }
180
181 @Override
182 public void onDetach() {
183 super.onDetach();
184 // in a root less tree it's not bound to any component
185 ((IModel)getRootNode().getUserObject()).detach();
186 }
187 }