Split SubNodeFactory out of SubNodeAdder

Split SubNodeAdder into two classes based on the two roles it previously
implemented in order to make these roles more obvious. This should make
it more intuitve to use the SubNodeFactory to create Nodes without
having to accumulate them.

Change-Id: I18a1310a695ca069c205f901c433a3622c323375
diff --git a/src/main/java/com/googlesource/gerrit/plugins/task/TaskTree.java b/src/main/java/com/googlesource/gerrit/plugins/task/TaskTree.java
index d551137..e246af8 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/task/TaskTree.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/task/TaskTree.java
@@ -120,7 +120,7 @@
     }
 
     protected List<Node> loadSubNodes() throws ConfigInvalidException, IOException, OrmException {
-      return new SubNodeAdder().getSubNodes();
+      return new SubNodeFactory().createFromPreloaded(preloader.getRootTasks());
     }
 
     public ChangeData getChangeData() {
@@ -131,30 +131,41 @@
       return true;
     }
 
-    protected class SubNodeAdder {
-      protected List<Node> nodes = new ArrayList<>();
+    protected class SubNodeFactory {
       protected Set<String> names = new HashSet<>();
 
-      public List<Node> getSubNodes() throws ConfigInvalidException, IOException, OrmException {
-        addSubNodes();
+      public List<Node> createFromPreloaded(List<Task> defs)
+          throws ConfigInvalidException, OrmException {
+        List<Node> nodes = new ArrayList<>();
+        for (Task def : defs) {
+          nodes.add(createFromPreloaded(def));
+        }
         return nodes;
       }
 
-      protected void addSubNodes() throws ConfigInvalidException, IOException, OrmException {
-        addPreloaded(preloader.getRootTasks());
+      public Node createFromPreloaded(Task def) throws ConfigInvalidException, OrmException {
+        return createFromPreloaded(def, (parent, definition) -> new Node(parent, definition));
       }
 
-      protected void addPreloaded(List<Task> defs) throws ConfigInvalidException, OrmException {
-        for (Task def : defs) {
-          addPreloaded(def);
-        }
+      public Node createFromPreloaded(Task def, ChangeData changeData)
+          throws ConfigInvalidException, OrmException {
+        return createFromPreloaded(
+            def,
+            (parent, definition) ->
+                new Node(parent, definition) {
+                  @Override
+                  public ChangeData getChangeData() {
+                    return changeData;
+                  }
+
+                  @Override
+                  public boolean isChange() {
+                    return true;
+                  }
+                });
       }
 
-      protected void addPreloaded(Task def) throws ConfigInvalidException, OrmException {
-        addPreloaded(def, (parent, definition) -> new Node(parent, definition));
-      }
-
-      protected void addPreloaded(Task def, NodeFactory nodeFactory)
+      protected Node createFromPreloaded(Task def, NodeFactory nodeFactory)
           throws ConfigInvalidException, OrmException {
         if (def != null) {
           try {
@@ -169,17 +180,16 @@
               if (isRefreshNeeded) {
                 node.refreshTask();
               }
-              nodes.add(node);
-              return;
+              return node;
             }
           } catch (Exception e) {
           }
         }
-        addInvalidNode();
+        return createInvalid();
       }
 
-      protected void addInvalidNode() {
-        nodes.add(new Node().new Invalid());
+      protected Node createInvalid() {
+        return new Node().new Invalid();
       }
     }
   }
@@ -281,13 +291,16 @@
       return false;
     }
 
-    protected class SubNodeAdder extends NodeList.SubNodeAdder {
-      @Override
-      protected void addSubNodes() throws ConfigInvalidException, IOException, OrmException {
+    protected class SubNodeAdder {
+      protected List<Node> nodes = new ArrayList<>();
+      protected SubNodeFactory factory = new SubNodeFactory();
+
+      public List<Node> getSubNodes() throws ConfigInvalidException, IOException, OrmException {
         addSubTasks();
         addSubTasksFactoryTasks();
         addSubTasksFiles();
         addSubTasksExternals();
+        return nodes;
       }
 
       protected void addSubTasks() throws ConfigInvalidException, IOException, OrmException {
@@ -372,18 +385,7 @@
               addPreloaded(
                   preloader.preload(
                       task.config.new Task(tasksFactory, changeData.getId().toString())),
-                  (parent, definition) ->
-                      new Node(parent, definition) {
-                        @Override
-                        public ChangeData getChangeData() {
-                          return changeData;
-                        }
-
-                        @Override
-                        public boolean isChange() {
-                          return true;
-                        }
-                      });
+                  changeData);
             }
             return;
           }
@@ -394,6 +396,23 @@
         addInvalidNode();
       }
 
+      public void addPreloaded(List<Task> defs) throws ConfigInvalidException, OrmException {
+        nodes.addAll(factory.createFromPreloaded(defs));
+      }
+
+      public void addPreloaded(Task def, ChangeData changeData)
+          throws ConfigInvalidException, OrmException {
+        nodes.add(factory.createFromPreloaded(def, changeData));
+      }
+
+      public void addPreloaded(Task def) throws ConfigInvalidException, OrmException {
+        nodes.add(factory.createFromPreloaded(def));
+      }
+
+      public void addInvalidNode() {
+        nodes.add(factory.createInvalid());
+      }
+
       protected List<Task> getPreloadedTasks(External external)
           throws ConfigInvalidException, IOException, OrmException {
         return preloader.getTasks(