Add logic to handle blank subtask and blank name in names-factory

Before the change, NPE was thrown in case the subtask or name field in
names-factory is blank. This makes the parent task invalid which is not
ideal. Add logic to handle these cases so that only the concerned task
is marked as invalid.

Change-Id: I1f8faf06f7bc55b6619a1ef40bd40ee98bd3b51b
diff --git a/src/main/java/com/googlesource/gerrit/plugins/task/TaskConfig.java b/src/main/java/com/googlesource/gerrit/plugins/task/TaskConfig.java
index 0c3136e..be7f120 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/task/TaskConfig.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/task/TaskConfig.java
@@ -300,8 +300,9 @@
   }
 
   protected List<String> getStringList(SubSectionKey s, String key) {
-    return Collections.unmodifiableList(
-        Arrays.asList(cfg.getStringList(s.section(), s.subSection(), key)));
+    List<String> stringList = Arrays.asList(cfg.getStringList(s.section(), s.subSection(), key));
+    stringList.replaceAll(str -> str == null ? "" : str);
+    return Collections.unmodifiableList(stringList);
   }
 
   protected SubSectionKey subSectionKey(String section, String subSection) {
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 ac65a0f..501e9db 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/task/TaskTree.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/task/TaskTree.java
@@ -58,6 +58,7 @@
 import java.util.Optional;
 import java.util.Set;
 import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.util.StringUtils;
 
 /**
  * Add structure to access the task definitions from the config as a tree.
@@ -430,7 +431,11 @@
       protected void addStaticTypeTasks(TasksFactory tasksFactory, NamesFactory namesFactory)
           throws ConfigInvalidException, IOException, StorageException {
         for (String name : namesFactory.names) {
-          addPreloaded(preloader.preload(task.config.new Task(tasksFactory, name)));
+          if (StringUtils.isEmptyOrNull(name)) {
+            addInvalidNode();
+          } else {
+            addPreloaded(preloader.preload(task.config.new Task(tasksFactory, name)));
+          }
         }
       }
 
diff --git a/src/main/resources/Documentation/test/preview.md b/src/main/resources/Documentation/test/preview.md
index 6aff577..510f073 100644
--- a/src/main/resources/Documentation/test/preview.md
+++ b/src/main/resources/Documentation/test/preview.md
@@ -102,6 +102,18 @@
          ]
       },
       {
+         "applicable" : true,
+         "hasPass" : true,
+         "name" : "Subtask Blank",
+         "status" : "WAITING",
+         "subTasks" : [
+            {
+               "name" : "UNKNOWN",
+               "status" : "INVALID"
+            }
+         ]
+      },
+      {
          "applicable" : false,
          "hasPass" : true,
          "name" : "NA Bad PASS query",
@@ -164,6 +176,18 @@
       {
          "applicable" : true,
          "hasPass" : false,
+         "name" : "task (names-factory name Blank)",
+         "status" : "WAITING",
+         "subTasks" : [
+            {
+               "name" : "UNKNOWN",
+               "status" : "INVALID"
+            }
+         ]
+      },
+      {
+         "applicable" : true,
+         "hasPass" : false,
          "name" : "task (names-factory duplicate)",
          "status" : "WAITING",
          "subTasks" : [
@@ -404,6 +428,18 @@
          ]
       },
       {
+         "applicable" : true,
+         "hasPass" : true,
+         "name" : "Subtask Blank",
+         "status" : "WAITING",
+         "subTasks" : [
+            {
+               "name" : "UNKNOWN",
+               "status" : "INVALID"
+            }
+         ]
+      },
+      {
          "applicable" : false,
          "hasPass" : true,
          "name" : "NA Bad PASS query",
@@ -466,6 +502,18 @@
       {
          "applicable" : true,
          "hasPass" : false,
+         "name" : "task (names-factory name Blank)",
+         "status" : "WAITING",
+         "subTasks" : [
+            {
+               "name" : "UNKNOWN",
+               "status" : "INVALID"
+            }
+         ]
+      },
+      {
+         "applicable" : true,
+         "hasPass" : false,
          "name" : "task (names-factory duplicate)",
          "status" : "WAITING",
          "subTasks" : [
diff --git a/src/main/resources/Documentation/test/task_states.md b/src/main/resources/Documentation/test/task_states.md
index f48f528..f5d4cd8 100644
--- a/src/main/resources/Documentation/test/task_states.md
+++ b/src/main/resources/Documentation/test/task_states.md
@@ -2266,6 +2266,18 @@
          ]
       },
       {
+         "applicable" : true,
+         "hasPass" : true,
+         "name" : "Subtask Blank",
+         "status" : "WAITING",
+         "subTasks" : [
+            {
+               "name" : "UNKNOWN",
+               "status" : "INVALID"
+            }
+         ]
+      },
+      {
          "applicable" : false,
          "hasPass" : true,
          "name" : "NA Bad PASS query",
@@ -2328,6 +2340,18 @@
       {
          "applicable" : true,
          "hasPass" : false,
+         "name" : "task (names-factory name Blank)",
+         "status" : "WAITING",
+         "subTasks" : [
+            {
+               "name" : "UNKNOWN",
+               "status" : "INVALID"
+            }
+         ]
+      },
+      {
+         "applicable" : true,
+         "hasPass" : false,
          "name" : "task (names-factory duplicate)",
          "status" : "WAITING",
          "subTasks" : [
@@ -2509,6 +2533,18 @@
          ]
       },
       {
+         "applicable" : true,
+         "hasPass" : true,
+         "name" : "Subtask Blank",
+         "status" : "WAITING",
+         "subTasks" : [
+            {
+               "name" : "UNKNOWN",
+               "status" : "INVALID"
+            }
+         ]
+      },
+      {
          "applicable" : false,
          "hasPass" : true,
          "name" : "NA Bad PASS query",
@@ -2571,6 +2607,18 @@
       {
          "applicable" : true,
          "hasPass" : false,
+         "name" : "task (names-factory name Blank)",
+         "status" : "WAITING",
+         "subTasks" : [
+            {
+               "name" : "UNKNOWN",
+               "status" : "INVALID"
+            }
+         ]
+      },
+      {
+         "applicable" : true,
+         "hasPass" : false,
          "name" : "task (names-factory duplicate)",
          "status" : "WAITING",
          "subTasks" : [
@@ -2669,6 +2717,10 @@
 [task "Subtask Optional"]
    subtask = MISSING | MISSING
 
+[task "Subtask Blank"]
+  pass = True
+  subtask =
+
 [task "NA Bad PASS query"]
   applicable = NOT is:open # Assumes test query is "is:open"
   fail = True
@@ -2699,6 +2751,9 @@
 [task "task (names-factory type INVALID)"]
   subtasks-factory = tasks-factory (names-factory type INVALID)
 
+[task "task (names-factory name Blank)"]
+  subtasks-factory = tasks-factory (names-factory name Blank)
+
 [task "task (names-factory duplicate)"]
   subtasks-factory = tasks-factory (names-factory duplicate)
 
@@ -2718,6 +2773,10 @@
 [tasks-factory "tasks-factory (names-factory type INVALID)"]
   names-factory = name-factory (type INVALID)
 
+[tasks-factory "tasks-factory (names-factory name Blank)"]
+  names-factory = names-factory (name Blank)
+  fail = True
+
 [tasks-factory "tasks-factory (names-factory duplicate)"]
   names-factory = names-factory duplicate
   fail = True
@@ -2744,6 +2803,10 @@
   name = invalid type test
   type = invalid
 
+[names-factory "names-factory (name Blank)"]
+  name =
+  type = static
+
 [names-factory "names-factory duplicate"]
   name = duplicate
   name = duplicate