Use StatisticsMap interface

The interface lagged behind functionality, add the timing methods to it
so that it may be used again instead of the concrete classes. In doing
so make timing explicit when desired and do not include 0 loading times
in the statistics if they were never recorded in the first place.

Change-Id: I405512f3b52c15906b3644ca0321ee9c844a794c
diff --git a/src/main/java/com/googlesource/gerrit/plugins/task/HitBooleanTable.java b/src/main/java/com/googlesource/gerrit/plugins/task/HitBooleanTable.java
index 39365cb..adce341 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/task/HitBooleanTable.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/task/HitBooleanTable.java
@@ -27,7 +27,7 @@
     public long size;
     public int numberOfRows;
     public int numberOfColumns;
-    public long sumNanosecondsLoading;
+    public Long sumNanosecondsLoading;
   }
 
   protected Statistics statistics;
@@ -49,6 +49,9 @@
     if (statistics == null) {
       return StopWatch.DISABLED;
     }
+    if (statistics.sumNanosecondsLoading == null) {
+      statistics.sumNanosecondsLoading = 0L;
+    }
     return new StopWatch.Enabled().setNanosConsumer(ns -> statistics.sumNanosecondsLoading += ns);
   }
 
diff --git a/src/main/java/com/googlesource/gerrit/plugins/task/HitHashMap.java b/src/main/java/com/googlesource/gerrit/plugins/task/HitHashMap.java
index 7a23fc6..82cedf7 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/task/HitHashMap.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/task/HitHashMap.java
@@ -26,7 +26,7 @@
   public static class Statistics {
     public long hits;
     public int size;
-    public long sumNanosecondsLoading;
+    public Long sumNanosecondsLoading;
     public List<Object> elements;
   }
 
@@ -65,6 +65,19 @@
   public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
     V v = get(key);
     if (v == null) {
+      v = mappingFunction.apply(key);
+      if (v != null) {
+        put(key, v);
+      }
+    }
+    return v;
+  }
+
+  @Override
+  @SuppressWarnings("try")
+  public V computeIfAbsentTimed(K key, Function<? super K, ? extends V> mappingFunction) {
+    V v = get(key);
+    if (v == null) {
       try (StopWatch stopWatch = createLoadingStopWatch()) {
         v = mappingFunction.apply(key);
       }
@@ -144,6 +157,9 @@
     if (statistics == null) {
       return StopWatch.DISABLED;
     }
+    if (statistics.sumNanosecondsLoading == null) {
+      statistics.sumNanosecondsLoading = 0L;
+    }
     return new StopWatch.Enabled().setNanosConsumer(ns -> statistics.sumNanosecondsLoading += ns);
   }
 
diff --git a/src/main/java/com/googlesource/gerrit/plugins/task/StatisticsMap.java b/src/main/java/com/googlesource/gerrit/plugins/task/StatisticsMap.java
index 4ae8857..7c401a9 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/task/StatisticsMap.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/task/StatisticsMap.java
@@ -15,5 +15,10 @@
 package com.googlesource.gerrit.plugins.task;
 
 import java.util.Map;
+import java.util.function.Function;
 
-public interface StatisticsMap<K, V> extends Map<K, V>, TracksStatistics {}
+public interface StatisticsMap<K, V> extends Map<K, V>, TracksStatistics {
+  V computeIfAbsentTimed(K key, Function<? super K, ? extends V> mappingFunction);
+
+  StopWatch createLoadingStopWatch();
+}
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 26f7260..4a7d0fd 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/task/TaskTree.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/task/TaskTree.java
@@ -86,7 +86,7 @@
   protected final NodeList root = new NodeList();
   protected final Provider<ChangeQueryBuilder> changeQueryBuilderProvider;
   protected final Provider<ChangeQueryProcessor> changeQueryProcessorProvider;
-  protected final HitHashMap<String, List<ChangeData>> changesByNamesFactoryQuery =
+  protected final StatisticsMap<String, List<ChangeData>> changesByNamesFactoryQuery =
       new HitHashMap<>();
   protected final StatisticsMap<SubSectionKey, List<Task>> definitionsBySubSection =
       new HitHashMapOfCollection<>();
@@ -264,7 +264,7 @@
         if (!isChange()) {
           return cachedNodes = nodes;
         }
-        definitionsBySubSection.computeIfAbsent(
+        definitionsBySubSection.computeIfAbsentTimed(
             task.key().subSection(),
             k -> nodes.stream().map(n -> n.getDefinition()).collect(toList()));
       } else {