Skip to content

Instantly share code, notes, and snippets.

@uklance
Last active December 18, 2015 14:18
Show Gist options
  • Save uklance/5795703 to your computer and use it in GitHub Desktop.
Save uklance/5795703 to your computer and use it in GitHub Desktop.
A more database friendly TreeModel implementation
public class LazyTreeModel<T> implements TreeModel<T> {
private final ValueEncoder<T> encoder;
private final LazyTreeModelSource<T> source;
public LazyTreeModel<T>(ValueEncoder<T> encoder, LazyTreeModelSource<T> source) {
this.encoder = encoder;
this.source = source;
}
@Override
public List<TreeNode<T>> getRootNodes() {
List<T> roots = source.getRoots();
return LazyTreeModel.createTreeNodes(encoder, source, roots);
}
@Override
public TreeNode<T> getById(String id) {
return find(encoder.toValue(id));
}
@Override
public TreeNode<T> find(T value) {
return new LazyTreeNode<T>(encoder, source, value);
}
public static List<TreeNode> createTreeNodes(ValueEncoder<T> encoder, LazyTreeModelSource<T> source, List<T> siblings) {
if (siblings == null || siblings.isEmpty()) {
return Collections.emptyList();
}
List<TreeNode> treeNodes = new ArrayList<TreeNode>(siblings.size());
for (T value : siblings) {
treeNodes.add(new LazyTreeNode<T>(encoder, source, value));
}
return treeNodes;
}
}
public interface LazyTreeModelSource<T> {
/**
* Get the label from the value. Note: for efficiency this should be sourced
* directly from the value and should not require an extra database lookup
**/
String getLabel(T value);
/**
* Get the isLeaf flag from the value. Note: for efficiency this should be sourced
* directly from the value and should not require an extra database lookup
**/
boolean isLeaf(T value);
/**
* Get the hasChildren flag from the value. Note: for efficiency this should be sourced
* directly from the value and should not require an extra database lookup
**/
boolean hasChildren(T value);
/**
* Get the children of the value
**/
List<T> getChildren(T value);
/**
* Get the roots of the TreeModel
**/
List<T> getRoots();
}
public class LazyTreeNode<T> implements TreeNode<T> {
private final ValueEncoder<T> encoder;
private final LazyTreeModelSource<T> source;
private final T value;
public LazyTreeNode<T>(ValueEncoder<T> encoder, LazyTreeModelSource<T> source, T value) {
this.encoder = encoder;
this.source = source;
this.value = value;
}
@Override
public T getValue() {
return value;
}
@Override
public String getId() {
return encoder.toClient(value);
}
@Override
public boolean isLeaf() {
return source.isLeaf(value);
}
@Override
public boolean getHasChildren() {
return source.hasChildren(value);
}
@Override
public String getLabel() {
return source.getLabel(value);
}
@Override
public List<TreeNode<T>> getChildren() {
List<T> children = source.getChildren(value);
return LazyTreeModel.createTreeNodes(encoder, source, children);
}
}
@uklance
Copy link
Author

uklance commented Jun 17, 2013

The roots are intentionally not part of LazyTreeModel. The roots are looked up via LazyTreeNodeSource on demand. The roots are ONLY required when rendering the root of the tree and are NOT required when expanding a node. I prefer this approach to DefaultTreeModel which ALWAYS requires the roots.

@uklance
Copy link
Author

uklance commented Jul 31, 2013

This has been implemented in tapestry-stitch demo here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment