Changes TreeModel into TreeViewModel and adds the ability to fire events.


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@7608 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/bikeshed/src/com/google/gwt/sample/tree/client/AbstractTreeNodeFactory.java b/bikeshed/src/com/google/gwt/sample/tree/client/AbstractTreeNodeFactory.java
deleted file mode 100644
index 9e13f74..0000000
--- a/bikeshed/src/com/google/gwt/sample/tree/client/AbstractTreeNodeFactory.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright 2010 Google Inc.
- * 
- * 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.google.gwt.sample.tree.client;
-
-import com.google.gwt.cells.client.Cell;
-import com.google.gwt.list.shared.ListModel;
-
-/**
- * A factory for generating tree nodes.
- * 
- * @param <C> the type of children in the tree node.
- */
-public abstract class AbstractTreeNodeFactory<C> implements TreeNodeFactory<C> {
-
-  private ListModel<C> listModel;
-  private Cell<C> cell;
-
-  /**
-   * Construct a new {@link AbstractTreeNodeFactory}.
-   * 
-   * @param listModel the {@link ListModel} used to generate child values
-   * @param cell the {@link Cell} used to render children
-   */
-  public AbstractTreeNodeFactory(ListModel<C> listModel, Cell<C> cell) {
-    this.listModel = listModel;
-    this.cell = cell;
-  }
-
-  /**
-   * Get the {@link Cell} used to render child nodes.
-   * 
-   * @return the cell
-   */
-  public Cell<C> getCell() {
-    return cell;
-  }
-
-  /**
-   * Get the {@link ListModel} used to retrieve child node values.
-   * 
-   * @return the list model
-   */
-  public ListModel<C> getListModel() {
-    return listModel;
-  }
-}
diff --git a/bikeshed/src/com/google/gwt/sample/tree/client/DefaultTreeNodeFactory.java b/bikeshed/src/com/google/gwt/sample/tree/client/DefaultTreeNodeFactory.java
deleted file mode 100644
index a5b7bb7..0000000
--- a/bikeshed/src/com/google/gwt/sample/tree/client/DefaultTreeNodeFactory.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2010 Google Inc.
- * 
- * 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.google.gwt.sample.tree.client;
-
-import com.google.gwt.cells.client.Cell;
-import com.google.gwt.list.shared.ListModel;
-
-/**
- * Default implementation of a {@link TreeNodeFactory}.
- * 
- * @param <C> the type of children in the tree node.
- */
-public class DefaultTreeNodeFactory<C> extends AbstractTreeNodeFactory<C> {
-
-  private TreeModel treeModel;
-
-  /**
-   * Construct a new {@link DefaultTreeNodeFactory}.
-   * 
-   * @param listModel the {@link ListModel} used to generate child values
-   * @param cell the {@link Cell} used to render children
-   * @param treeModel the {@link TreeModel} used to generate factories
-   */
-  public DefaultTreeNodeFactory(ListModel<C> listModel, Cell<C> cell,
-      TreeModel treeModel) {
-    super(listModel, cell);
-    this.treeModel = treeModel;
-  }
-
-  public TreeNodeFactory<?> createChildFactory(C value) {
-    return treeModel.createTreeNodeFactory(value);
-  }
-}
diff --git a/bikeshed/src/com/google/gwt/sample/tree/client/MyTreeModel.java b/bikeshed/src/com/google/gwt/sample/tree/client/MyTreeViewModel.java
similarity index 76%
rename from bikeshed/src/com/google/gwt/sample/tree/client/MyTreeModel.java
rename to bikeshed/src/com/google/gwt/sample/tree/client/MyTreeViewModel.java
index 81c1d40..a5e025f 100644
--- a/bikeshed/src/com/google/gwt/sample/tree/client/MyTreeModel.java
+++ b/bikeshed/src/com/google/gwt/sample/tree/client/MyTreeViewModel.java
@@ -15,8 +15,10 @@
  */
 package com.google.gwt.sample.tree.client;
 
+import com.google.gwt.cells.client.ButtonCell;
 import com.google.gwt.cells.client.Cell;
 import com.google.gwt.cells.client.TextCell;
+import com.google.gwt.cells.client.ValueUpdater;
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.list.shared.AsyncListModel;
 import com.google.gwt.list.shared.ListModel;
