Add more stats and refactor to handle more

Add an addStatistics() method to the TaskAttributeFactory to separate
out the part to create statistics for a TaskAttribute from the main
logic. Add hasUnfilterableSubNodes and nodeByBranchCache statistics to
the node level statistics ouptut, and add numberOfChangeNodes and
numberOfDuplicates to the query statistics.

Change-Id: Ie57f5d1ee2c44dabb71cee0f7f36788b7f69adb9
diff --git a/src/main/java/com/googlesource/gerrit/plugins/task/HitHashMapOfCollection.java b/src/main/java/com/googlesource/gerrit/plugins/task/HitHashMapOfCollection.java
index 7429dba..422418b 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/task/HitHashMapOfCollection.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/task/HitHashMapOfCollection.java
@@ -31,6 +31,14 @@
 
   protected Statistics statistics;
 
+  public HitHashMapOfCollection() {}
+
+  public HitHashMapOfCollection(boolean initStatistics) {
+    if (initStatistics) {
+      initStatistics();
+    }
+  }
+
   @Override
   public void initStatistics() {
     super.initStatistics();
diff --git a/src/main/java/com/googlesource/gerrit/plugins/task/TaskAttributeFactory.java b/src/main/java/com/googlesource/gerrit/plugins/task/TaskAttributeFactory.java
index 2d7d400..6533f5a 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/task/TaskAttributeFactory.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/task/TaskAttributeFactory.java
@@ -52,6 +52,8 @@
 
   public static class Statistics {
     public long numberOfChanges;
+    public long numberOfChangeNodes;
+    public long numberOfDuplicates;
     public long numberOfNodes;
     public long numberOfTaskPluginAttributes;
     public Object predicateCache;
@@ -64,6 +66,8 @@
       public boolean isApplicableRefreshRequired;
       public boolean isSubNodeReloadRequired;
       public boolean isTaskRefreshNeeded;
+      public Boolean hasUnfilterableSubNodes;
+      public Object nodesByBranchCache;
     }
 
     public Boolean applicable;
@@ -154,6 +158,12 @@
       attribute = new TaskAttribute(task.name());
       if (options.includeStatistics) {
         statistics.numberOfNodes++;
+        if (node.isChange()) {
+          statistics.numberOfChangeNodes++;
+        }
+        if (node.isDuplicate) {
+          statistics.numberOfDuplicates++;
+        }
         attribute.statistics = new TaskAttribute.Statistics();
       }
     }
@@ -203,13 +213,7 @@
               if (options.evaluationTime) {
                 attribute.evaluationMilliSeconds = millis() - attribute.evaluationMilliSeconds;
               }
-              if (attribute.statistics != null) {
-                attribute.statistics.isApplicableRefreshRequired =
-                    node.properties.isApplicableRefreshRequired;
-                attribute.statistics.isSubNodeReloadRequired =
-                    node.properties.isSubNodeReloadRequired();
-                attribute.statistics.isTaskRefreshNeeded = node.properties.isTaskRefreshNeeded;
-              }
+              addStatistics(attribute.statistics);
               return Optional.of(attribute);
             }
           }
@@ -220,6 +224,20 @@
       return Optional.empty();
     }
 
+    public void addStatistics(TaskAttribute.Statistics statistics) {
+      if (statistics != null) {
+        statistics.isApplicableRefreshRequired = node.properties.isApplicableRefreshRequired;
+        statistics.isSubNodeReloadRequired = node.properties.isSubNodeReloadRequired();
+        statistics.isTaskRefreshNeeded = node.properties.isTaskRefreshNeeded;
+        if (!statistics.isSubNodeReloadRequired) {
+          statistics.hasUnfilterableSubNodes = node.hasUnfilterableSubNodes;
+        }
+        if (node.nodesByBranch != null) {
+          statistics.nodesByBranchCache = node.nodesByBranch.getStatistics();
+        }
+      }
+    }
+
     protected Status getStatusWithExceptions() throws StorageException, QueryParseException {
       if (node.isDuplicate) {
         return Status.DUPLICATE;
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 c7964ad..6349156 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/task/TaskTree.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/task/TaskTree.java
@@ -222,7 +222,7 @@
 
     protected final Properties properties;
     protected final TaskKey taskKey;
-    protected Map<BranchNameKey, List<Node>> nodesByBranch;
+    protected StatisticsMap<BranchNameKey, List<Node>> nodesByBranch;
     protected boolean hasUnfilterableSubNodes = false;
 
     protected Node() { // Only for Invalid
@@ -476,7 +476,7 @@
           Optional<List<Node>> filterable = getOptionalApplicableForBranch(nodes);
           if (filterable.isPresent()) {
             if (nodesByBranch == null) {
-              nodesByBranch = new HashMap<>();
+              nodesByBranch = new HitHashMapOfCollection<>(statistics != null);
             }
             nodesByBranch.put(branch, filterable.get());
             return filterable.get();