Author: hlship
Date: Mon Oct 17 19:12:44 2011
New Revision: 1185333

URL: http://svn.apache.org/viewvc?rev=1185333&view=rev
Log:
TAP5-1630: A TreeModelAdapter that returns null from getChildren() causes an NPE

Modified:
    
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/DefaultTreeModel.java
    
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeModelAdapter.java
    
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/Stuff.java
    
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/StuffTreeModelAdapter.java

Modified: 
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/DefaultTreeModel.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/DefaultTreeModel.java?rev=1185333&r1=1185332&r2=1185333&view=diff
==============================================================================
--- 
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/DefaultTreeModel.java
 (original)
+++ 
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/DefaultTreeModel.java
 Mon Oct 17 19:12:44 2011
@@ -27,9 +27,9 @@ import java.util.Map;
 /**
  * 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>
+ * <p/>
  * This implementation is <em>not</em> thread safe.
- * 
+ *
  * @param <T>
  * @since 5.3
  */
@@ -86,7 +86,15 @@ public class DefaultTreeModel<T> impleme
         public List<TreeNode<T>> getChildren()
         {
             if (children == null)
-                children = 
F.flow(adapter.getChildren(value)).map(toTreeNode).toList();
+            {
+                List<T> childValues = adapter.getChildren(value);
+
+                boolean empty = childValues == null || childValues.isEmpty();
+
+                children = empty
+                        ? emptyTreeNodeList()
+                        : F.flow(childValues).map(toTreeNode).toList();
+            }
 
             return children;
         }
@@ -96,17 +104,19 @@ public class DefaultTreeModel<T> impleme
             return adapter.getLabel(value);
         }
 
+        private List<TreeNode<T>> emptyTreeNodeList()
+        {
+            return Collections.emptyList();
+        }
+
     }
 
     /**
      * Creates a new model starting from a single root element.
-     * 
-     * @param encoder
-     *            used to convert values to strings and vice-versa
-     * @param adapter
-     *            adapts elements to the tree
-     * @param root
-     *            defines the root node of the model
+     *
+     * @param encoder used to convert values to strings and vice-versa
+     * @param adapter adapts elements to the tree
+     * @param root    defines the root node of the model
      */
     public DefaultTreeModel(ValueEncoder<T> encoder, TreeModelAdapter<T> 
adapter, T root)
     {
@@ -115,13 +125,10 @@ public class DefaultTreeModel<T> impleme
 
     /**
      * 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
+     *
+     * @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)
     {

Modified: 
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeModelAdapter.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeModelAdapter.java?rev=1185333&r1=1185332&r2=1185333&view=diff
==============================================================================
--- 
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeModelAdapter.java
 (original)
+++ 
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeModelAdapter.java
 Mon Oct 17 19:12:44 2011
@@ -18,35 +18,36 @@ import java.util.List;
 
 /**
  * Used with {@link DefaultTreeModel} to define how to extract labels and 
child nodes from a value.
- * 
+ *
  * @since 5.3
  */
 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.
-     * 
+     * This should return the childen in the correct presentation or, or 
return null or an empty list.
+     *
      * @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);

Modified: 
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/Stuff.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/Stuff.java?rev=1185333&r1=1185332&r2=1185333&view=diff
==============================================================================
--- 
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/Stuff.java
 (original)
+++ 
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/Stuff.java
 Mon Oct 17 19:12:44 2011
@@ -28,7 +28,7 @@ public class Stuff
 
     public final String name;
 
-    public final List<Stuff> children = CollectionFactory.newList();
+    public List<Stuff> children;
 
     public Stuff(String name)
     {
@@ -39,7 +39,7 @@ public class Stuff
     {
         for (String name : names)
         {
-            children.add(new Stuff(name));
+            addChild(new Stuff(name));
         }
 
         return this;
@@ -47,6 +47,11 @@ public class Stuff
 
     public Stuff addChild(Stuff child)
     {
+        if (children == null)
+        {
+            children = CollectionFactory.newList();
+        }
+
         children.add(child);
 
         return this;
@@ -87,7 +92,8 @@ public class Stuff
         ROOT.addChild(numbers);
     }
 
-    public static TreeModel<Stuff> createTreeModel() {
+    public static TreeModel<Stuff> createTreeModel()
+    {
         ValueEncoder<Stuff> encoder = new StuffValueEncoder();
 
         return new DefaultTreeModel<Stuff>(encoder, new 
StuffTreeModelAdapter(), Stuff.ROOT.children);

Modified: 
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/StuffTreeModelAdapter.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/StuffTreeModelAdapter.java?rev=1185333&r1=1185332&r2=1185333&view=diff
==============================================================================
--- 
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/StuffTreeModelAdapter.java
 (original)
+++ 
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/StuffTreeModelAdapter.java
 Mon Oct 17 19:12:44 2011
@@ -14,20 +14,20 @@
 
 package org.apache.tapestry5.integration.app1;
 
-import java.util.List;
-
 import org.apache.tapestry5.tree.TreeModelAdapter;
 
+import java.util.List;
+
 public class StuffTreeModelAdapter implements TreeModelAdapter<Stuff>
 {
     public boolean isLeaf(Stuff value)
     {
-        return value.children.isEmpty();
+        return !hasChildren(value);
     }
 
     public boolean hasChildren(Stuff value)
     {
-        return !value.children.isEmpty();
+        return value.children != null && !value.children.isEmpty();
     }
 
     public List<Stuff> getChildren(Stuff value)


Reply via email to