Permalink
Browse files

Add a find() method to TreeModel, and provide a DefaultTreeModel impl…

…ementation that delegates most behavior to a ValueEncoder and a TreeModelAdapter
  • Loading branch information...
1 parent aaa67d2 commit 59dc5a0d36b6bcf49c3ba1a6eabfd68b0b713245 @hlship committed Mar 15, 2011
@@ -0,0 +1,199 @@
+// Copyright 2011 Howard M. Lewis Ship
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.howardlewisship.tapx.core;
+
+import java.util.Deque;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.tapestry5.ValueEncoder;
+import org.apache.tapestry5.func.F;
+import org.apache.tapestry5.func.Mapper;
+import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
+
+/**
+ * A default implementation of TreeModel
+ * that starts with a {@link ValueEncoder} (for the element to string conversion),
+ * a {@link TreeModelAdapter}, and a list of root nodes.
+ * <p>
+ * This implementation is <em>not</em> thread safe.
+ *
+ * @param <T>
+ * @since 1.1
+ */
+public class DefaultTreeModel<T> implements TreeModel<T>
+{
+ private final ValueEncoder<T> encoder;
+
+ private final TreeModelAdapter<T> adapter;
+
+ private final List<TreeNode<T>> roots;
+
+ private final Map<String, TreeNode<T>> cache = CollectionFactory.newMap();
+
+ private final Mapper<T, TreeNode<T>> toTreeNode = new Mapper<T, TreeNode<T>>()
+ {
+ public TreeNode<T> map(T value)
+ {
+ return new DefaultTreeNode(value);
+ };
+ };
+
+ private class DefaultTreeNode implements TreeNode<T>
+ {
+ private final T value;
+
+ private List<TreeNode<T>> children;
+
+ DefaultTreeNode(T value)
+ {
+ this.value = value;
+ }
+
+ @Override
+ public String getId()
+ {
+ return encoder.toClient(value);
+ }
+
+ @Override
+ public T getValue()
+ {
+ return value;
+ }
+
+ @Override
+ public boolean isLeaf()
+ {
+ return adapter.isLeaf(value);
+ }
+
+ @Override
+ public boolean getHasChildren()
+ {
+ return adapter.hasChildren(value);
+ }
+
+ @Override
+ public List<TreeNode<T>> getChildren()
+ {
+ if (children == null)
+ children = F.flow(adapter.getChildren(value)).map(toTreeNode).toList();
+
+ return children;
+ }
+
+ @Override
+ public String getLabel()
+ {
+ return adapter.getLabel(value);
+ }
+
+ }
+
+ /**
+ * Creates a new model starting from a single root element.
+ *
+ * @param encoder
+ * @param adapter
+ * @param root
+ */
+ @SuppressWarnings("unchecked")
+ public DefaultTreeModel(ValueEncoder<T> encoder, TreeModelAdapter<T> adapter, T root)
+ {
+ this(encoder, adapter, CollectionFactory.newList(root));
+ }
+
+ /**
+ * Standard constructor.
+ *
+ * @param encoder
+ * used to convert values to strings and vice-versa
+ * @param adapter
+ * adapts elements to the tree
+ * @param roots
+ * defines the root nodes of the model
+ */
+ public DefaultTreeModel(ValueEncoder<T> encoder, TreeModelAdapter<T> adapter, List<T> roots)
+ {
+ assert encoder != null;
+ assert adapter != null;
+ assert roots != null;
+ assert !roots.isEmpty();
+
+ this.encoder = encoder;
+ this.adapter = adapter;
+ this.roots = F.flow(roots).map(toTreeNode).toList();
+ }
+
+ @Override
+ public List<TreeNode<T>> getRootNodes()
+ {
+ return roots;
+ }
+
+ @Override
+ public TreeNode<T> getById(String id)
+ {
+ assert id != null;
+
+ TreeNode<T> result = findById(id);
+
+ if (result == null)
+ throw new IllegalArgumentException(String.format("Could not locate TreeNode '%s'.", id));
+
+ return result;
+ }
+
+ private TreeNode<T> findById(String id)
+ {
+ TreeNode<T> result = cache.get(id);
+
+ if (result != null)
+ return result;
+
+ Deque<TreeNode<T>> queue = new LinkedList<TreeNode<T>>(roots);
+
+ while (!queue.isEmpty())
+ {
+ TreeNode<T> node = queue.removeFirst();
+
+ String nodeId = node.getId();
+
+ cache.put(nodeId, node);
+
+ if (nodeId.equals(id))
+ return node;
+
+ if (!node.isLeaf() && node.getHasChildren())
+ {
+ for (TreeNode<T> child : node.getChildren())
+ {
+ queue.addFirst(child);
+ }
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public TreeNode<T> find(T element)
+ {
+ return findById(encoder.toClient(element));
+ }
+
+}
@@ -16,10 +16,14 @@
import java.util.List;
+import org.apache.tapestry5.ValueEncoder;
+
import com.howardlewisship.tapx.core.components.Tree;
/**
- * A model for tree-oriented data used by the {@link Tree} component.
+ * A model for tree-oriented data used by the {@link Tree} component. The default implemention, {@link DefaultTreeModel}
+ * uses a {@link ValueEncoder} and a {@link TreeModelAdapter} to supply the
+ * underlying information.
*
* @param <T>
* type of data in the tree
@@ -40,4 +44,14 @@
* @see TreeNode#getId()
*/
TreeNode<T> getById(String id);
+
+ /**
+ * Recursively searches from the root nodes to find the tree node that matches
+ * the provided element.
+ *
+ * @param element
+ * to search for
+ * @return matching node, or null if not found
+ */
+ TreeNode<T> find(T element);
}
@@ -0,0 +1,53 @@
+// Copyright 2011 Howard M. Lewis Ship
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.howardlewisship.tapx.core;
+
+import java.util.List;
+
+/**
+ * Used with {@link DefaultTreeModel} to define how to extract labels and child nodes from a value.
+ *
+ * @since 1.1
+ */
+public interface TreeModelAdapter<T>
+{
+ /**
+ * Determines if the value is a leaf or a (potential) container of children.
+ *
+ * @see TreeNode#isLeaf()
+ */
+ boolean isLeaf(T value);
+
+ /**
+ * Returns true if the value has children (only invoked for non-leaf values).
+ *
+ * @see TreeNode#getHasChildren()
+ */
+ boolean hasChildren(T value);
+
+ /**
+ * Returns the children, in the order they should be presented to the client.
+ *
+ * @see TreeNode#getChildren()
+ */
+ List<T> getChildren(T value);
+
+ /**
+ * Returns a text label for the value, which may be presented to the client.
+ *
+ * @see TreeNode#getLabel()
+ */
+ String getLabel(T value);
+}
@@ -17,7 +17,8 @@
import java.util.List;
/**
- * A node within a {@link TreeModel}.
+ * A node within a {@link TreeModel}. In a {@link DefaultTreeModel}, most of the node's information
+ * comes via the {@link TreeModelAdapter}.
*
* @param <T>
* type of node
@@ -41,18 +42,31 @@
* may have children (i.e., a folder).
*
* @return true for leaf nodes, false for folder nodes
+ * @see TreeModelAdapter#isLeaf(Object)
*/
boolean isLeaf();
- /** Returns true if this non-leaf node has child nodes. This will not be invoked for leaf nodes. */
+ /**
+ * Returns true if this non-leaf node has child nodes. This will not be invoked for leaf nodes.
+ *
+ * @see TreeModelAdapter#hasChildren(Object)
+ */
boolean getHasChildren();
- /** Returns the actual children of this non-leaf node, as additional nodes. */
+ /**
+ * Returns the actual children of this non-leaf node, as additional nodes.
+ *
+ * @see TreeModelAdapter#getChildren(Object)
+ */
List<TreeNode<T>> getChildren();
// TODO: Some way to influence the rendered output (i.e., to display different icons based on
// file type).
- /** Returns a textual label for the node. Not all UIs will make use of the label, but default UIs will. */
+ /**
+ * Returns a textual label for the node. Not all UIs will make use of the label, but default UIs will.
+ *
+ * @see TreeModelAdapter#getLabel(Object)
+ */
public String getLabel();
}
Oops, something went wrong.

0 comments on commit 59dc5a0

Please sign in to comment.