@@ -29,7 +31,7 @@
 /**
  * A demo TreeModel.
  */
-public class MyTreeModel implements TreeModel {
+public class MyTreeViewModel implements TreeViewModel {
 
   private static class IntegerListModel extends AsyncListModel<Integer> {
     public IntegerListModel(final int length) {
@@ -48,7 +50,8 @@
     public StringListModel(final String value) {
       super(new DataSource<String>() {
         public void requestData(final AsyncListModel<String> listModel) {
-          String prefix = value.endsWith("...") ? value.substring(0, value.length() - 3) : value;
+          String prefix = value.endsWith("...") ? value.substring(0,
+              value.length() - 3) : value;
           dataService.getNext(prefix, new AsyncCallback<List<String>>() {
             public void onFailure(Throwable caught) {
               Window.alert("Error: " + caught);
@@ -81,11 +84,11 @@
    */
   private static final Cell<String> STRING_CELL = new TextCell();
 
-  public TreeNodeFactory<?> createTreeNodeFactory(Object value) {
+  public <T> NodeInfo<?> getNodeInfo(T value, TreeNodeView<T> treeNodeView) {
     if (value instanceof String) {
-      return createTreeNodeFactoryHelper((String) value);
+      return getNodeInfoHelper((String) value);
     } else if (value instanceof Integer) {
-      return createTreeNodeFactoryHelper((Integer) value);
+      return getNodeInfoHelper((Integer) value);
     }
 
     // Unhandled type.
@@ -94,17 +97,27 @@
   }
 
   @SuppressWarnings("unused")
-  private TreeNodeFactory<?> createTreeNodeFactoryHelper(final Integer value) {
+  private NodeInfo<?> getNodeInfoHelper(final Integer value) {
     return null;
   }
 
-  private TreeNodeFactory<?> createTreeNodeFactoryHelper(final String value) {
+  private NodeInfo<?> getNodeInfoHelper(final String value) {
     if (value.endsWith("...")) {
       ListModel<String> listModel = new StringListModel(value.toString());
-      return new DefaultTreeNodeFactory<String>(listModel, STRING_CELL, this);
+      return new DefaultNodeInfo<String>(listModel, new ButtonCell(),
+          new ValueUpdater<String>() {
+            public void update(String value) {
+              Window.alert("Clicked: " + value);
+            }
+          });
     } else {
       ListModel<Integer> listModel = new IntegerListModel(value.length());
-      return new DefaultTreeNodeFactory<Integer>(listModel, INTEGER_CELL, this);
+      return new DefaultNodeInfo<Integer>(listModel, INTEGER_CELL,
+          new ValueUpdater<Integer>() {
+            public void update(Integer value) {
+              Window.alert("Integer = " + value);
+            }
+          });
     }
   }
 }
diff --git a/bikeshed/src/com/google/gwt/sample/tree/client/TreeEntryPoint.java b/bikeshed/src/com/google/gwt/sample/tree/client/TreeEntryPoint.java
index 401de19..467f534 100644
--- a/bikeshed/src/com/google/gwt/sample/tree/client/TreeEntryPoint.java
+++ b/bikeshed/src/com/google/gwt/sample/tree/client/TreeEntryPoint.java
@@ -24,7 +24,7 @@
 public class TreeEntryPoint implements EntryPoint {
 
   public void onModuleLoad() {
-    TreeView tree = new TreeView(new MyTreeModel(), "...");
+    TreeView tree = new TreeView(new MyTreeViewModel(), "...");
     RootPanel.get().add(tree);
   }
 }
diff --git a/bikeshed/src/com/google/gwt/sample/tree/client/TreeModel.java b/bikeshed/src/com/google/gwt/sample/tree/client/TreeModel.java
deleted file mode 100644
index 329280b..0000000
--- a/bikeshed/src/com/google/gwt/sample/tree/client/TreeModel.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2010 Google Inc.
- * 
- * 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.google.gwt.sample.tree.client;
-
-/**
- * A model of a tree.
- */
-public interface TreeModel {
-
-  /**
-   * Create a {@link TreeNodeFactory} that will provide the children of the
-   * given value.
-   * 
-   * @param value the parent value
-   * @return the {@link TreeNodeFactory}
-   */
-  TreeNodeFactory<?> createTreeNodeFactory(Object value);
-}
diff --git a/bikeshed/src/com/google/gwt/sample/tree/client/TreeNodeFactory.java b/bikeshed/src/com/google/gwt/sample/tree/client/TreeNodeFactory.java
deleted file mode 100644
index 3f9e614..0000000
--- a/bikeshed/src/com/google/gwt/sample/tree/client/TreeNodeFactory.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2010 Google Inc.
- * 
- * 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.google.gwt.sample.tree.client;
-
-import com.google.gwt.cells.client.Cell;
-import com.google.gwt.list.shared.ListModel;
-
-/**
- * A factory for generating tree nodes.
- * 
- * @param <C> the type of children in the tree node.
- */
-public interface TreeNodeFactory<C> {
-
-  /**
-   * Create the {@link AbstractTreeNodeFactory} for a given child value that
-   * will generate grand-children.
-   * 
-   * @param value the value of the child
-   * @return the {@link AbstractTreeNodeFactory} that will generate
-   *         grand-children
-   */
-  TreeNodeFactory<?> createChildFactory(C value);
-
-  /**
-   * Get the {@link Cell} used to render child nodes.
-   * 
-   * @return the cell
-   */
-  Cell<C> getCell();
-
-  /**
-   * Get the {@link ListModel} used to retrieve child node values.
-   * 
-   * @return the list model
-   */
-  ListModel<C> getListModel();
-}
diff --git a/bikeshed/src/com/google/gwt/sample/tree/client/TreeNodeView.java b/bikeshed/src/com/google/gwt/sample/tree/client/TreeNodeView.java
index eb44264..4cc54fa 100644
--- a/bikeshed/src/com/google/gwt/sample/tree/client/TreeNodeView.java
+++ b/bikeshed/src/com/google/gwt/sample/tree/client/TreeNodeView.java
@@ -27,13 +27,19 @@
 import com.google.gwt.list.shared.ListModel;
 import com.google.gwt.list.shared.ListRegistration;
 import com.google.gwt.list.shared.SizeChangeEvent;
+import com.google.gwt.sample.tree.client.TreeViewModel.NodeInfo;
 import com.google.gwt.user.client.ui.Composite;
 import com.google.gwt.user.client.ui.TreeItem;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * A view of a tree node.
+ * 
+ * @param <T> the type that this {@link TreeNodeView} contains
  */
-public class TreeNodeView extends Composite {
+public class TreeNodeView<T> extends Composite {
 
   /**
    * A {@link TreeItem} that fires value change events when the state changes.
@@ -78,21 +84,115 @@
   private ExtraTreeItem treeItem;
 
   /**
+   * This node's value.
+   */
+  private T value;
+
+  /**
+   * The parent {@link TreeNodeView}.
+   */
+  private TreeNodeView<?> parent;
+
+  /**
+   * The containing {@link TreeView}.
+   */
+  private TreeView tree;
+
+  /**
+   * The children of this {@link TreeNodeView}.
+   */
+  private List<TreeNodeView<?>> children;
+
+  /**
+   * The info about the child nodes.
+   */
+  private NodeInfo<?> nodeInfo;
+
+  /**
+   * The info about this node.
+   */
+  private NodeInfo<T> parentNodeInfo;
+
+  /**
    * Construct a {@link TreeNodeView}.
    * 
+   * @param value the value of this node
+   * @param tree the parent {@link TreeView}
    * @param treeItem this nodes view
-   * @param factory the factory used to generate child nodes
    */
-  public TreeNodeView(ExtraTreeItem treeItem, final TreeNodeFactory<?> factory) {
+  TreeNodeView(T value, final TreeView tree, final TreeNodeView<?> parent,
+      ExtraTreeItem treeItem, NodeInfo<T> parentNodeInfo) {
+    this.value = value;
+    this.tree = tree;
+    this.parent = parent;
     this.treeItem = treeItem;
+    this.parentNodeInfo = parentNodeInfo;
+  }
 
+  /**
+   * Get the child at the specified index.
+   * 
+   * @return the child node
+   */
+  public TreeNodeView<?> getChild(int index) {
+    if ((index < 0) || (index >= getChildCount())) {
+      return null;
+    }
+    return children.get(index);
+  }
+
+  /**
+   * Get the number of children under this node.
+   * 
+   * @return the child count
+   */
+  public int getChildCount() {
+    return children == null ? 0 : children.size();
+  }
+
+  /**
+   * Get the parent {@link TreeNodeView}.
+   */
+  public TreeNodeView<?> getParentTreeNodeView() {
+    return parent;
+  }
+
+  /**
+   * Get the value contained in this node.
+   * 
+   * @return the value of the node
+   */
+  public T getValue() {
+    return value;
+  }
+
+  NodeInfo<?> getNodeInfo() {
+    return nodeInfo;
+  }
+
+  NodeInfo<T> getParentNodeInfo() {
+    return parentNodeInfo;
+  }
+
+  TreeItem getTreeItem() {
+    return treeItem;
+  }
+
+  /**
+   * Initialize the node info.
+   * 
+   * @param nodeInfo the {@link NodeInfo} that provides information about the
+   *          child values
+   */
+  void initNodeInfo(final NodeInfo<?> nodeInfo) {
     // Force a + icon if this node might have children.
-    if (factory != null) {
+    if (nodeInfo != null) {
+      this.nodeInfo = nodeInfo;
       treeItem.addItem("loading...");
       treeItem.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
         public void onValueChange(ValueChangeEvent<Boolean> event) {
           if (event.getValue()) {
-            onOpen(factory);
+            onOpen(tree, nodeInfo);
           } else {
             onClose();
           }
@@ -101,10 +201,6 @@
     }
   }
 
-  TreeItem getTreeItem() {
-    return treeItem;
-  }
-
   /**
    * Cleanup when the node is closed.
    */
@@ -118,25 +214,29 @@
   /**
    * Setup the node when it is opened.
    * 
-   * @param factory the factory used to generate child nodes
+   * @param tree the parent {@link TreeView}
+   * @param nodeInfo the {@link NodeInfo} that provides information about the
+   *          child values
    * @param <C> the child data type of the node.
    */
-  private <C> void onOpen(final TreeNodeFactory<C> factory) {
-    ListModel<C> listModel = factory.getListModel();
+  private <C> void onOpen(final TreeView tree, final NodeInfo<C> nodeInfo) {
+    ListModel<C> listModel = nodeInfo.getListModel();
     listReg = listModel.addListHandler(new ListHandler<C>() {
       public void onDataChanged(ListEvent<C> event) {
         // TODO - handle event start and length
         treeItem.removeItems();
 
         // Add child tree items.
-        Cell<C> cell = factory.getCell();
-        for (C value : event.getValues()) {
+        Cell<C> theCell = nodeInfo.getCell();
+        children = new ArrayList<TreeNodeView<?>>();
+        for (C childValue : event.getValues()) {
           // TODO(jlabanca): Use one StringBuilder.
           StringBuilder sb = new StringBuilder();
-          cell.render(value, sb);
+          theCell.render(childValue, sb);
           ExtraTreeItem child = new ExtraTreeItem(sb.toString());
           treeItem.addItem(child);
-          new TreeNodeView(child, factory.createChildFactory(value));
+          children.add(tree.createChildView(childValue, TreeNodeView.this,
+              child, nodeInfo));
         }
       }
 
diff --git a/bikeshed/src/com/google/gwt/sample/tree/client/TreeView.java b/bikeshed/src/com/google/gwt/sample/tree/client/TreeView.java
index 240299d..20c91d1 100644
--- a/bikeshed/src/com/google/gwt/sample/tree/client/TreeView.java
+++ b/bikeshed/src/com/google/gwt/sample/tree/client/TreeView.java
@@ -15,9 +15,17 @@
  */
 package com.google.gwt.sample.tree.client;
 
+import com.google.gwt.dom.client.NativeEvent;
 import com.google.gwt.sample.tree.client.TreeNodeView.ExtraTreeItem;
+import com.google.gwt.sample.tree.client.TreeViewModel.NodeInfo;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Element;
+import com.google.gwt.user.client.Event;
 import com.google.gwt.user.client.ui.Composite;
 import com.google.gwt.user.client.ui.Tree;
+import com.google.gwt.user.client.ui.TreeItem;
+
+import java.util.ArrayList;
 
 /**
  * A view of a tree.
@@ -25,30 +33,128 @@
 public class TreeView extends Composite {
 
   /**
-   * Construct a new {@link TreeView}.
-   * 
-   * @param treeModel the model that supplies the data
-   * @param rootData the data object that will be passed to the
-   *          {@link TreeModel} to create the first factory
+   * The {@link TreeViewModel} that backs the tree.
    */
-  public TreeView(TreeModel treeModel, Object rootData) {
-    this(treeModel.createTreeNodeFactory(rootData));
-  }
+  private TreeViewModel model;
+
+  /**
+   * The hidden root node in the tree.
+   */
+  private TreeNodeView<?> rootNode;
 
   /**
    * Construct a new {@link TreeView}.
    * 
-   * @param factory the factory that supplies the data
+   * @param <T> the type of data in the root node
+   * @param viewModel the {@link TreeViewModel} that backs the tree
+   * @param rootValue the hidden root value of the tree
    */
-  public TreeView(TreeNodeFactory<?> factory) {
-    Tree tree = new Tree();
+  public <T> TreeView(TreeViewModel viewModel, T rootValue) {
+    this.model = viewModel;
+
+    // Initialize the widget.
+    Tree tree = new Tree() {
+      @Override
+      public void onBrowserEvent(Event event) {
+        super.onBrowserEvent(event);
+
+        switch (event.getTypeInt()) {
+          case Event.ONMOUSEUP: {
+            if ((DOM.eventGetCurrentTarget(event) == getElement())
+                && (event.getButton() == Event.BUTTON_LEFT)) {
+              elementClicked(DOM.eventGetTarget(event), event);
+            }
+            break;
+          }
+        }
+      }
+    };
     initWidget(tree);
+    sinkEvents(Event.ONMOUSEUP);
 
     // Add the root item.
     ExtraTreeItem rootItem = new ExtraTreeItem("Dummy UI Root");
     tree.addItem(rootItem);
 
     // Associate a view with the item.
-    new TreeNodeView(rootItem, factory);
+    rootNode = createChildView(rootValue, null, rootItem, null);
+  }
+
+  public TreeViewModel getTreeViewModel() {
+    return model;
+  }
+
+  /**
+   * Create a child view for this tree.
+   * 
+   * @param <C> the data type of the child
+   * @param childValue the value of the child
+   * @param parentTreeNodeView the parent {@link TreeNodeView}
+   * @param childItem the DOM view of the child
+   * @return a {@link TreeNodeView} for the child
+   */
+  <C> TreeNodeView<C> createChildView(C childValue,
+      TreeNodeView<?> parentTreeNodeView, ExtraTreeItem childItem,
+      NodeInfo<C> parentNodeInfo) {
+    TreeNodeView<C> childView = new TreeNodeView<C>(childValue, this,
+        parentTreeNodeView, childItem, parentNodeInfo);
+    NodeInfo<?> nodeInfo = model.getNodeInfo(childValue, childView);
+    childView.initNodeInfo(nodeInfo);
+    return childView;
+  }
+
+  /**
+   * Collects parents going up the element tree, terminated at the tree root.
+   */
+  private void collectElementChain(ArrayList<Element> chain, Element hRoot,
+      Element hElem) {
+    if ((hElem == null) || (hElem == hRoot)) {
+      return;
+    }
+
+    collectElementChain(chain, hRoot, DOM.getParent(hElem));
+    chain.add(hElem);
+  }
+
+  private boolean elementClicked(Element hElem, NativeEvent event) {
+    ArrayList<Element> chain = new ArrayList<Element>();
+    collectElementChain(chain, getElement(), hElem);
+
+    TreeNodeView<?> nodeView = findItemByChain(chain, 0, rootNode);
+    if (nodeView != null && nodeView != rootNode) {
+      TreeItem item = nodeView.getTreeItem();
+      if (DOM.isOrHasChild(item.getElement(), hElem)) {
+        fireEvent(nodeView, event);
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  private TreeNodeView<?> findItemByChain(ArrayList<Element> chain, int idx,
+      TreeNodeView<?> parent) {
+    if (idx == chain.size()) {
+      return parent;
+    }
+
+    Element hCurElem = chain.get(idx);
+    for (int i = 0, n = parent.getChildCount(); i < n; ++i) {
+      TreeNodeView<?> child = parent.getChild(i);
+      if (child.getTreeItem().getElement() == hCurElem) {
+        TreeNodeView<?> retItem = findItemByChain(chain, idx + 1, child);
+        if (retItem == null) {
+          return child;
+        }
+        return retItem;
+      }
+    }
+
+    return findItemByChain(chain, idx + 1, parent);
+  }
+
+  private <T> void fireEvent(TreeNodeView<T> nodeView, NativeEvent event) {
+    nodeView.getParentNodeInfo().onBrowserEvent(
+        nodeView.getTreeItem().getElement(), nodeView.getValue(), event);
   }
 }
diff --git a/bikeshed/src/com/google/gwt/sample/tree/client/TreeViewModel.java b/bikeshed/src/com/google/gwt/sample/tree/client/TreeViewModel.java
new file mode 100644
index 0000000..20aacbe
--- /dev/null
+++ b/bikeshed/src/com/google/gwt/sample/tree/client/TreeViewModel.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2010 Google Inc.
+ * 
+ * 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.google.gwt.sample.tree.client;
+
+import com.google.gwt.cells.client.Cell;
+import com.google.gwt.cells.client.ValueUpdater;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.NativeEvent;
+import com.google.gwt.list.shared.ListModel;
+
+/**
+ * A model of a tree.
+ */
+public interface TreeViewModel {
+
+  /**
+   * The info needed to create a {@link TreeNodeView}.
+   */
+  interface NodeInfo<C> {
+    /**
+     * Get the {@link Cell} used to render child nodes.
+     * 
+     * @return the cell
+     */
+    Cell<C> getCell();
+
+    /**
+     * Get the {@link ListModel} used to retrieve child node values.
+     * 
+     * @return the list model
+     */
+    ListModel<C> getListModel();
+
+    /**
+     * Handle an event that is fired on one of the children of this item.
+     * 
+     * @param elem the parent element of the item
+     * @param object the data value of the item
+     * @param event the event that was fired
+     */
+    void onBrowserEvent(Element elem, final C object, NativeEvent event);
+  }
+
+  /**
+   * Default implementation of {@link NodeInfo}.
+   */
+  class DefaultNodeInfo<C> implements NodeInfo<C> {
+
+    private Cell<C> cell;
+    private ListModel<C> listModel;
+    private ValueUpdater<C> valueUpdater;
+
+    /**
+     * Construct a new {@link DefaultNodeInfo}.
+     * 
+     * @param listModel the {@link ListModel} that provides the child values
+     * @param cell the {@link Cell} used to render the child values
+     */
+    public DefaultNodeInfo(ListModel<C> listModel, Cell<C> cell) {
+      this.cell = cell;
+      this.listModel = listModel;
+    }
+
+    /**
+     * Construct a new {@link DefaultNodeInfo}.
+     * 
+     * @param listModel the {@link ListModel} that provides the child values
+     * @param cell the {@link Cell} used to render the child values
+     * @param valueUpdater the {@link ValueUpdater}
+     */
+    public DefaultNodeInfo(ListModel<C> listModel, Cell<C> cell,
+        ValueUpdater<C> valueUpdater) {
+      this(listModel, cell);
+      this.valueUpdater = valueUpdater;
+    }
+
+    public Cell<C> getCell() {
+      return cell;
+    }
+
+    public ListModel<C> getListModel() {
+      return listModel;
+    }
+
+    public void onBrowserEvent(Element elem, final C object, NativeEvent event) {
+      cell.onBrowserEvent(elem, object, event, valueUpdater);
+    }
+  }
+
+  /**
+   * Get the {@link NodeInfo} that will provide the {@link ListModel} and
+   * {@link Cell} to retrieve the children of the specified value.
+   * 
+   * @param value the value in the parent node
+   * @param treeNodeView the {@link TreeNodeView} that contains the value
+   * @return the {@link NodeInfo}
+   */
+  <T> NodeInfo<?> getNodeInfo(T value, TreeNodeView<T> treeNodeView);
+}