Store properties at the TaskTree.NodeList level

Refactor property storage in the TaskTree to reduce the need to pass
around parent Properties during tree creations.

Change-Id: I959c10a00e66b513b9c5f652019c6833e1857586
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 13600e6..46ad180 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/task/TaskTree.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/task/TaskTree.java
@@ -94,28 +94,37 @@
     protected LinkedList<String> path = new LinkedList<>();
     protected List<Node> nodes;
     protected Set<String> names = new HashSet<>();
+    protected Map<String, String> properties;
 
-    protected void addSubDefinitions(List<Task> defs, Map<String, String> parentProperties) {
+    protected void addSubDefinitions(List<Task> defs) {
       for (Task def : defs) {
-        if (def != null && !path.contains(def.name) && names.add(def.name)) {
-          // path check above detects looping definitions
-          // names check above detects duplicate subtasks
-          try {
-            nodes.add(new Node(def, path, parentProperties));
-            continue;
-          } catch (Exception e) {
-          } // bad definition, handled below
-        }
-        nodes.add(null);
+        addSubDefinition(def);
       }
     }
+
+    protected void addSubDefinition(Task def) {
+      Node node = null;
+      if (def != null && !path.contains(def.name) && names.add(def.name)) {
+        // path check above detects looping definitions
+        // names check above detects duplicate subtasks
+        try {
+          node = new Node(def, path, properties);
+        } catch (Exception e) {
+        } // bad definition, handled with null
+      }
+      nodes.add(node);
+    }
   }
 
   protected class Root extends NodeList {
+    protected Root() {
+      properties = new HashMap<String, String>();
+    }
+
     public List<Node> getRootNodes() throws ConfigInvalidException, IOException {
       if (nodes == null) {
         nodes = new ArrayList<>();
-        addSubDefinitions(getRootDefinitions(), new HashMap<String, String>());
+        addSubDefinitions(getRootDefinitions());
       }
       return nodes;
     }
@@ -135,6 +144,7 @@
       this.path.add(definition.name);
       Preloader.preload(definition);
       new Properties(definition, parentProperties);
+      properties = definition.properties;
     }
 
     public List<Node> getSubNodes() throws OrmException {
@@ -152,10 +162,6 @@
       addExternalDefinitions();
     }
 
-    protected void addSubDefinitions(List<Task> defs) {
-      addSubDefinitions(defs, definition.properties);
-    }
-
     protected void addSubFileDefinitions() {
       for (String file : definition.subTasksFiles) {
         try {